Added support for NOS version 4.1.0, 5.0.0 and greater
NETCONF temaplates for NOS version greater than 4.1.0 are slightly different (argh). An init time check of the NOS version is done to enable selection of the correct NETCONF templates. Change-Id: I01e82ad402fbbb25d92a99a3325ca2608dd514cb Closes-bug: #1332719
This commit is contained in:
parent
5b2e730f35
commit
f5e58fff58
@ -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,
|
||||
osversion = None
|
||||
self._switch = {
|
||||
'address': cfg.CONF.ml2_brocade.address,
|
||||
'username': cfg.CONF.ml2_brocade.username,
|
||||
'password': cfg.CONF.ml2_brocade.password
|
||||
}
|
||||
'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)
|
||||
|
||||
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