# password = <mgmt admin password>
# address = <switch mgmt ip address>
# ostype = NOS
+# osversion = autodetect | n.n.n
# physical_networks = physnet1,physnet2
#
# Example:
# password = password
# address = 10.24.84.38
# ostype = NOS
+# osversion = 4.1.1
# physical_networks = physnet1,physnet2
cfg.StrOpt('physical_networks', default='',
help=_('Allowed physical networks')),
cfg.StrOpt('ostype', default='NOS',
- help=_('Unused'))
+ help=_('OS Type of the switch')),
+ cfg.StrOpt('osversion', default='4.0.0',
+ help=_('OS Version number'))
]
cfg.CONF.register_opts(ML2_BROCADE, "ml2_brocade")
def brocade_init(self):
"""Brocade specific initialization for this class."""
- self._switch = {'address': cfg.CONF.ml2_brocade.address,
- 'username': cfg.CONF.ml2_brocade.username,
- 'password': cfg.CONF.ml2_brocade.password
- }
+ osversion = None
+ self._switch = {
+ 'address': cfg.CONF.ml2_brocade.address,
+ 'username': cfg.CONF.ml2_brocade.username,
+ 'password': cfg.CONF.ml2_brocade.password,
+ 'ostype': cfg.CONF.ml2_brocade.ostype,
+ 'osversion': cfg.CONF.ml2_brocade.osversion}
+
self._driver = importutils.import_object(NOS_DRIVER)
+ # Detect version of NOS on the switch
+ osversion = self._switch['osversion']
+ if osversion == "autodetect":
+ osversion = self._driver.get_nos_version(
+ self._switch['address'],
+ self._switch['username'],
+ self._switch['password'])
+
+ virtual_fabric_enabled = self._driver.is_virtual_fabric_enabled(
+ self._switch['address'],
+ self._switch['username'],
+ self._switch['password'])
+
+ if virtual_fabric_enabled:
+ LOG.debug(_("Virtual Fabric: enabled"))
+ else:
+ LOG.debug(_("Virtual Fabric: not enabled"))
+
+ self.set_features_enabled(osversion, virtual_fabric_enabled)
+
+ def set_features_enabled(self, nos_version, virtual_fabric_enabled):
+ self._virtual_fabric_enabled = virtual_fabric_enabled
+ version = nos_version.split(".", 2)
+
+ # Starting 4.1.0 port profile domains are supported
+ if int(version[0]) >= 5 or (int(version[0]) >= 4
+ and int(version[1]) >= 1):
+ self._pp_domains_supported = True
+ else:
+ self._pp_domains_supported = False
+ self._driver.set_features_enabled(self._pp_domains_supported,
+ self._virtual_fabric_enabled)
+
+ def get_features_enabled(self):
+ return self._pp_domains_supported, self._virtual_fabric_enabled
+
def create_network_precommit(self, mech_context):
"""Create Network in the mechanism specific database table."""
Interface Configuration Commands
"""
+# Get NOS Version
+SHOW_FIRMWARE_VERSION = (
+ "show-firmware-version xmlns:nc="
+ "'urn:brocade.com:mgmt:brocade-firmware-ext'"
+)
+GET_VCS_DETAILS = (
+ 'get-vcs-details xmlns:nc="urn:brocade.com:mgmt:brocade-vcs"'
+)
+SHOW_VIRTUAL_FABRIC = (
+ 'show-virtual-fabric xmlns:nc="urn:brocade.com:mgmt:brocade-vcs"'
+)
+GET_VIRTUAL_FABRIC_INFO = (
+ 'interface xmlns:nc="urn:brocade.com:mgmt:brocade-firmware-ext"'
+)
+
+NOS_VERSION = "./*/{urn:brocade.com:mgmt:brocade-firmware-ext}os-version"
+VFAB_ENABLE = "./*/*/*/{urn:brocade.com:mgmt:brocade-vcs}vfab-enable"
+
# Create VLAN (vlan_id)
CREATE_VLAN_INTERFACE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
</config>
"""
+# Configure L2 mode for VLAN sub-profile (port_profile_name)
+CONFIGURE_L2_MODE_FOR_VLAN_PROFILE_IN_DOMAIN = """
+ <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
+ <name>{name}</name>
+ <vlan-profile>
+ <switchport-basic>
+ <basic/>
+ </switchport-basic>
+ </vlan-profile>
+ </port-profile>
+ </config>
+"""
+
# Configure L2 mode for VLAN sub-profile (port_profile_name)
CONFIGURE_L2_MODE_FOR_VLAN_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
</config>
"""
+#port-profile domain management commands
+REMOVE_PORTPROFILE_FROM_DOMAIN = """
+ <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <port-profile-domain xmlns="urn:brocade.com:mgmt:brocade-port-profile">
+ <port-profile-domain-name>{domain_name}</port-profile-domain-name>
+ <profile operation="delete">
+ <profile-name>{name}</profile-name>
+ </profile>
+ </port-profile-domain>
+ </config>
+"""
+#put port profile in default domain
+CONFIGURE_PORTPROFILE_IN_DOMAIN = """
+ <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <port-profile-domain xmlns="urn:brocade.com:mgmt:brocade-port-profile">
+ <port-profile-domain-name>{domain_name}</port-profile-domain-name>
+ <profile>
+ <profile-name>{name}</profile-name>
+ </profile>
+ </port-profile-domain>
+ </config>
+"""
+
#
# Constants
#
"""
from ncclient import manager
+from xml.etree import ElementTree
from neutron.openstack.common import excutils
from neutron.openstack.common import log as logging
def __init__(self):
self.mgr = None
+ self._virtual_fabric_enabled = False
+ self._pp_domains_supported = False
+
+ def set_features_enabled(self, pp_domains_supported,
+ virtual_fabric_enabled):
+ """Set features in the driver based on what was detected by the MD."""
+ self._pp_domains_supported = pp_domains_supported
+ self._virtual_fabric_enabled = virtual_fabric_enabled
+
+ def get_features_enabled(self):
+ """Respond to status of features enabled."""
+ return self._pp_domains_supported, self._virtual_fabric_enabled
def connect(self, host, username, password):
"""Connect via SSH and initialize the NETCONF session."""
self.mgr = manager.connect(host=host, port=SSH_PORT,
username=username, password=password,
unknown_host_cb=nos_unknown_host_cb)
+
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("Connect failed to switch"))
self.mgr.close_session()
self.mgr = None
+ def get_nos_version(self, host, username, password):
+ """Show version of NOS."""
+ try:
+ mgr = self.connect(host, username, password)
+ return self.nos_version_request(mgr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_("NETCONF error"))
+ self.close_session()
+
+ def is_virtual_fabric_enabled(self, host, username, password):
+ """Show version of NOS."""
+ try:
+ mgr = self.connect(host, username, password)
+ return (self.virtual_fabric_info(mgr) == "enabled")
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_("NETCONF error"))
+ self.close_session()
+
def create_network(self, host, username, password, net_id):
"""Creates a new virtual network."""
+ domain_name = "default"
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
self.create_vlan_interface(mgr, net_id)
self.create_port_profile(mgr, name)
+
+ if self._pp_domains_supported and self._virtual_fabric_enabled:
+ self.configure_port_profile_in_domain(mgr, domain_name, name)
+
self.create_vlan_profile_for_port_profile(mgr, name)
- self.configure_l2_mode_for_vlan_profile(mgr, name)
+
+ if self._pp_domains_supported:
+ self.configure_l2_mode_for_vlan_profile_with_domains(mgr, name)
+ else:
+ self.configure_l2_mode_for_vlan_profile(mgr, name)
+
self.configure_trunk_mode_for_vlan_profile(mgr, name)
self.configure_allowed_vlans_for_vlan_profile(mgr, name, net_id)
self.activate_port_profile(mgr, name)
def delete_network(self, host, username, password, net_id):
"""Deletes a virtual network."""
+ domain_name = "default"
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
+ if self._pp_domains_supported and self._virtual_fabric_enabled:
+ self.remove_port_profile_from_domain(mgr, domain_name, name)
self.deactivate_port_profile(mgr, name)
self.delete_port_profile(mgr, name)
self.delete_vlan_interface(mgr, net_id)
confstr = template.CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE.format(
name=name, vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)
+
+ def remove_port_profile_from_domain(self, mgr, domain_name, name):
+ """Remove port-profile from default domain."""
+ confstr = template.REMOVE_PORTPROFILE_FROM_DOMAIN.format(
+ domain_name=domain_name, name=name)
+ mgr.edit_config(target='running', config=confstr)
+
+ def configure_port_profile_in_domain(self, mgr, domain_name, name):
+ """put port-profile in default domain."""
+ confstr = template.CONFIGURE_PORTPROFILE_IN_DOMAIN.format(
+ domain_name=domain_name, name=name)
+ mgr.edit_config(target='running', config=confstr)
+
+ def configure_l2_mode_for_vlan_profile_with_domains(self, mgr, name):
+ """Configures L2 mode for VLAN sub-profile."""
+ confstr = template.CONFIGURE_L2_MODE_FOR_VLAN_PROFILE_IN_DOMAIN.format(
+ name=name)
+ mgr.edit_config(target='running', config=confstr)
+
+ def nos_version_request(self, mgr):
+ """Get firmware information using NETCONF rpc."""
+ reply = mgr.dispatch(template.SHOW_FIRMWARE_VERSION, None, None)
+ et = ElementTree.fromstring(str(reply))
+ return et.find(template.NOS_VERSION).text
+
+ def virtual_fabric_info(self, mgr):
+ """Get virtual fabric info using NETCONF get-config."""
+ response = mgr.get_config('running',
+ filter=("xpath", "/vcs/virtual-fabric"))
+ et = ElementTree.fromstring(str(response))
+ vfab_enable = et.find(template.VFAB_ENABLE)
+ if vfab_enable is not None:
+ return "enabled"
+ return "disabled"
class TestBrocadeMechDriverSubnetsV2(test_db_plugin.TestSubnetsV2,
TestBrocadeMechDriverV2):
pass
+
+
+class TestBrocadeMechDriverFeaturesEnabledTestCase(TestBrocadeMechDriverV2):
+
+ def setUp(self):
+ super(TestBrocadeMechDriverFeaturesEnabledTestCase, self).setUp()
+
+ def test_version_features(self):
+
+ vf = True
+ # Test for NOS version 4.0.3
+ self.mechanism_driver.set_features_enabled("4.0.3", vf)
+ # Verify
+ pp_domain_support, virtual_fabric_enabled = (
+ self.mechanism_driver.get_features_enabled()
+ )
+ self.assertFalse(pp_domain_support)
+ self.assertTrue(virtual_fabric_enabled)
+
+ # Test for NOS version 4.1.0
+ vf = True
+ self.mechanism_driver.set_features_enabled("4.1.0", vf)
+ # Verify
+ pp_domain_support, virtual_fabric_enabled = (
+ self.mechanism_driver.get_features_enabled()
+ )
+ self.assertTrue(pp_domain_support)
+ self.assertTrue(virtual_fabric_enabled)
+
+ # Test for NOS version 4.1.3
+ vf = False
+ self.mechanism_driver.set_features_enabled("4.1.3", vf)
+ # Verify
+ pp_domain_support, virtual_fabric_enabled = (
+ self.mechanism_driver.get_features_enabled()
+ )
+ self.assertTrue(pp_domain_support)
+ self.assertFalse(virtual_fabric_enabled)
+
+ # Test for NOS version 5.0.0
+ vf = True
+ self.mechanism_driver.set_features_enabled("5.0.0", vf)
+ # Verify
+ pp_domain_support, virtual_fabric_enabled = (
+ self.mechanism_driver.get_features_enabled()
+ )
+ self.assertTrue(pp_domain_support)
+ self.assertTrue(virtual_fabric_enabled)