Browse Source

Adding Non-ampp support for VDX

Add support for NON-AMPP(VLAN) based networking on VDX switches.
NON-AMPP based networking can be accessed through a seperate mechanism driver.

Change-Id: I4e67ff816af71ebbed1be29023c8b3c652ab3f73
tags/mitaka-eol
Ritesh Madapurath 3 years ago
parent
commit
8bfc8971a4
46 changed files with 6730 additions and 9 deletions
  1. 0
    0
      networking_brocade/vdx/ampp/__init__.py
  2. 0
    0
      networking_brocade/vdx/ampp/ml2driver/__init__.py
  3. 464
    0
      networking_brocade/vdx/ampp/ml2driver/mechanism_brocade.py
  4. 0
    0
      networking_brocade/vdx/ampp/ml2driver/nos/__init__.py
  5. 433
    0
      networking_brocade/vdx/ampp/ml2driver/nos/nctemplates.py
  6. 488
    0
      networking_brocade/vdx/ampp/ml2driver/nos/nosdriver.py
  7. 0
    0
      networking_brocade/vdx/ampp/tests/__init__.py
  8. 0
    0
      networking_brocade/vdx/ampp/tests/unit/__init__.py
  9. 0
    0
      networking_brocade/vdx/ampp/tests/unit/ml2/__init__.py
  10. 0
    0
      networking_brocade/vdx/ampp/tests/unit/ml2/drivers/__init__.py
  11. 0
    0
      networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/__init__.py
  12. 57
    0
      networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py
  13. 118
    0
      networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_mechanism_driver.py
  14. 0
    0
      networking_brocade/vdx/bare_metal/__init__.py
  15. 200
    0
      networking_brocade/vdx/bare_metal/mechanism_brocade.py
  16. 235
    0
      networking_brocade/vdx/bare_metal/util.py
  17. 0
    0
      networking_brocade/vdx/db/__init__.py
  18. 0
    0
      networking_brocade/vdx/db/migration/__init__.py
  19. 0
    0
      networking_brocade/vdx/db/migration/alembic_migrations/__init__.py
  20. 123
    0
      networking_brocade/vdx/db/migration/alembic_migrations/env.py
  21. 1
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/HEAD.bk
  22. 2
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/HEADS
  23. 27
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/kilo_release.py
  24. 35
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/contract/4fadb44a1e2d_ml2_brcd_contract.py
  25. 0
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/contract/__init__.py
  26. 0
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/expand/__init__.py
  27. 53
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/expand/a84d6a05d397_ml2_brcd.py
  28. 34
    0
      networking_brocade/vdx/db/migration/alembic_migrations/versions/start_networking_brocade_migration.py
  29. 220
    0
      networking_brocade/vdx/db/models.py
  30. 3
    2
      networking_brocade/vdx/ml2driver/mechanism_brocade.py
  31. 2
    1
      networking_brocade/vdx/ml2driver/nos/nosdriver.py
  32. 0
    0
      networking_brocade/vdx/non_ampp/__init__.py
  33. 0
    0
      networking_brocade/vdx/non_ampp/ml2driver/__init__.py
  34. 293
    0
      networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_driver.py
  35. 230
    0
      networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_plugin.py
  36. 408
    0
      networking_brocade/vdx/non_ampp/ml2driver/fwaas_plugin.py
  37. 346
    0
      networking_brocade/vdx/non_ampp/ml2driver/l3_router_plugin.py
  38. 493
    0
      networking_brocade/vdx/non_ampp/ml2driver/mechanism_brocade.py
  39. 0
    0
      networking_brocade/vdx/non_ampp/ml2driver/nos/__init__.py
  40. 825
    0
      networking_brocade/vdx/non_ampp/ml2driver/nos/nctemplates.py
  41. 1013
    0
      networking_brocade/vdx/non_ampp/ml2driver/nos/nosdriver.py
  42. 615
    0
      networking_brocade/vdx/non_ampp/ml2driver/utils.py
  43. 4
    4
      networking_brocade/vdx/services/l3_router/l3_router_plugin.py
  44. 1
    1
      networking_brocade/vdx/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py
  45. 6
    0
      setup.cfg
  46. 1
    1
      tox.ini

+ 0
- 0
networking_brocade/vdx/ampp/__init__.py View File


+ 0
- 0
networking_brocade/vdx/ampp/ml2driver/__init__.py View File


+ 464
- 0
networking_brocade/vdx/ampp/ml2driver/mechanism_brocade.py View File

@@ -0,0 +1,464 @@
# Copyright 2016 Brocade Communications System, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


"""Implentation of Brocade ML2 Mechanism driver for ML2 Plugin."""

from networking_brocade._i18n import _
from networking_brocade._i18n import _LE
from networking_brocade._i18n import _LI
from networking_brocade.vdx.db import models as brocade_db
from neutron.plugins.common import constants as p_const
from neutron.plugins.ml2 import driver_api
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import importutils

LOG = logging.getLogger(__name__)
MECHANISM_VERSION = 0.9
NOS_DRIVER = 'networking_brocade.vdx.ampp.ml2driver.nos.nosdriver.NOSdriver'

