Merge "Added support for NOS version 4.1.0, 5.0.0 and greater"
This commit is contained in:
commit
ac78a0f5df
@ -3,6 +3,7 @@
|
||||
# password = <mgmt admin password>
|
||||
# address = <switch mgmt ip address>
|
||||
# ostype = NOS
|
||||
# osversion = autodetect | n.n.n
|
||||
# physical_networks = physnet1,physnet2
|
||||
#
|
||||
# Example:
|
||||
@ -10,4 +11,5 @@
|
||||
# password = password
|
||||
# address = 10.24.84.38
|
||||
# ostype = NOS
|
||||
# osversion = 4.1.1
|
||||
# physical_networks = physnet1,physnet2
|
||||
|
@ -39,7 +39,9 @@ ML2_BROCADE = [cfg.StrOpt('address', default='',
|
||||
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")
|
||||
@ -66,12 +68,52 @@ class BrocadeMechanism(driver_api.MechanismDriver):
|
||||
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."""
|
||||
|
||||
|
@ -23,6 +23,24 @@
|
||||
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">
|
||||
@ -72,6 +90,20 @@ CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """
|
||||
</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">
|
||||
@ -185,6 +217,29 @@ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
|
||||
</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
|
||||
#
|
||||
|
@ -23,6 +23,7 @@ Neutron network life-cycle management.
|
||||
"""
|
||||
|
||||
from ncclient import manager
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import log as logging
|
||||
@ -51,6 +52,18 @@ class NOSdriver():
|
||||
|
||||
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."""
|
||||
@ -69,6 +82,7 @@ class NOSdriver():
|
||||
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"))
|
||||
@ -83,16 +97,46 @@ class NOSdriver():
|
||||
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)
|
||||
@ -104,9 +148,12 @@ class NOSdriver():
|
||||
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)
|
||||
@ -234,3 +281,37 @@ class NOSdriver():
|
||||
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"
|
||||
|
@ -66,3 +66,51 @@ class TestBrocadeMechDriverPortsV2(test_db_plugin.TestPortsV2,
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user