ML2_BROCADE = [cfg.StrOpt('address', default='',
help=_('The address of the host to SSH to')),
cfg.StrOpt('username', default='admin',
help=_('The SSH username to use')),
cfg.StrOpt('password', default='password', secret=True,
help=_('The SSH password to use')),
cfg.StrOpt('physical_networks', default='',
help=_('Allowed physical networks')),
cfg.StrOpt('ostype', default='NOS',
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")


class BrocadeMechanism(driver_api.MechanismDriver):

"""ML2 Mechanism driver for Brocade VDX switches.

This is the upper layer driver class that interfaces to
lower layer (NETCONF) below.
"""

def __init__(self):
self._driver = None
self._physical_networks = None
self._switch = None
self.initialize()

def initialize(self):
"""Initilize of variables needed by this class."""

self._physical_networks = cfg.CONF.ml2_brocade.physical_networks
self.brocade_init()
self._driver.close_session()

def brocade_init(self):
"""Brocade specific initialization for this class."""

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 is_flat_network(self, segment):
if not segment or segment['network_type'] == p_const.TYPE_FLAT:
LOG.info(_LI("Flat network nothing to be done"))
return True
return False

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."""
if self.is_flat_network(mech_context.network_segments[0]):
return

network = mech_context.current
context = mech_context._plugin_context
tenant_id = network['tenant_id']
network_id = network['id']

segments = mech_context.network_segments
# currently supports only one segment per network
segment = segments[0]

network_type = segment['network_type']
vlan_id = segment['segmentation_id']
segment_id = segment['id']

if segment['physical_network'] not in self._physical_networks:
raise Exception(
_("Brocade Mechanism: failed to create network, "
"network cannot be created in the configured "
"physical network"))

if network_type not in [p_const.TYPE_VLAN]:
raise Exception(
_("Brocade Mechanism: failed to create network, "
"only network type vlan is supported"))

try:
brocade_db.create_network(context, network_id, vlan_id,
segment_id, network_type, tenant_id)
except Exception:
LOG.exception(
_LE("Brocade Mechanism: failed to create network in db"))
raise Exception(
_("Brocade Mechanism: create_network_precommit failed"))

LOG.info(_LI("create network (precommit): %(network_id)s "
"of network type = %(network_type)s "
"with vlan = %(vlan_id)s "
"for tenant %(tenant_id)s"),
{'network_id': network_id,
'network_type': network_type,
'vlan_id': vlan_id,
'tenant_id': tenant_id})

def create_network_postcommit(self, mech_context):
"""Create Network as a portprofile on the switch."""

LOG.debug("create_network_postcommit: called")
if self.is_flat_network(mech_context.network_segments[0]):
return

network = mech_context.current
# use network_id to get the network attributes
# ONLY depend on our db for getting back network attributes
# this is so we can replay postcommit from db
context = mech_context._plugin_context

network_id = network['id']
network = brocade_db.get_network(context, network_id)
network_type = network.network_type
tenant_id = network['tenant_id']
vlan_id = network['vlan']

try:
self._driver.create_network(self._switch['address'],
self._switch['username'],
self._switch['password'],
vlan_id)
except Exception:
LOG.exception(_LE("Brocade NOS driver: failed in create network"))
brocade_db.delete_network(context, network_id)
raise Exception(
_("Brocade Mechanism: create_network_postcommmit failed"))

LOG.info(_LI("created network (postcommit): %(network_id)s"
" of network type = %(network_type)s"
" with vlan = %(vlan_id)s"
" for tenant %(tenant_id)s"),
{'network_id': network_id,
'network_type': network_type,
'vlan_id': vlan_id,
'tenant_id': tenant_id})

def delete_network_precommit(self, mech_context):
"""Delete Network from the plugin specific database table."""

LOG.debug("delete_network_precommit: called")
if self.is_flat_network(mech_context.network_segments[0]):
return

network = mech_context.current
network_id = network['id']
vlan_id = network['provider:segmentation_id']
tenant_id = network['tenant_id']

context = mech_context._plugin_context

try:
brocade_db.delete_network(context, network_id)
except Exception:
LOG.exception(
_LE("Brocade Mechanism: failed to delete network in db"))
raise Exception(
_("Brocade Mechanism: delete_network_precommit failed"))

LOG.info(_LI("delete network (precommit): %(network_id)s"
" with vlan = %(vlan_id)s"
" for tenant %(tenant_id)s"),
{'network_id': network_id,
'vlan_id': vlan_id,
'tenant_id': tenant_id})

def delete_network_postcommit(self, mech_context):
"""Delete network.

This translates to removng portprofile
from the switch.
"""

LOG.debug("delete_network_postcommit: called")
if self.is_flat_network(mech_context.network_segments[0]):
return

network = mech_context.current
network_id = network['id']
vlan_id = network['provider:segmentation_id']
tenant_id = network['tenant_id']

try:
self._driver.delete_network(self._switch['address'],
self._switch['username'],
self._switch['password'],
vlan_id)
except Exception:
LOG.exception(_LE("Brocade NOS driver: failed to delete network"))
raise Exception(
_("Brocade switch exception, "
"delete_network_postcommit failed"))

LOG.info(_LI("delete network (postcommit): %(network_id)s"
" with vlan = %(vlan_id)s"
" for tenant %(tenant_id)s"),
{'network_id': network_id,
'vlan_id': vlan_id,
'tenant_id': tenant_id})

def update_network_precommit(self, mech_context):
"""Noop now, it is left here for future."""
pass

def update_network_postcommit(self, mech_context):
"""Noop now, it is left here for future."""
pass

def create_port_precommit(self, mech_context):
"""Create logical port on the switch (db update)."""

LOG.debug("create_port_precommit: called")
if self.is_flat_network(mech_context.network.network_segments[0]):
return

port = mech_context.current
port_id = port['id']
network_id = port['network_id']
tenant_id = port['tenant_id']
admin_state_up = port['admin_state_up']

context = mech_context._plugin_context

network = brocade_db.get_network(context, network_id)
vlan_id = network['vlan']

try:
brocade_db.create_port(context, port_id, network_id,
None,
vlan_id, tenant_id, admin_state_up,
None)
except Exception:
LOG.exception(_LE("Brocade Mechanism: failed to create port"
" in db"))
raise Exception(
_("Brocade Mechanism: create_port_precommit failed"))

def create_port_postcommit(self, mech_context):
"""Associate the assigned MAC address to the portprofile."""

LOG.debug("create_port_postcommit: called")
if self.is_flat_network(mech_context.network.network_segments[0]):
return

port = mech_context.current
port_id = port['id']
network_id = port['network_id']
tenant_id = port['tenant_id']

context = mech_context._plugin_context

network = brocade_db.get_network(context, network_id)
if not network:
LOG.info(_LI("network not populated nothing to be done"))
return
vlan_id = network['vlan']

interface_mac = port['mac_address']

# convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx
mac = self.mac_reformat_62to34(interface_mac)
try:
self._driver.associate_mac_to_network(self._switch['address'],
self._switch['username'],
self._switch['password'],
vlan_id,
mac)
except Exception:
LOG.exception(
_LE("Brocade NOS driver: failed to associate mac %s"),
interface_mac)
raise Exception(
_("Brocade switch exception: create_port_postcommit failed"))

LOG.info(
_LI("created port (postcommit): port_id=%(port_id)s"
" network_id=%(network_id)s tenant_id=%(tenant_id)s"),
{'port_id': port_id,
'network_id': network_id, 'tenant_id': tenant_id})

def delete_port_precommit(self, mech_context):
"""Delete logical port on the switch (db update)."""

LOG.debug("delete_port_precommit: called")
if self.is_flat_network(mech_context.network.network_segments[0]):
return
port = mech_context.current
port_id = port['id']

context = mech_context._plugin_context

try:
brocade_db.delete_port(context, port_id)
except Exception:
LOG.exception(_LE("Brocade Mechanism: failed to delete port"
" in db"))
raise Exception(
_("Brocade Mechanism: delete_port_precommit failed"))

def delete_port_postcommit(self, mech_context):
"""Dissociate MAC address from the portprofile."""

LOG.debug("delete_port_postcommit: called")
if self.is_flat_network(mech_context.network.network_segments[0]):
return
port = mech_context.current
port_id = port['id']
network_id = port['network_id']
tenant_id = port['tenant_id']

context = mech_context._plugin_context

network = brocade_db.get_network(context, network_id)
if not network:
LOG.info(_LI("network not populated nothing to be done"))
return
vlan_id = network['vlan']

interface_mac = port['mac_address']

# convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx
mac = self.mac_reformat_62to34(interface_mac)
try:
self._driver.dissociate_mac_from_network(
self._switch['address'],
self._switch['username'],
self._switch['password'],
vlan_id,
mac)
except Exception:
LOG.exception(
_LE("Brocade NOS driver: failed to dissociate MAC %s"),
interface_mac)
raise Exception(
_("Brocade switch exception, delete_port_postcommit failed"))

LOG.info(
_LI("delete port (postcommit): port_id=%(port_id)s"
" network_id=%(network_id)s tenant_id=%(tenant_id)s"),
{'port_id': port_id,
'network_id': network_id, 'tenant_id': tenant_id})

def update_port_precommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("update_port_precommit(self: called")

def update_port_postcommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("update_port_postcommit: called")

def create_subnet_precommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("create_subnetwork_precommit: called")

def create_subnet_postcommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("create_subnetwork_postcommit: called")

def delete_subnet_precommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("delete_subnetwork_precommit: called")

def delete_subnet_postcommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("delete_subnetwork_postcommit: called")

def update_subnet_precommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("update_subnet_precommit(self: called")

def update_subnet_postcommit(self, mech_context):
"""Noop now, it is left here for future."""
LOG.debug("update_subnet_postcommit: called")

@staticmethod
def mac_reformat_62to34(interface_mac):
"""Transform MAC address format.

Transforms from 6 groups of 2 hexadecimal numbers delimited by ":"
to 3 groups of 4 hexadecimals numbers delimited by ".".

:param interface_mac: MAC address in the format xx:xx:xx:xx:xx:xx
:type interface_mac: string
:returns: MAC address in the format xxxx.xxxx.xxxx
:rtype: string
"""

mac = interface_mac.replace(":", "")
mac = mac[0:4] + "." + mac[4:8] + "." + mac[8:12]
return mac

+ 0
- 0
networking_brocade/vdx/ampp/ml2driver/nos/__init__.py View File


+ 433
- 0
networking_brocade/vdx/ampp/ml2driver/nos/nctemplates.py View File

@@ -0,0 +1,433 @@
# Copyright (c) 2014 Brocade Communications Systems, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


"""NOS NETCONF XML Configuration Command Templates.

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">
<interface-vlan xmlns="urn:brocade.com:mgmt:brocade-interface">
<interface>
<vlan>
<name>{vlan_id}</name>
</vlan>
</interface>
</interface-vlan>
</config>
"""

# Delete VLAN (vlan_id)
DELETE_VLAN_INTERFACE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface-vlan xmlns="urn:brocade.com:mgmt:brocade-interface">
<interface>
<vlan operation="delete">
<name>{vlan_id}</name>
</vlan>
</interface>
</interface-vlan>
</config>
"""

#
# AMPP Life-cycle Management Configuration Commands
#

# Create AMPP port-profile (port_profile_name)
CREATE_PORT_PROFILE = """
<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>
</port-profile>
</config>
"""

# Create VLAN sub-profile for port-profile (port_profile_name)
CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """
<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/>
</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">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile>
<switchport/>
</vlan-profile>
</port-profile>
</config>
"""

# Configure trunk mode for VLAN sub-profile (port_profile_name)
CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE = """
<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>
<mode>
<vlan-mode>trunk</vlan-mode>
</mode>
</switchport>
</vlan-profile>
</port-profile>
</config>
"""

# Configure allowed VLANs for VLAN sub-profile
# (port_profile_name, allowed_vlan, native_vlan)
CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE = """
<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>
<trunk>
<allowed>
<vlan>
<add>{vlan_id}</add>
</vlan>
</allowed>
</trunk>
</switchport>
</vlan-profile>
</port-profile>
</config>
"""

# Delete port-profile (port_profile_name)
DELETE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile
xmlns="urn:brocade.com:mgmt:brocade-port-profile" operation="delete">
<name>{name}</name>
</port-profile>
</config>
"""

# Activate port-profile (port_profile_name)
ACTIVATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<activate/>
</port-profile>
</port-profile-global>
</config>
"""

# Deactivate port-profile (port_profile_name)
DEACTIVATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<activate
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete" />
</port-profile>
</port-profile-global>
</config>
"""

# Associate MAC address to port-profile (port_profile_name, mac_address)
ASSOCIATE_MAC_TO_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<static>
<mac-address>{mac_address}</mac-address>
</static>
</port-profile>
</port-profile-global>
</config>
"""

# Dissociate MAC address from port-profile (port_profile_name, mac_address)
DISSOCIATE_MAC_FROM_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<static
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
<mac-address>{mac_address}</mac-address>
</static>
</port-profile>
</port-profile-global>
</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>
"""

#
# L3 Life-cycle Management Configuration Commands
#

# Create SVI and assign ippaddres (rbridge_id,vlan_id,ip_address)
CONFIGURE_SVI_WITH_IP_ADDRESS = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<interface xmlns="urn:brocade.com:mgmt:brocade-interface">
<ve>
<name>{vlan_id}</name>
<ip xmlns="urn:brocade.com:mgmt:brocade-ip-config">
<ip-config>
<address>
<address>{ip_address}</address>
</address>
</ip-config>
</ip>
</ve>
</interface>
</rbridge-id>
</config>
"""

# delete SVI (rbridge_id,vlan_id)
DELETE_SVI = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<interface xmlns="urn:brocade.com:mgmt:brocade-interface">
<ve operation="delete">
<name>{vlan_id}</name>
</ve>
</interface>
</rbridge-id>
</config>
"""

# Activate SVI (rbridge_id,vlan_id)
ACTIVATE_SVI = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<interface xmlns="urn:brocade.com:mgmt:brocade-interface">
<ve>
<name>{vlan_id}</name>
<shutdown xmlns="urn:brocade.com:mgmt:brocade-ip-config"
xc:operation="delete"></shutdown>
</ve>
</interface>
</rbridge-id>
</config>
"""

# Remove ipaddress from SVI (rbridge_id,vlan_id)
DECONFIGURE_IP_FROM_SVI = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<interface xmlns="urn:brocade.com:mgmt:brocade-interface">
<ve>
<name>{vlan_id}</name>
<ip xmlns="urn:brocade.com:mgmt:brocade-ip-config">
<ip-config>
<address xc:operation="delete">
<address>{gw_ip}</address>
</address>
</ip-config>
</ip>
</ve>
</interface>
</rbridge-id>
</config>
"""

# create vrf (rbridge_id,vrf_name)
CREATE_VRF = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<vrf xmlns="urn:brocade.com:mgmt:brocade-vrf">
<vrf-name>{vrf_name}</vrf-name>
</vrf>
</rbridge-id>
</config>
"""


# delete vrf (rbridge_id,vrf_name)
DELETE_VRF = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<vrf xmlns="urn:brocade.com:mgmt:brocade-vrf"
xc:operation="delete">
<vrf-name>{vrf_name}</vrf-name>
</vrf>
</rbridge-id>
</config>
"""

# configure route distinguisher for vrf (rbridge_id,vrf_name, rd)
CONFIGURE_RD_FOR_VRF = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<vrf xmlns="urn:brocade.com:mgmt:brocade-vrf">
<vrf-name>{vrf_name}</vrf-name>
<route-distiniguisher>{rd}</route-distiniguisher>
</vrf>
</rbridge-id>
</config>
"""

# configure address-family for vrf (rbridge_id,vrf_name)
ADD_ADDRESS_FAMILY_FOR_VRF_V1 = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<vrf xmlns="urn:brocade.com:mgmt:brocade-vrf">
<vrf-name>{vrf_name}</vrf-name>
<address-family xmlns="urn:brocade.com:mgmt:brocade-vrf">
<ipv4>
<max-route>1200</max-route>
</ipv4>
</address-family>
</vrf>
</rbridge-id>
</config>
"""

# configure address-family for vrf (rbridge_id,vrf_name)
ADD_ADDRESS_FAMILY_FOR_VRF = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<vrf xmlns="urn:brocade.com:mgmt:brocade-vrf">
<vrf-name>{vrf_name}</vrf-name>
<address-family xmlns="urn:brocade.com:mgmt:brocade-vrf">
<ip>
<unicast/>
</ip>
</address-family>
</vrf>
</rbridge-id>
</config>
"""

# Bind vrf to SVI (rbridge_id,vlan_idi, vrf)
ADD_VRF_TO_SVI = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<interface xmlns="urn:brocade.com:mgmt:brocade-interface">
<ve>
<name>{vlan_id}</name>
<vrf xmlns="urn:brocade.com:mgmt:brocade-ip-config">
<forwarding>{vrf_name}</forwarding>
</vrf>
</ve>
</interface>
</rbridge-id>
</config>
"""

# unbind vrf from SVI (rbridge_id,vlan_idi, vrf)
DELETE_VRF_FROM_SVI = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rbridge-id xmlns="urn:brocade.com:mgmt:brocade-rbridge">
<rbridge-id>{rbridge_id}</rbridge-id>
<interface xmlns="urn:brocade.com:mgmt:brocade-interface">
<ve>
<name>{vlan_id}</name>
<vrf xmlns="urn:brocade.com:mgmt:brocade-ip-config"
operation="delete">
<forwarding>{vrf_name}</forwarding>
</vrf>
</ve>
</interface>
</rbridge-id>
</config>
"""

#
# Constants
#

# Port profile naming convention for Neutron networks
OS_PORT_PROFILE_NAME = "openstack-profile-{id}"
OS_VRF_NAME = "osv-{id}"

# Port profile filter expressions
PORT_PROFILE_XPATH_FILTER = "/port-profile"
PORT_PROFILE_NAME_XPATH_FILTER = "/port-profile[name='{name}']"

+ 488
- 0
networking_brocade/vdx/ampp/ml2driver/nos/nosdriver.py View File

@@ -0,0 +1,488 @@
# Copyright 2016 Brocade Communications System, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


"""Brocade NOS Driver implements NETCONF over SSHv2 for
Neutron network life-cycle management.
"""

from ncclient import manager
from oslo_log import log as logging
from oslo_utils import excutils

from xml.etree import ElementTree

from networking_brocade._i18n import _
from networking_brocade._i18n import _LE
from networking_brocade.vdx.ampp.ml2driver.nos import nctemplates as template

LOG = logging.getLogger(__name__)
SSH_PORT = 22


def nos_unknown_host_cb(host, fingerprint):
"""An unknown host callback.

Returns `True` if it finds the key acceptable,
and `False` if not. This default callback for NOS always returns 'True'
(i.e. trusts all hosts for now).
"""
return True


class NOSdriver(object):

"""NOS NETCONF interface driver for Neutron network.

Handles life-cycle management of Neutron network (leverages AMPP on NOS)
"""

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."""

# Use the persisted NETCONF connection
if self.mgr and self.mgr.connected:
return self.mgr

# check if someone forgot to edit the conf file with real values
if host == '':
raise Exception(_("Brocade Switch IP address is not set, "
"check config ml2_conf_brocade.ini file"))

# Open new NETCONF connection
try:
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(_LE("Connect failed to switch"))

LOG.debug("Connect success to host %(host)s:%(ssh_port)d",
dict(host=host, ssh_port=SSH_PORT))
return self.mgr

def close_session(self):
"""Close NETCONF session."""
if self.mgr:
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(_LE("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(_LE("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)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

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)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

def associate_mac_to_network(self, host, username, password,
net_id, mac):
"""Associates a MAC address to virtual network."""

name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
self.associate_mac_to_port_profile(mgr, name, mac)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

def dissociate_mac_from_network(self, host, username, password,
net_id, mac):
"""Dissociates a MAC address from virtual network."""

name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
self.dissociate_mac_from_port_profile(mgr, name, mac)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

def create_vlan_interface(self, mgr, vlan_id):
"""Configures a VLAN interface."""

confstr = template.CREATE_VLAN_INTERFACE.format(vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)

def delete_vlan_interface(self, mgr, vlan_id):
"""Deletes a VLAN interface."""

confstr = template.DELETE_VLAN_INTERFACE.format(vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)

def get_port_profiles(self, mgr):
"""Retrieves all port profiles."""

filterstr = template.PORT_PROFILE_XPATH_FILTER
response = mgr.get_config(source='running',
filter=('xpath', filterstr)).data_xml
return response

def get_port_profile(self, mgr, name):
"""Retrieves a port profile."""

filterstr = template.PORT_PROFILE_NAME_XPATH_FILTER.format(name=name)
response = mgr.get_config(source='running',
filter=('xpath', filterstr)).data_xml
return response

def create_port_profile(self, mgr, name):
"""Creates a port profile."""

confstr = template.CREATE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)

def delete_port_profile(self, mgr, name):
"""Deletes a port profile."""

confstr = template.DELETE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)

def activate_port_profile(self, mgr, name):
"""Activates a port profile."""

confstr = template.ACTIVATE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)

def deactivate_port_profile(self, mgr, name):
"""Deactivates a port profile."""

confstr = template.DEACTIVATE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)

def associate_mac_to_port_profile(self, mgr, name, mac_address):
"""Associates a MAC address to a port profile."""

confstr = template.ASSOCIATE_MAC_TO_PORT_PROFILE.format(
name=name, mac_address=mac_address)
mgr.edit_config(target='running', config=confstr)

def dissociate_mac_from_port_profile(self, mgr, name, mac_address):
"""Dissociates a MAC address from a port profile."""

confstr = template.DISSOCIATE_MAC_FROM_PORT_PROFILE.format(
name=name, mac_address=mac_address)
mgr.edit_config(target='running', config=confstr)

def create_vlan_profile_for_port_profile(self, mgr, name):
"""Creates VLAN sub-profile for port profile."""

confstr = template.CREATE_VLAN_PROFILE_FOR_PORT_PROFILE.format(
name=name)
mgr.edit_config(target='running', config=confstr)

def configure_l2_mode_for_vlan_profile(self, mgr, name):
"""Configures L2 mode for VLAN sub-profile."""

confstr = template.CONFIGURE_L2_MODE_FOR_VLAN_PROFILE.format(
name=name)
mgr.edit_config(target='running', config=confstr)

def configure_trunk_mode_for_vlan_profile(self, mgr, name):
"""Configures trunk mode for VLAN sub-profile."""

confstr = template.CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE.format(
name=name)
mgr.edit_config(target='running', config=confstr)

def configure_allowed_vlans_for_vlan_profile(self, mgr, name, vlan_id):
"""Configures allowed VLANs for VLAN sub-profile."""

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"

def create_svi(self, host, username, password,
rbridge_id, vlan_id, ip_address, router_id):
"""create svi on configured rbridge-id."""
try:
mgr = self.connect(host, username, password)
self.bind_vrf_to_svi(host, username, password,
rbridge_id, vlan_id, router_id)
self.configure_svi_with_ip_address(mgr,
rbridge_id, vlan_id, ip_address)
self.activate_svi(mgr, rbridge_id, vlan_id)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error: %s"), ex)
self.close_session()

def delete_svi(self, host, username, password,
rbridge_id, vlan_id, gw_ip, router_id):
"""delete svi from configured rbridge-id."""
try:
mgr = self.connect(host, username, password)
self.remove_svi(mgr, rbridge_id, vlan_id)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error: %s"), ex)
self.close_session()

def create_router(self, host, username, password, rbridge_id, router_id):
"""create vrf and associate vrf."""
router_id = router_id[0:11]
vrf_name = template.OS_VRF_NAME.format(id=router_id)
rd = "".join(i for i in router_id if i in "0123456789")
rd = rd[:4] + ":" + rd[:4]
try:
mgr = self.connect(host, username, password)
self.create_vrf(mgr, rbridge_id, vrf_name)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()
try:
# For Nos5.0.0
self.configure_rd_for_vrf(mgr, rbridge_id, vrf_name, rd)
self.configure_address_family_for_vrf(mgr, rbridge_id, vrf_name)
except Exception:
with excutils.save_and_reraise_exception() as ctxt:
try:
# This is done because on 4.0.0 rd doesnt accept alpha
# character nor hyphen
rd = "".join(i for i in router_id if i in "0123456789")
rd = rd[:4] + ":" + rd[:4]
self.configure_rd_for_vrf(mgr, rbridge_id, vrf_name, rd)
self.configure_address_family_for_vrf_v1(mgr,
rbridge_id,
vrf_name)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

ctxt.reraise = False

def delete_router(self, host, username, password, rbridge_id, router_id):
"""delete router and associated vrf."""
router_id = router_id[0:11]
vrf_name = template.OS_VRF_NAME.format(id=router_id)
try:
mgr = self.connect(host, username, password)
self.delete_vrf(mgr, rbridge_id, vrf_name)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

def bind_vrf_to_svi(self, host, username, password, rbridge_id,
vlan_id, router_id):
"""binds vrf to a svi."""
router_id = router_id[0:11]
vrf_name = template.OS_VRF_NAME.format(id=router_id)
try:
mgr = self.connect(host, username, password)
self.add_vrf_to_svi(mgr, rbridge_id, vlan_id, vrf_name)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

def unbind_vrf_to_svi(self, host, username, password, rbridge_id,
vlan_id, router_id):
"""unbind vrf from the svi."""
router_id = router_id[0:11]
vrf_name = template.OS_VRF_NAME.format(id=router_id)
try:
mgr = self.connect(host, username, password)
self.delete_vrf_from_svi(mgr, rbridge_id, vlan_id, vrf_name)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("NETCONF error"))
self.close_session()

def create_vrf(self, mgr, rbridge_id, vrf_name):
"""create vrf on rbridge."""
confstr = template.CREATE_VRF.format(rbridge_id=rbridge_id,
vrf_name=vrf_name)
mgr.edit_config(target='running', config=confstr)

def delete_vrf(self, mgr, rbridge_id, vrf_name):
"""delete vrf on rbridge."""

confstr = template.DELETE_VRF.format(rbridge_id=rbridge_id,
vrf_name=vrf_name)
mgr.edit_config(target='running', config=confstr)

def configure_rd_for_vrf(self, mgr, rbridge_id, vrf_name, rd):
"""configure rd on vrf on rbridge."""

confstr = template.CONFIGURE_RD_FOR_VRF.format(rbridge_id=rbridge_id,
vrf_name=vrf_name,
rd=rd)
mgr.edit_config(target='running', config=confstr)

def configure_address_family_for_vrf_v1(self, mgr, rbridge_id, vrf_name):
"""configure ipv4 address family to vrf on rbridge."""

confstr = template.ADD_ADDRESS_FAMILY_FOR_VRF_V1.format(
rbridge_id=rbridge_id,
vrf_name=vrf_name)
mgr.edit_config(target='running', config=confstr)

def configure_address_family_for_vrf(self, mgr, rbridge_id, vrf_name):
"""configure ipv4 address family to vrf on rbridge."""

confstr = template.ADD_ADDRESS_FAMILY_FOR_VRF.format(
rbridge_id=rbridge_id, vrf_name=vrf_name)
mgr.edit_config(target='running', config=confstr)

def configure_svi_with_ip_address(self, mgr, rbridge_id,
vlan_id, ip_address):
"""configure SVI with ip address on rbridge."""

confstr = template.CONFIGURE_SVI_WITH_IP_ADDRESS.format(
rbridge_id=rbridge_id,
vlan_id=vlan_id,
ip_address=ip_address)

mgr.edit_config(target='running', config=confstr)

def activate_svi(self, mgr, rbridge_id, vlan_id):
"""activate the svi on the rbridge."""
confstr = template.ACTIVATE_SVI.format(rbridge_id=rbridge_id,
vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)

def add_vrf_to_svi(self, mgr, rbridge_id, vlan_id, vrf_name):
"""add vrf to svi on rbridge."""
confstr = template.ADD_VRF_TO_SVI.format(rbridge_id=rbridge_id,
vlan_id=vlan_id,
vrf_name=vrf_name)
mgr.edit_config(target='running', config=confstr)

def delete_vrf_from_svi(self, mgr, rbridge_id, vlan_id, vrf_name):
"""delete vrf from svi on rbridge."""
confstr = template.DELETE_VRF_FROM_SVI.format(rbridge_id=rbridge_id,
vlan_id=vlan_id,
vrf_name=vrf_name)
mgr.edit_config(target='running', config=confstr)

def remove_svi(self, mgr, rbridge_id, vlan_id):
"""delete vrf from svi on rbridge."""
confstr = template.DELETE_SVI.format(rbridge_id=rbridge_id,
vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)

+ 0
- 0
networking_brocade/vdx/ampp/tests/__init__.py View File


+ 0
- 0
networking_brocade/vdx/ampp/tests/unit/__init__.py View File


+ 0
- 0
networking_brocade/vdx/ampp/tests/unit/ml2/__init__.py View File


+ 0
- 0
networking_brocade/vdx/ampp/tests/unit/ml2/drivers/__init__.py View File


+ 0
- 0
networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/__init__.py View File


+ 57
- 0
networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py View File

@@ -0,0 +1,57 @@
# Copyright (c) 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#

import mock
from networking_brocade._i18n import _LI
from neutron.db import api as db
from neutron.tests.unit.extensions import test_l3
from oslo_config import cfg
from oslo_context import context as oslo_context
from oslo_log import log as logging
from oslo_utils import importutils

LOG = logging.getLogger(__name__)
L3_SVC_PLUGIN = ('neutron.services.l3_router.'
'brocade.l3_router_plugin.BrocadeSVIPlugin')


class BrocadeSVIPlugin_TestCases(test_l3.TestL3NatBasePlugin):

def setUp(self):

def mocked_brocade_init(self):
LOG.debug("brocadeSVIPlugin::mocked_brocade_init()")

self._switch = {'address': cfg.CONF.ml2_brocade.address,
'username': cfg.CONF.ml2_brocade.username,
'password': cfg.CONF.ml2_brocade.password,
'rbridge_id': cfg.CONF.ml2_brocade.rbridge_id
}
LOG.info(_LI("rbridge id %s"), self._switch['rbridge_id'])
self._driver = mock.MagicMock()

self.l3_plugin = importutils.import_object(L3_SVC_PLUGIN)
with mock.patch.object(self.l3_plugin,
'brocade_init', new=mocked_brocade_init):
super(BrocadeSVIPlugin_TestCases, self).setUp()
self.context = oslo_context.get_admin_context()
self.context.session = db.get_session()


class TestBrocadeSVINatBase(test_l3.L3NatExtensionTestCase,
BrocadeSVIPlugin_TestCases):
pass

+ 118
- 0
networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_mechanism_driver.py View File

@@ -0,0 +1,118 @@
# Copyright (c) 2016 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import mock
from networking_brocade.vdx.ml2driver import (
mechanism_brocade as brocademechanism)
from neutron.plugins.ml2 import config as ml2_config
from neutron.tests.unit.plugins.ml2 import test_plugin
from oslo_log import log as logging
from oslo_utils import importutils

LOG = logging.getLogger(__name__)

MECHANISM_NAME = ('networking_brocade.'
'vdx.ml2driver.mechanism_brocade.BrocadeMechanism')


class TestBrocadeMechDriverV2(test_plugin.Ml2PluginV2TestCase):

"""Test Brocade VCS/VDX mechanism driver.

"""

_mechanism_name = MECHANISM_NAME

def setUp(self):

_mechanism_name = MECHANISM_NAME

ml2_opts = {
'mechanism_drivers': ['brocade'],
'tenant_network_types': ['vlan']}

for opt, val in ml2_opts.items():
ml2_config.cfg.CONF.set_override(opt, val, 'ml2')

def mocked_brocade_init(self):
self._driver = mock.MagicMock()

with mock.patch.object(brocademechanism.BrocadeMechanism,
'brocade_init', new=mocked_brocade_init):
super(TestBrocadeMechDriverV2, self).setUp()
self.mechanism_driver = importutils.import_object(_mechanism_name)


class TestBrocadeMechDriverNetworksV2(test_plugin.TestMl2NetworksV2,
TestBrocadeMechDriverV2):
pass


class TestBrocadeMechDriverPortsV2(test_plugin.TestMl2PortsV2,
TestBrocadeMechDriverV2):
pass


class TestBrocadeMechDriverSubnetsV2(test_plugin.TestMl2SubnetsV2,
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)

+ 0
- 0
networking_brocade/vdx/bare_metal/__init__.py View File


+ 200
- 0
networking_brocade/vdx/bare_metal/mechanism_brocade.py View File

@@ -0,0 +1,200 @@
# Copyright 2014 Brocade Communications System, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


"""Implentation of Brocade ML2 Mechanism driver for ML2 Plugin."""

from networking_brocade._i18n import _
from networking_brocade._i18n import _LE
from networking_brocade._i18n import _LI
from networking_brocade.vdx.bare_metal import util as baremetal_util
from networking_brocade.vdx.non_ampp.ml2driver.nos import nosdriver as driver
from neutron.common import constants as n_const
from neutron.extensions import portbindings
from neutron.plugins.ml2 import driver_api as api
from oslo_config import cfg
from oslo_log import helpers as log_helpers
try:
from oslo_log import log as logging
except ImportError:
from neutron.openstack.common import log as logging

LOG = logging.getLogger(__name__)

NOS_DRIVER = 'networking_brocade.vdx.non_ampp.ml2driver'
'.nos.nosdriver.NOSdriver'
ML2_BROCADE = [cfg.StrOpt('address', default='',
help=_('The address of the host to SSH to')),
cfg.StrOpt('username', default='admin',
help=_('The SSH username to use')),
cfg.StrOpt('password', default='password', secret=True,
help=_('The SSH password to use')),
cfg.StrOpt('physical_networks', default='',
help=_('Allowed physical networks')),
cfg.StrOpt('ostype', default='NOS',
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")


class BrocadeMechanism(api.MechanismDriver):
"""ML2 Mechanism driver for Brocade VDX switches.
This is the upper layer driver class that interfaces to
lower layer (NETCONF) below.
"""

def __init__(self):
self._driver = None
self._physical_networks = None
self._switch = None
self.initialize()

def initialize(self):
"""Initilize of variables needed by this class."""

self._physical_networks = cfg.CONF.ml2_brocade.physical_networks
self.brocade_init()
self._driver.close_session()

def brocade_init(self):
"""Brocade specific initialization for this class."""

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 = driver.NOSdriver(self._switch['address'],
self._switch['username'],
self._switch['password'])

# 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'])
self._driver.close_session()

def create_network_precommit(self, mech_context):
"""Create Network in the mechanism specific database table."""

def create_network_postcommit(self, mech_context):
"""Create Network as a portprofile on the switch."""

def delete_network_precommit(self, mech_context):
"""Delete Network from the plugin specific database table."""

def delete_network_postcommit(self, mech_context):
"""Delete network.

This translates to removng portprofile
from the switch.
"""

def update_network_precommit(self, mech_context):
"""Noop now, it is left here for future."""

def update_network_postcommit(self, mech_context):
"""Noop now, it is left here for future."""

def create_port_precommit(self, mech_context):
"""Create logical port on the switch (db update)."""

def create_port_postcommit(self, mech_context):
"""Associate the assigned MAC address to the portprofile."""

def delete_port_precommit(self, mech_context):
"""Delete logical port on the switch (db update)."""

def delete_port_postcommit(self, mech_context):
"""Dissociate VLAN from baremetal connected
port.
"""
LOG.debug(("brocade_baremetal delete_port_postcommit(self: called"))
port = mech_context.current
if baremetal_util.is_baremetal_deploy(port):
params = baremetal_util.validate_physical_net_params(mech_context)
try:
# TODO(rmadapur): Handle local_link_info portgroups
for i in params["local_link_information"]:
speed, name = i['port_id']
self._driver.remove_native_vlan_from_interface(speed, name)
except Exception:
LOG.exception(_LE("Brocade NOS driver:failed to remove native"
" vlan from bare metal interface"))
raise Exception(_("NOS driver:failed to remove native vlan"))

def update_port_precommit(self, mech_context):
"""Noop now, it is left here for future."""

def update_port_postcommit(self, mech_context):
"""Noop now, it is left here for future."""

@log_helpers.log_method_call
def bind_port(self, context):
port = context.current
vnic_type = port['binding:vnic_type']

LOG.debug("Brcd:Attempting to bind port %(port)s with vnic_type "
"%(vnic_type)s on network %(network)s",
{'port': port['id'], 'vnic_type': vnic_type,
'network': context.network.current['id']})

if baremetal_util.is_baremetal_deploy(port):
segments = context.segments_to_bind
LOG.info(_LI("Segments:%s"), segments)
params = baremetal_util.validate_physical_net_params(context)
try:
# TODO(rmadapur): Handle local_link_info portgroups
for i in params["local_link_information"]:
speed, name = i['port_id']
vlan_id = segments[0][api.SEGMENTATION_ID]
self._driver.configure_native_vlan_on_interface(
speed,
name, vlan_id)
except Exception:
LOG.exception(_LE("Brocade NOS driver:failed to trunk"
" bare metal vlan"))
raise Exception(_("Brocade switch exception:"
" bind_port failed for baremetal"))
context.set_binding(segments[0][api.ID],
portbindings.VIF_TYPE_OTHER, {},
status=n_const.PORT_STATUS_ACTIVE)

def create_subnet_precommit(self, mech_context):
"""Noop now, it is left here for future."""

def create_subnet_postcommit(self, mech_context):
"""Noop now, it is left here for future."""

def delete_subnet_precommit(self, mech_context):
"""Noop now, it is left here for future."""

def delete_subnet_postcommit(self, mech_context):
"""Noop now, it is left here for future."""

def update_subnet_precommit(self, mech_context):
"""Noop now, it is left here for future."""

def update_subnet_postcommit(self, mech_context):
"""Noop now, it is left here for future."""

+ 235
- 0
networking_brocade/vdx/bare_metal/util.py View File

@@ -0,0 +1,235 @@
# Copyright 2014 Brocade Communications System, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from neutron.extensions import portbindings
from neutron.plugins.ml2.common import exceptions as ml2_exc
from neutron.plugins.ml2 import driver_api
import oslo_i18n
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
import re

LOG = logging.getLogger(__name__)
RANGE_DEFINITION = re.compile(r'(\d)-(\d)')
_translators = oslo_i18n.TranslatorFactory(domain="fj")
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical


def eliminate_val(definition, target):
"""Eliminate specified value from range value.

@param definition a string of range definition separated with ","
ex. "1,2,3" or "1-5"
@param target an integer of the target to eliminate
@return eliminated a string of eliminated value
"""
if definition is None:
return []
targets = definition.split(',')
rejected = targets
val = str(target)
LOG.info(_LI("Before rejected:%s"), targets)
for t in targets:
m = RANGE_DEFINITION.match(t)
if m:
low = m.group(1)
high = m.group(2)
if val in t:
rejected.remove(t)
# matches the lowest one
if (val == low):
# Case: definition is "1-2" and target is "1"
if ((int(val) + 1) == int(high)):
rejected.append(high)
else:
rejected.append(str(int(val) + 1) + "-" + high)
LOG.info(_LI("Rejected result:%s"), rejected)
return ','.join(rejected)
# matches the highest one
else:
# Ex. definition is "1-2" and target is "2"
if ((int(val) - 1) == int(low)):
rejected.append(low)
else:
rejected.append(low + "-" + str(int(val) - 1))
LOG.info(_LI("Rejected result:%s"), rejected)
return ','.join(rejected)
# matches between lower one and higher one
elif (int(low) < int(val) and int(val) < int(high)):
rejected.remove(t)
# Ex. definition is "1-n" and target is "2"
if ((int(val) - 1) == int(low)):
rejected.append(low)
# Ex. definition is "1-3" and target is "2"
if ((int(val) + 1) == int(high)):
rejected.append(high)
# Ex. definition is "1-4" and target is "2"
else:
rejected.append(str(int(val) + 1) + "-" + high)
# Ex. definition is "n-5" and target is "4"(n is NOT "3")
elif ((int(val) + 1) == int(high)):
rejected.append(high)
rejected.append(low + "-" + str(int(val) - 1))
# Ex. definition is "1-5" and target is "3"
else:
rejected.append(low + "-" + str(int(val) - 1))
rejected.append(str(int(val) + 1) + "-" + high)
LOG.info(_LI("Rejected result:%s"), rejected)
return ','.join(rejected)
elif val == t:
rejected.remove(t)
LOG.info(_LI('Rejected result:%s'), rejected)
return ','.join(rejected)
LOG.info(_LI('target for eliminate doesn\'t exist.'))
return ','.join(rejected)


def get_network_segments(network):
"""Get network_type and segmentation_id from specified network.

@param network a network object
@return network_type a string of network type(ex. "vlan" or "vxlan")
segmentation_id a integer of segmentation_id
"""

_validate_network(network)
segment = network.network_segments[0]
network_type = segment[driver_api.NETWORK_TYPE]
segmentation_id = segment[driver_api.SEGMENTATION_ID]
LOG.info(_LI("network_type = %s") % network_type)
LOG.info(_LI("segmentation_id = %s") % segmentation_id)
return network_type, segmentation_id


def _get_long_speed(short_speed):
if 'Te' in short_speed:
return "tengigabitethernet"

elif 'Gi' in short_speed:
return "gigabitethernet"

elif 'Fo' in short_speed:
return "fortyGigabitEthernet"

elif 'Hu' in short_speed:
return "hundredGigabitEthernet"
else:
return "unknown"


def get_physical_connectivity(port):
"""Get local_link_information from specified port.

@param port a port object
@return lli a list of following dict
{"switch_id": "MAC_of_switch", "port_id": "Te:1/0/1",
"switch_info": "switch_name"}
"""

link_infos = []
binding_profile = port['binding:profile']
lli = binding_profile.get("local_link_information", {})
is_all_specified = True if lli else False
for i in lli:
if not (i.get('switch_id') and i.get('port_id') and
i.get('switch_info')):
is_all_specified = False

else:
p = i.get('port_id')
speed, port = p.split(':')
speed = _get_long_speed(speed)
speed_port = (speed, port)
i['port_id'] = speed_port
link_infos.append(i)

if is_all_specified:
return link_infos
LOG.error(_LE("Some physical network param is missing:%s"), lli)
raise ml2_exc.MechanismDriverError(method="get_physical_connectivity")


def is_baremetal_deploy(port):
"""Judge a specified port is for baremetal or not.

@param port a port object
@return True/False a boolean baremetal:True, otherwise:False
"""

vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
if (vnic_type == portbindings.VNIC_BAREMETAL):
return True
else:
return False


def is_lag(local_link_information):
"""Judge a specified port param is for LAG(linkaggregation) or not.

@param local_link_information a list of dict
@return True/False a boolean LAG:True, otherwise:False
"""

return True if len(local_link_information) > 1 else False


def _validate_network(network):
"""Validate network parameter(network_type and segmentation_id).

@param a network object
@return None if both network_type and segmentation_id are included
"""

segment = network.network_segments[0]
vlan_id = segment[driver_api.SEGMENTATION_ID]
if (segment[driver_api.NETWORK_TYPE] == 'vlan' and vlan_id):
return
LOG.error(_LE("Fujitsu Mechanism: only network type vlan is supported"))
raise ml2_exc.MechanismDriverError(method="_validate_network_type")


@log_helpers.log_method_call
def validate_physical_net_params(mech_context):
"""Validate physical network parameters for baremetal deployment.

Validates network & port params and returns dictionary.
'local_link_information' is a dictionary from Ironic-port. This value
includes as follows:
'switch_id': A string of switch's MAC address
This value is equal to 'chassis_id' from LLDP TLV.
'port_id': A string of switch interface name.
This value is equal to 'port_id' from LLDP TLV.
'switch_info': A string of switch name.
This value is equal to 'system_name' from LLDP TLV.

@param mech_context a Context instance
@return A dictionary parameters for baremetal deploy
"""

port = mech_context.current
_validate_network(mech_context.network)

# currently supports only one segment per network
segment = mech_context.network.network_segments[0]
vlan_id = segment[driver_api.SEGMENTATION_ID]
local_link_information = get_physical_connectivity(port)
return {
"local_link_information": local_link_information,
"vlan_id": vlan_id,
"lag": is_lag(local_link_information)
}

+ 0
- 0
networking_brocade/vdx/db/__init__.py View File


+ 0
- 0
networking_brocade/vdx/db/migration/__init__.py View File


+ 0
- 0
networking_brocade/vdx/db/migration/alembic_migrations/__init__.py View File


+ 123
- 0
networking_brocade/vdx/db/migration/alembic_migrations/env.py View File

@@ -0,0 +1,123 @@
# Copyright (c) 2015 Brocade Networks, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from logging.config import fileConfig

from alembic import context
from oslo_config import cfg
from oslo_db.sqlalchemy import session
import sqlalchemy as sa
from sqlalchemy import event

from neutron.db.migration.alembic_migrations import external
from neutron.db.migration.models import head # noqa
from neutron.db import model_base

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
neutron_config = config.neutron_config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = model_base.BASEV2.metadata

MYSQL_ENGINE = None
BROCADE_VERSION_TABLE = 'brocade_alembic_version'


def set_mysql_engine():
try:
mysql_engine = neutron_config.command.mysql_engine
except cfg.NoSuchOptError:
mysql_engine = None

global MYSQL_ENGINE
MYSQL_ENGINE = (mysql_engine or
model_base.BASEV2.__table_args__['mysql_engine'])


def include_object(object, name, type_, reflected, compare_to):
if type_ == 'table' and name in external.TABLES:
return False
else:
return True


def run_migrations_offline():
"""Run migrations in 'offline' mode.

This configures the context with just a URL or an Engine.