diff --git a/networking_brocade/vdx/ampp/__init__.py b/networking_brocade/vdx/ampp/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/ml2driver/__init__.py b/networking_brocade/vdx/ampp/ml2driver/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/ml2driver/mechanism_brocade.py b/networking_brocade/vdx/ampp/ml2driver/mechanism_brocade.py
new file mode 100644
index 0000000..ecdbd3c
--- /dev/null
+++ b/networking_brocade/vdx/ampp/ml2driver/mechanism_brocade.py
@@ -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
diff --git a/networking_brocade/vdx/ampp/ml2driver/nos/__init__.py b/networking_brocade/vdx/ampp/ml2driver/nos/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/ml2driver/nos/nctemplates.py b/networking_brocade/vdx/ampp/ml2driver/nos/nctemplates.py
new file mode 100644
index 0000000..f52c8c9
--- /dev/null
+++ b/networking_brocade/vdx/ampp/ml2driver/nos/nctemplates.py
@@ -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 = """
+
+
+
+
+ {vlan_id}
+
+
+
+
+"""
+
+# Delete VLAN (vlan_id)
+DELETE_VLAN_INTERFACE = """
+
+
+
+
+ {vlan_id}
+
+
+
+
+"""
+
+#
+# AMPP Life-cycle Management Configuration Commands
+#
+
+# Create AMPP port-profile (port_profile_name)
+CREATE_PORT_PROFILE = """
+
+
+ {name}
+
+
+"""
+
+# Create VLAN sub-profile for port-profile (port_profile_name)
+CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """
+
+
+ {name}
+
+
+
+"""
+
+# Configure L2 mode for VLAN sub-profile (port_profile_name)
+CONFIGURE_L2_MODE_FOR_VLAN_PROFILE_IN_DOMAIN = """
+
+
+ {name}
+
+
+
+
+
+
+
+"""
+
+# Configure L2 mode for VLAN sub-profile (port_profile_name)
+CONFIGURE_L2_MODE_FOR_VLAN_PROFILE = """
+
+
+ {name}
+
+
+
+
+
+"""
+
+# Configure trunk mode for VLAN sub-profile (port_profile_name)
+CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE = """
+
+
+ {name}
+
+
+
+ trunk
+
+
+
+
+
+"""
+
+# Configure allowed VLANs for VLAN sub-profile
+# (port_profile_name, allowed_vlan, native_vlan)
+CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE = """
+
+
+ {name}
+
+
+
+
+
+ {vlan_id}
+
+
+
+
+
+
+
+"""
+
+# Delete port-profile (port_profile_name)
+DELETE_PORT_PROFILE = """
+
+
+ {name}
+
+
+"""
+
+# Activate port-profile (port_profile_name)
+ACTIVATE_PORT_PROFILE = """
+
+
+
+ {name}
+
+
+
+
+"""
+
+# Deactivate port-profile (port_profile_name)
+DEACTIVATE_PORT_PROFILE = """
+
+
+
+ {name}
+
+
+
+
+"""
+
+# Associate MAC address to port-profile (port_profile_name, mac_address)
+ASSOCIATE_MAC_TO_PORT_PROFILE = """
+
+
+
+ {name}
+
+ {mac_address}
+
+
+
+
+"""
+
+# Dissociate MAC address from port-profile (port_profile_name, mac_address)
+DISSOCIATE_MAC_FROM_PORT_PROFILE = """
+
+
+
+ {name}
+
+ {mac_address}
+
+
+
+
+"""
+
+# port-profile domain management commands
+REMOVE_PORTPROFILE_FROM_DOMAIN = """
+
+
+ {domain_name}
+
+ {name}
+
+
+
+"""
+# put port profile in default domain
+CONFIGURE_PORTPROFILE_IN_DOMAIN = """
+
+
+ {domain_name}
+
+ {name}
+
+
+
+"""
+
+#
+# L3 Life-cycle Management Configuration Commands
+#
+
+# Create SVI and assign ippaddres (rbridge_id,vlan_id,ip_address)
+CONFIGURE_SVI_WITH_IP_ADDRESS = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+ {ip_address}
+
+
+
+
+
+
+
+"""
+
+# delete SVI (rbridge_id,vlan_id)
+DELETE_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+
+"""
+
+# Activate SVI (rbridge_id,vlan_id)
+ACTIVATE_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+
+
+"""
+
+# Remove ipaddress from SVI (rbridge_id,vlan_id)
+DECONFIGURE_IP_FROM_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+ {gw_ip}
+
+
+
+
+
+
+
+"""
+
+# create vrf (rbridge_id,vrf_name)
+CREATE_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+"""
+
+
+# delete vrf (rbridge_id,vrf_name)
+DELETE_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+"""
+
+# configure route distinguisher for vrf (rbridge_id,vrf_name, rd)
+CONFIGURE_RD_FOR_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+ {rd}
+
+
+
+"""
+
+# configure address-family for vrf (rbridge_id,vrf_name)
+ADD_ADDRESS_FAMILY_FOR_VRF_V1 = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+ 1200
+
+
+
+
+
+"""
+
+# configure address-family for vrf (rbridge_id,vrf_name)
+ADD_ADDRESS_FAMILY_FOR_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+
+
+
+
+
+"""
+
+# Bind vrf to SVI (rbridge_id,vlan_idi, vrf)
+ADD_VRF_TO_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrf_name}
+
+
+
+
+
+"""
+
+# unbind vrf from SVI (rbridge_id,vlan_idi, vrf)
+DELETE_VRF_FROM_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrf_name}
+
+
+
+
+
+"""
+
+#
+# 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}']"
diff --git a/networking_brocade/vdx/ampp/ml2driver/nos/nosdriver.py b/networking_brocade/vdx/ampp/ml2driver/nos/nosdriver.py
new file mode 100644
index 0000000..a09553b
--- /dev/null
+++ b/networking_brocade/vdx/ampp/ml2driver/nos/nosdriver.py
@@ -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)
diff --git a/networking_brocade/vdx/ampp/tests/__init__.py b/networking_brocade/vdx/ampp/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/tests/unit/__init__.py b/networking_brocade/vdx/ampp/tests/unit/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/tests/unit/ml2/__init__.py b/networking_brocade/vdx/ampp/tests/unit/ml2/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/__init__.py b/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/__init__.py b/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py b/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py
new file mode 100644
index 0000000..801b30b
--- /dev/null
+++ b/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py
@@ -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
diff --git a/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_mechanism_driver.py b/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_mechanism_driver.py
new file mode 100644
index 0000000..23d038e
--- /dev/null
+++ b/networking_brocade/vdx/ampp/tests/unit/ml2/drivers/brocade/test_brocade_mechanism_driver.py
@@ -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)
diff --git a/networking_brocade/vdx/bare_metal/__init__.py b/networking_brocade/vdx/bare_metal/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/bare_metal/mechanism_brocade.py b/networking_brocade/vdx/bare_metal/mechanism_brocade.py
new file mode 100644
index 0000000..111e67e
--- /dev/null
+++ b/networking_brocade/vdx/bare_metal/mechanism_brocade.py
@@ -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."""
diff --git a/networking_brocade/vdx/bare_metal/util.py b/networking_brocade/vdx/bare_metal/util.py
new file mode 100644
index 0000000..349ee28
--- /dev/null
+++ b/networking_brocade/vdx/bare_metal/util.py
@@ -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)
+ }
diff --git a/networking_brocade/vdx/db/__init__.py b/networking_brocade/vdx/db/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/db/migration/__init__.py b/networking_brocade/vdx/db/migration/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/__init__.py b/networking_brocade/vdx/db/migration/alembic_migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/env.py b/networking_brocade/vdx/db/migration/alembic_migrations/env.py
new file mode 100644
index 0000000..30120e9
--- /dev/null
+++ b/networking_brocade/vdx/db/migration/alembic_migrations/env.py
@@ -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.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ set_mysql_engine()
+
+ kwargs = dict()
+ if neutron_config.database.connection:
+ kwargs['url'] = neutron_config.database.connection
+ else:
+ kwargs['dialect_name'] = neutron_config.database.engine
+ kwargs['include_object'] = include_object
+ kwargs['version_table'] = BROCADE_VERSION_TABLE
+ context.configure(**kwargs)
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+@event.listens_for(sa.Table, 'after_parent_attach')
+def set_storage_engine(target, parent):
+ if MYSQL_ENGINE:
+ target.kwargs['mysql_engine'] = MYSQL_ENGINE
+
+
+def run_migrations_online():
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+ set_mysql_engine()
+ engine = session.create_engine(neutron_config.database.connection)
+
+ connection = engine.connect()
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata,
+ include_object=include_object,
+ version_table=BROCADE_VERSION_TABLE,
+ )
+
+ try:
+ with context.begin_transaction():
+ context.run_migrations()
+ finally:
+ connection.close()
+ engine.dispose()
+
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/HEAD.bk b/networking_brocade/vdx/db/migration/alembic_migrations/versions/HEAD.bk
new file mode 100644
index 0000000..638ff42
--- /dev/null
+++ b/networking_brocade/vdx/db/migration/alembic_migrations/versions/HEAD.bk
@@ -0,0 +1 @@
+4fadb44a1e2d
\ No newline at end of file
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/HEADS b/networking_brocade/vdx/db/migration/alembic_migrations/versions/HEADS
new file mode 100644
index 0000000..65e26ca
--- /dev/null
+++ b/networking_brocade/vdx/db/migration/alembic_migrations/versions/HEADS
@@ -0,0 +1,2 @@
+a84d6a05d397
+4fadb44a1e2d
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/kilo_release.py b/networking_brocade/vdx/db/migration/alembic_migrations/versions/kilo_release.py
new file mode 100644
index 0000000..90ee786
--- /dev/null
+++ b/networking_brocade/vdx/db/migration/alembic_migrations/versions/kilo_release.py
@@ -0,0 +1,27 @@
+# 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.
+#
+
+"""kilo
+
+Revision ID: kilo
+Revises: start_networking_brcd_clos
+Create Date: 2015-04-16 00:00:00.000000
+
+"""
+# revision identifiers, used by Alembic.
+revision = 'kilo'
+down_revision = 'start_ml2_brcd'
+
+
+def upgrade():
+ pass
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/contract/4fadb44a1e2d_ml2_brcd_contract.py b/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/contract/4fadb44a1e2d_ml2_brcd_contract.py
new file mode 100644
index 0000000..3aa9568
--- /dev/null
+++ b/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/contract/4fadb44a1e2d_ml2_brcd_contract.py
@@ -0,0 +1,35 @@
+# Copyright 2016 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.
+#
+
+"""ml2_brcd_contract
+
+Revision ID: 4fadb44a1e2d
+Revises: 30d703af31fb
+Create Date: 2016-02-09 15:30:27.535472
+
+"""
+
+from neutron.db.migration import cli
+
+# revision identifiers, used by Alembic.
+revision = '4fadb44a1e2d'
+down_revision = 'kilo'
+branch_labels = None
+depends_on = None
+branch_labels = (cli.CONTRACT_BRANCH,)
+
+
+def upgrade():
+ pass
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/contract/__init__.py b/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/contract/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/expand/__init__.py b/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/expand/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/expand/a84d6a05d397_ml2_brcd.py b/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/expand/a84d6a05d397_ml2_brcd.py
new file mode 100644
index 0000000..a00fb98
--- /dev/null
+++ b/networking_brocade/vdx/db/migration/alembic_migrations/versions/liberty/expand/a84d6a05d397_ml2_brcd.py
@@ -0,0 +1,53 @@
+# Copyright 2016 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.
+#
+
+"""ml2_brcd
+
+Revision ID: a84d6a05d397
+Revises: kilo
+Create Date: 2016-02-09 14:56:58.752583
+
+"""
+from alembic import op
+from neutron.db.migration import cli
+import sqlalchemy as sa
+
+# revision identifiers, used by Alembic.
+revision = 'a84d6a05d397'
+down_revision = 'kilo'
+branch_labels = None
+depends_on = None
+branch_labels = (cli.EXPAND_BRANCH,)
+
+
+def upgrade():
+
+ op.create_table('ml2_brocadesvis',
+ sa.Column('tenant_id', sa.String(
+ length=255), nullable=True),
+ sa.Column('id', sa.String(length=36), nullable=False),
+ sa.Column('svi_id', sa.String(length=36), nullable=False),
+ sa.Column('admin_state_up', sa.Boolean(), nullable=False),
+ sa.Column('ip_address', sa.String(
+ length=36), nullable=True),
+ sa.Column('subnet_mask', sa.String(
+ length=36), nullable=True),
+ sa.PrimaryKeyConstraint('id', 'svi_id'),
+ mysql_engine='InnoDB'
+ )
+ op.create_index(op.f('ix_ml2_brocadesvis_tenant_id'),
+ 'ml2_brocadesvis', ['tenant_id'], unique=False)
+ op.add_column('ml2_brocadeports', sa.Column(
+ 'host', sa.String(length=255), nullable=True))
diff --git a/networking_brocade/vdx/db/migration/alembic_migrations/versions/start_networking_brocade_migration.py b/networking_brocade/vdx/db/migration/alembic_migrations/versions/start_networking_brocade_migration.py
new file mode 100644
index 0000000..6a4589a
--- /dev/null
+++ b/networking_brocade/vdx/db/migration/alembic_migrations/versions/start_networking_brocade_migration.py
@@ -0,0 +1,34 @@
+# opyright 2015 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.
+#
+
+"""start networking-brocade chain
+
+Revision ID: start_networking_brocade_migration
+Revises: None
+Create Date: 2015-02-04 11:06:18.196062
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'start_ml2_brcd'
+down_revision = None
+
+
+def upgrade():
+ pass
+
+
+def downgrade():
+ pass
diff --git a/networking_brocade/vdx/db/models.py b/networking_brocade/vdx/db/models.py
new file mode 100644
index 0000000..f6bc925
--- /dev/null
+++ b/networking_brocade/vdx/db/models.py
@@ -0,0 +1,220 @@
+# 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.
+#
+# Authors:
+# Shiv Haris (sharis@brocade.com)
+# Varma Bhupatiraju (vbhupati@#brocade.com)
+# Ritesh Madapurath (rmadapur@brocade.com)
+# Raghuprem Muthigi (rmuthigi@brocade.com)
+
+"""Brocade specific database schema/model."""
+import sqlalchemy as sa
+
+from neutron.db import model_base
+from neutron.db import models_v2
+
+
+class ML2_BrocadeNetwork(model_base.BASEV2, models_v2.HasId,
+ models_v2.HasTenant):
+
+ """Schema for brocade network."""
+
+ vlan = sa.Column(sa.String(10))
+ segment_id = sa.Column(sa.String(36))
+ network_type = sa.Column(sa.String(10))
+
+
+class ML2_BrocadePort(model_base.BASEV2, models_v2.HasId,
+ models_v2.HasTenant):
+
+ """Schema for brocade port."""
+
+ network_id = sa.Column(sa.String(36),
+ sa.ForeignKey("ml2_brocadenetworks.id"),
+ nullable=False)
+ admin_state_up = sa.Column(sa.Boolean, nullable=False)
+ physical_interface = sa.Column(sa.String(36))
+ vlan_id = sa.Column(sa.String(36))
+ host = sa.Column(sa.String(255))
+
+
+class ML2_BrocadeSvi(model_base.BASEV2, models_v2.HasId,
+ models_v2.HasTenant):
+
+ """schema for brocade svi """
+ svi_id = sa.Column(sa.String(36), primary_key=True)
+ admin_state_up = sa.Column(sa.Boolean, nullable=False)
+ ip_address = sa.Column(sa.String(36))
+ subnet_mask = sa.Column(sa.String(36))
+
+
+def create_svi(context, router_id, tenant_id, svi,
+ admin_state_up, ip_address, net_mask):
+ """create svi port """
+ session = context.session
+ with session.begin(subtransactions=True):
+ ve = get_svi(context, router_id, tenant_id, svi, ip_address, net_mask)
+ if not ve:
+ svi = ML2_BrocadeSvi(id=router_id, tenant_id=tenant_id,
+ svi_id=svi, admin_state_up=admin_state_up,
+ ip_address=ip_address,
+ subnet_mask=net_mask)
+ session.add(svi)
+ return svi
+
+
+def delete_svi(context, router_id, tenant_id, svi, ip_address, net_mask):
+ """Delete a brocade specific network/port-profiles."""
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ svi = get_svi(context, router_id, tenant_id, svi, ip_address, net_mask)
+ if svi:
+ session.delete(svi)
+
+
+def get_svi(context, router_id, tenant_id, svi, ip_address, net_mask):
+ session = context.session
+ return session.query(ML2_BrocadeSvi).filter_by(id=router_id,
+ tenant_id=tenant_id,
+ svi_id=svi,
+ ip_address=ip_address,
+ subnet_mask=net_mask).\
+ first()
+
+
+def get_svis(context, router_id, tenant_id):
+ session = context.session
+ return session.query(ML2_BrocadeSvi).filter_by(id=router_id,
+ tenant_id=tenant_id).all()
+
+
+def get_list_svi_ids(context, router_id, tenant_id):
+ ves = []
+ svis = get_svis(context, router_id, tenant_id)
+ for svi in svis:
+ if svi['svi_id']:
+ ves.append(svi['svi_id'])
+ return ves
+
+
+def create_network(context, net_id, vlan, segment_id, network_type, tenant_id):
+ """Create a brocade specific network/port-profiles."""
+
+ # only network_type of vlan is supported
+ session = context.session
+ with session.begin(subtransactions=True):
+ net = get_network(context, net_id, None)
+ if not net:
+ net = ML2_BrocadeNetwork(id=net_id, vlan=vlan,
+ segment_id=segment_id,
+ network_type=network_type,
+ tenant_id=tenant_id)
+ session.add(net)
+ return net
+
+
+def delete_network(context, net_id):
+ """Delete a brocade specific network"""
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ net = get_network(context, net_id, None)
+ if net:
+ session.delete(net)
+
+
+def get_network(context, net_id, fields=None):
+ """Get brocade specific network, with vlan extension."""
+
+ session = context.session
+ return session.query(ML2_BrocadeNetwork).filter_by(id=net_id).first()
+
+
+def get_networks(context, filters=None, fields=None):
+ """Get all brocade specific networks."""
+
+ session = context.session
+ return session.query(ML2_BrocadeNetwork).all()
+
+
+def create_port(context, port_id, network_id, physical_interface,
+ vlan_id, tenant_id, admin_state_up, host):
+ """Create a brocade specific port, has policy like vlan."""
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ port = get_port(context, port_id)
+ if not port:
+ port = ML2_BrocadePort(id=port_id,
+ network_id=network_id,
+ physical_interface=physical_interface,
+ vlan_id=vlan_id,
+ admin_state_up=admin_state_up,
+ tenant_id=tenant_id,
+ host=host)
+ session.add(port)
+
+ return port
+
+
+def get_port(context, port_id):
+ """get a brocade specific port."""
+
+ session = context.session
+ return session.query(ML2_BrocadePort).filter_by(id=port_id).first()
+
+
+def is_vm_exists_on_host(context, host, physnet, vlan_id):
+ """check if port is tagged on host"""
+ session = context.session
+ qc = session.query(ML2_BrocadePort).filter_by(
+ physical_interface=physnet, host=host, vlan_id=vlan_id).count()
+ return qc > 1
+
+
+def is_last_vm_on_host(context, host, physnet, vlan_id):
+ """check if port is tagged on host"""
+ session = context.session
+ qc = session.query(ML2_BrocadePort).filter_by(
+ physical_interface=physnet, host=host, vlan_id=vlan_id).count()
+ return qc <= 0
+
+
+def get_ports(context, network_id=None):
+ """get a brocade specific port."""
+
+ session = context.session
+ return session.query(ML2_BrocadePort).filter_by(
+ network_id=network_id).all()
+
+
+def delete_port(context, port_id):
+ """delete brocade specific port."""
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ port = get_port(context, port_id)
+ if port:
+ session.delete(port)
+
+
+def update_port_state(context, port_id, admin_state_up):
+ """Update port attributes."""
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ session.query(ML2_BrocadePort).filter_by(
+ id=port_id).update({'admin_state_up': admin_state_up})
diff --git a/networking_brocade/vdx/ml2driver/mechanism_brocade.py b/networking_brocade/vdx/ml2driver/mechanism_brocade.py
index 19daa5f..45e9789 100644
--- a/networking_brocade/vdx/ml2driver/mechanism_brocade.py
+++ b/networking_brocade/vdx/ml2driver/mechanism_brocade.py
@@ -16,8 +16,9 @@
"""Implentation of Brocade ML2 Mechanism driver for ML2 Plugin."""
-from neutron.i18n import _LE
-from neutron.i18n import _LI
+from networking_brocade._i18n import _
+from networking_brocade._i18n import _LE
+from networking_brocade._i18n import _LI
from neutron.plugins.ml2 import driver_api
from oslo_config import cfg
from oslo_log import log as logging
diff --git a/networking_brocade/vdx/ml2driver/nos/nosdriver.py b/networking_brocade/vdx/ml2driver/nos/nosdriver.py
index ea65a7b..a9645da 100644
--- a/networking_brocade/vdx/ml2driver/nos/nosdriver.py
+++ b/networking_brocade/vdx/ml2driver/nos/nosdriver.py
@@ -24,8 +24,9 @@ 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.ml2driver.nos import nctemplates as template
-from neutron.i18n import _LE
LOG = logging.getLogger(__name__)
SSH_PORT = 22
diff --git a/networking_brocade/vdx/non_ampp/__init__.py b/networking_brocade/vdx/non_ampp/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/__init__.py b/networking_brocade/vdx/non_ampp/ml2driver/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_driver.py b/networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_driver.py
new file mode 100644
index 0000000..727e881
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_driver.py
@@ -0,0 +1,293 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2016 Brocade Networks 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 networking_brocade._i18n import _LE
+from networking_brocade._i18n import _LW
+from networking_brocade.vdx.non_ampp.ml2driver.nos import (
+ nctemplates as template)
+from networking_brocade.vdx.non_ampp.ml2driver.nos import (
+ nosdriver as driver)
+from networking_brocade.vdx.non_ampp.ml2driver import utils
+from neutron_fwaas.services.firewall.drivers import fwaas_base
+import os.path
+from oslo_log import log as logging
+from oslo_serialization import jsonutils
+
+ACL_BATCH_SIZE = 510
+LOG = logging.getLogger(__name__)
+
+
+class BrocadeFwaasDriver(fwaas_base.FwaasDriverBase):
+
+ def __init__(self):
+ LOG.debug("Initializing fwaas Brocade driver")
+ self._driver = None
+ self._seq_id_low = None
+ self._seq_id_high = None
+ self.brocade_init()
+
+ def brocade_init(self):
+ """Brocade specific initialization."""
+ LOG.debug("brocade init BrocadeFwaas Drivers")
+ self._switch = utils.get_brocade_credentials()
+ self._svi = utils.get_brocade_l3_config()
+ self._switch['rbridge_ids'] = self._svi['rbridge_ids']
+ self._fwaas = utils.get_brocade_fwaas_config()
+ LOG.debug("FWAAS PARAMETERS seq_ids %s direction %s count %s"
+ " log %s", self._fwaas['seq_ids'],
+ self._fwaas['direction'],
+ self._fwaas['count'],
+ self._fwaas['log'])
+
+ if not ((self._fwaas['direction'] == 'both') or
+ (self._fwaas['direction'] == 'in') or
+ (self._fwaas['direction'] == 'out')):
+ LOG.warning(_LW("invalid direction %s intializing"
+ " todirection both"),
+ self._fwaas['direction'])
+ self._fwaas['direction'] = 'both'
+ self._seq_id_low, self._seq_id_high = utils.get_seq_ids(
+ self._fwaas['seq_ids'])
+ self.seq_id_bm = utils.SeqIdBitmap(int(self._seq_id_low),
+ int(self._seq_id_high))
+ self._driver = driver.NOSdriver(self._switch['address'],
+ self._switch['username'],
+ self._switch['password'])
+ self._pre_acls, self._post_acls = self.open_file_if_exists(
+ self._fwaas['acl_file'])
+ self.req = []
+ self._driver.close_session()
+
+ def open_file_if_exists(self, fname):
+ pre_acls = []
+ post_acls = []
+ if os.path.isfile(fname):
+ with open(fname, "r") as acl_file:
+ try:
+ data = jsonutils.load(acl_file)
+ if 'pre_acls' in data:
+ pre_acls = data['pre_acls']
+ if 'post_acls' in data:
+ post_acls = data['post_acls']
+ LOG.debug("pre acls : %s", pre_acls)
+ LOG.debug("post acls : %s", post_acls)
+ except Exception:
+ LOG.warning(_LW("Error Loadng %s file(may be empty file)"),
+ fname)
+ return pre_acls, post_acls
+ else:
+ LOG.warning(_LW("%s file doesn't exists"), fname)
+
+ return pre_acls, post_acls
+
+ def create_firewall(self, apply_list, firewall):
+ LOG.debug('create_firewall (%s)', firewall['id'])
+ # update firewall would take lock so no lock here
+ return self.update_firewall(apply_list, firewall)
+
+ def update_firewall(self, apply_list, firewall):
+ LOG.debug("update_firewall (%s)", firewall['id'])
+
+ if firewall['admin_state_up']:
+ return self._update_firewall(apply_list, firewall)
+ else:
+ return self.apply_default_policy(apply_list, firewall)
+
+ def delete_firewall(self, apply_list, firewall):
+ LOG.debug("delete_firewall (%s)", firewall['id'])
+
+ return self.apply_default_policy(apply_list, firewall)
+
+ def apply_default_policy(self, apply_list, firewall):
+ LOG.debug("apply_default_policy (%s)", firewall['id'])
+
+ self._clear_policy(apply_list, firewall)
+ return True
+
+ def _update_firewall(self, apply_list, firewall):
+ LOG.debug("Updating firewall (%s)", firewall['id'])
+ self._clear_policy(apply_list, firewall)
+ try:
+ self._setup_policy(apply_list, firewall)
+ except Exception as e:
+ LOG.error(_LE("_update_firewall::Error creating ACL policy :"
+ "Error: %s"), e)
+ raise e
+ return True
+
+ def _apply_policy_on_interface(self, policy_name, svi):
+ LOG.debug("brocade_fwaas:_setup_policy svi %s", svi)
+ if(self._fwaas['direction'] == 'both' or
+ self._fwaas['direction'] == 'in'):
+ for rbridge_id in self._switch['rbridge_ids']:
+ self._driver.configure_policy_on_interface(rbridge_id,
+ svi,
+ policy_name,
+ "in")
+ if(self._fwaas['direction'] == 'both' or
+ self._fwaas['direction'] == 'out'):
+ for rbridge_id in self._switch['rbridge_ids']:
+ self._driver.configure_policy_on_interface(rbridge_id,
+ svi,
+ policy_name,
+ "out")
+
+ def _is_policy_exists(self, policy_name):
+ return self._driver.is_ip_acl_exists(policy_name)
+
+ def merge_and_replay_acls(self, name):
+ if self.req:
+ all_rules = "".join(self.req)
+ acl_header = template.IP_ACL_RULE_BULKING_START.format(name=name)
+ acl_footer = template.IP_ACL_RULE_BULKING_END.format()
+ acl_netconf = acl_header + all_rules + acl_footer
+ LOG.debug("merge_and_replay_acls netconf %s", acl_netconf)
+ self._driver.create_acl_rule(acl_netconf)
+ del self.req[:]
+
+ def _config_replay_acls(self, policy_name, rule, seq_id):
+ try:
+ req = self._make_policy(policy_name, rule, seq_id)
+ self.req.append(req)
+ if len(self.req) >= ACL_BATCH_SIZE:
+ self.merge_and_replay_acls(policy_name)
+ except Exception as e:
+ LOG.error(_LE("error creating rule %s"), e)
+ raise e
+ return
+
+ def _config_replay_acls_file(self, policy_name, acl_file, seq_ids, index):
+ for rule in acl_file:
+ rule = rule['acl']
+ try:
+ self._config_replay_acls(
+ policy_name, rule, str(seq_ids[index]))
+ except Exception as e:
+ LOG.error(_LE("error _config_replay_acls_file %s"), e)
+ raise e
+ index = index + 1
+ return index
+
+ def _setup_policy(self, apply_list, fw):
+ # create zones no matter if they exist. Interfaces are added by router
+ policy_name = utils.get_firewall_object_prefix(fw)
+ num_seq_id = len(fw['firewall_rule_list']) + len(self._pre_acls) +\
+ len(self._post_acls)
+ seq_ids = self.seq_id_bm.get_seq_ids(policy_name, num_seq_id)
+ index = 0
+ try:
+ if not self._driver.is_ip_acl_exists(policy_name):
+ index = self._config_replay_acls_file(policy_name,
+ self._pre_acls,
+ seq_ids, index)
+ for rule in fw['firewall_rule_list']:
+ if not rule['enabled']:
+ continue
+ if rule['ip_version'] == 4:
+ self._config_replay_acls(policy_name, rule,
+ str(seq_ids[index]))
+ index = index + 1
+ else:
+ LOG.warning(_LW("Unsupported IP version rule."))
+ index = self._config_replay_acls_file(policy_name,
+ self._post_acls,
+ seq_ids, index)
+ self.merge_and_replay_acls(policy_name)
+
+ for ri in apply_list:
+ for svi in ri.router['svis']:
+ self._apply_policy_on_interface(policy_name, svi)
+ except Exception as e:
+ LOG.error(_LE("Error creating ACL policy :Error: %s"), e)
+ self._clear_policy(apply_list, fw)
+ raise e
+
+ def _clear_policy(self, apply_list, fw):
+ policy = utils.get_firewall_object_prefix(fw)
+ for ri in apply_list:
+ for svi in ri.router['svis']:
+ LOG.debug("brocade_fwaas:_clear_policy svi %s", svi)
+ if(self._fwaas['direction'] == 'both' or
+ self._fwaas['direction'] == 'in'):
+ for rbridge_id in self._switch['rbridge_ids']:
+ self._driver.remove_policy_on_interface(rbridge_id,
+ svi,
+ policy,
+ "in")
+ if(self._fwaas['direction'] == 'both' or
+ self._fwaas['direction'] == 'out'):
+ for rbridge_id in self._switch['rbridge_ids']:
+ self._driver.remove_policy_on_interface(rbridge_id,
+ svi,
+ policy,
+ "out")
+
+ for rbridge_id in self._switch['rbridge_ids']:
+ self._driver.delete_policy(rbridge_id, policy)
+
+ def _make_policy(self, name, rule, seq_id):
+ src_ip = 'any'
+ dst_ip = 'any'
+ src_mask = ''
+ dst_mask = ''
+ sport_operator = None
+ sport_low = None
+ sport_high = None
+ dport_operator = None
+ dport_low = None
+ dport_high = None
+ dscp = None
+ if rule.get('action') == 'allow':
+ action = 'permit'
+ else:
+ action = 'deny'
+
+ protocol = rule.get('protocol')
+ if((protocol == 'any') or (protocol is None)):
+ protocol = 'ip'
+ if ((protocol == 'tcp') or (protocol == 'udp')):
+ if 'source_port' in rule:
+ sport = rule['source_port']
+ sport_low, sport_high = utils.get_ports(sport)
+ sport_operator = utils.get_port_operator(sport_low, sport_high)
+ if 'destination_port' in rule:
+ dport = rule['destination_port']
+ dport_low, dport_high = utils.get_ports(dport)
+ dport_operator = utils.get_port_operator(dport_low,
+ dport_high)
+
+ src_address = rule.get('source_ip_address') or 'any'
+ if src_address != 'any':
+ src_ip, src_mask = utils.cidr_2_nwm(src_address)
+ dst_address = rule.get('destination_ip_address') or 'any'
+ if dst_address != 'any':
+ dst_ip, dst_mask = utils.cidr_2_nwm(dst_address)
+
+ if 'dscp' in rule:
+ dscp = str(rule.get('dscp'))
+ try:
+ xml_request = utils.make_rule(
+ name, seq_id, action, protocol, src_ip,
+ src_mask, dst_ip, dst_mask, sport_operator, sport_low,
+ sport_high, dport_operator, dport_low, dport_high,
+ self._fwaas['count'], self._fwaas['log'], dscp)
+ except Exception as e:
+ LOG.error(_LE("error _make_policy %s"), e)
+ raise e
+
+ LOG.debug("xml request %s", xml_request)
+
+ return xml_request
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_plugin.py b/networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_plugin.py
new file mode 100644
index 0000000..093f236
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/brocade_fwaas_plugin.py
@@ -0,0 +1,230 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2016 Brocade Networks 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 networking_brocade._i18n import _LE
+from networking_brocade._i18n import _LI
+from networking_brocade.vdx.non_ampp.ml2driver import\
+ brocade_fwaas_driver as fwaas_driver
+from networking_brocade.vdx.non_ampp.ml2driver import fwaas_plugin as plugin
+from networking_brocade.vdx.non_ampp.ml2driver import utils
+from neutron import manager
+from neutron.plugins.common import constants as const
+from neutron.plugins.common import constants as plugin_constants
+from neutron.plugins.ml2.drivers.brocade.db import models as brocade_db
+import neutron_fwaas
+from neutron_fwaas.extensions import firewall as fw_ext
+from oslo_config import cfg
+from oslo_log import log as logging
+import threading
+LOG = logging.getLogger(__name__)
+
+
+class RouterInfo(object):
+
+ def __init__(self, router):
+ self._router = router
+
+ @property
+ def router(self):
+ return self._router
+
+
+class BrocadeFirewallPlugin(plugin.FirewallPlugin):
+
+ """Implementation of the Neutron Brocade Firewall Service Plugin.
+ This class manages fwass request and response with the help
+ fwaas_driver.BrocadeFwaasDriver
+ """
+ supported_extension_aliases = ["fwaas"]
+ path_prefix = fw_ext.FIREWALL_PREFIX
+
+ def __init__(self):
+ super(BrocadeFirewallPlugin, self).__init__()
+ ext_path = neutron_fwaas.extensions.__path__[0]
+ if ext_path not in cfg.CONF.api_extensions_path.split(':'):
+ cfg.CONF.set_override('api_extensions_path',
+ cfg.CONF.api_extensions_path + ':' +
+ ext_path)
+ self._fwaas_driver = fwaas_driver.BrocadeFwaasDriver()
+ self._lock = threading.Lock()
+
+ def get_plugin_type(self):
+ return plugin_constants.FIREWALL
+
+ def _is_l3_agent_running(self, context):
+ l3plugin = manager.NeutronManager.get_service_plugins()[
+ plugin_constants.L3_ROUTER_NAT]
+ if not l3plugin:
+ LOG.error(_LE('No plugin for L3 routing registered! Will reply '
+ 'no l3 agents!! '))
+ return False
+ l3_agents = l3plugin.get_l3_agents(context, True)
+ LOG.info(_LI("List of L3 agents _is_l3_agent_running %s"), l3_agents)
+ return ((l3_agents) and (len(l3_agents) > 0))
+
+ def _get_routers(self, context):
+ """get all active routers for tenant"""
+ l3plugin = manager.NeutronManager.get_service_plugins()[
+ plugin_constants.L3_ROUTER_NAT]
+ if not l3plugin:
+ routers = {}
+ LOG.error(_LE('No plugin for L3 routing registered! Will reply '
+ 'to l3 agent with empty router dictionary.'))
+ return routers
+ routers = l3plugin._get_sync_routers(context)
+ for r in routers:
+ router_id = r['id']
+ tenant_id = r['tenant_id']
+ svis = brocade_db.get_list_svi_ids(context, router_id, tenant_id)
+ r['svis'] = svis
+ return routers
+
+ def handle_router_interface_add(self, context, svi, tenant_id):
+ fw_list = self.get_firewalls(context)
+ for fw in fw_list:
+ fw = self._make_firewall_dict(fw)
+ policy_name = utils.get_firewall_object_prefix(fw)
+ if fw['tenant_id'] == tenant_id and\
+ fw['status'] == const.ACTIVE:
+ try:
+ if not self._fwaas_driver._is_policy_exists(policy_name):
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context,
+ fw['id']))
+ self._invoke_driver_for_plugin_api(context,
+ fw_with_rules,
+ 'update_firewall')
+ else:
+ self._fwaas_driver._apply_policy_on_interface(
+ policy_name, svi)
+ except Exception as e:
+ LOG.error(_LE("Error adding Firewall rule to"
+ "interface %s "), e)
+ elif fw['tenant_id'] == tenant_id and\
+ fw['status'] == const.PENDING_CREATE:
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context, fw['id']))
+ self._invoke_driver_for_plugin_api(context, fw_with_rules,
+ 'update_firewall')
+ self.endpoints[0].set_firewall_status(context, fw['id'],
+ const.ACTIVE)
+
+ def create_firewall(self, context, firewall):
+ with self._lock:
+ fw = super(BrocadeFirewallPlugin, self).create_firewall(
+ context, firewall)
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context, fw['id']))
+ self._invoke_driver_for_plugin_api(context, fw_with_rules,
+ 'create_firewall')
+ return fw
+
+ def update_firewall(self, context, id, firewall):
+ with self._lock:
+ fw = super(BrocadeFirewallPlugin, self).update_firewall(context,
+ id,
+ firewall)
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context, fw['id']))
+ self._invoke_driver_for_plugin_api(context, fw_with_rules,
+ 'update_firewall')
+ return fw
+
+ def delete_firewall(self, context, id):
+ with self._lock:
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context, id))
+ super(BrocadeFirewallPlugin, self).delete_firewall(context, id)
+ self._invoke_driver_for_plugin_api(context, fw_with_rules,
+ 'delete_firewall')
+ self.endpoints[0].firewall_deleted(context, id)
+
+ def _rpc_update_firewall(self, context, firewall_id):
+ with self._lock:
+ super(BrocadeFirewallPlugin, self).\
+ _rpc_update_firewall(context,
+ firewall_id)
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context,
+ firewall_id))
+ self._invoke_driver_for_plugin_api(context,
+ fw_with_rules,
+ 'update_firewall')
+
+ def _get_router_info_list_for_tenant(self, routers, tenant_id):
+ """Returns the list of router info objects on which to apply the fw."""
+ router_info_list = []
+ for router in routers:
+ # for routers without an interface - _get_routers returns
+ # the router - but this is not yet populated in router_info
+ if router['tenant_id'] != tenant_id:
+ continue
+ LOG.info(_LI("_get_router_info_list_for_tenant router %s"), router)
+ # This is done to Keep fwaas driver code unchanged
+ ri = RouterInfo(router)
+ router_info_list.append(ri)
+ return router_info_list
+
+ def _is_interface_present_added_to_routers(self, appply_list):
+ for ri in appply_list:
+ router = ri.router
+ if router['svis']:
+ return True
+ return False
+
+ def _invoke_driver_for_plugin_api(self, context, fw, func_name):
+ """Invoke driver method for plugin API and provide status back."""
+ LOG.debug("%(func_name)s from agent for fw: %(fwid)s",
+ {'func_name': func_name, 'fwid': fw['id']})
+ try:
+ routers = self._get_routers(context)
+ router_info_list = self._get_router_info_list_for_tenant(
+ routers,
+ fw['tenant_id'])
+ if not router_info_list and func_name != 'delete_firewall':
+ LOG.debug('No Routers on tenant: %s', fw['tenant_id'])
+ # fw was created before any routers were added, and if a
+ # delete is sent then we need to ack so that plugin can
+ # cleanup.
+ return
+ elif not self._is_interface_present_added_to_routers(
+ router_info_list) and func_name != 'delete_firewall':
+ LOG.debug('No Router interface')
+ return
+ LOG.debug("Apply fw on Router List: '%s'",
+ [ri.router['id'] for ri in router_info_list])
+ # call into the driver
+ try:
+ self._fwaas_driver.__getattribute__(func_name)(
+ router_info_list,
+ fw)
+ if func_name != "delete_firewall":
+ self.endpoints[0].set_firewall_status(context, fw['id'],
+ const.ACTIVE)
+ except Exception as e:
+ LOG.error(_LE("Exception %s"), e)
+ LOG.error(_LE("Firewall Driver Error for %(func_name)s "
+ "for fw: %(fwid)s"),
+ {'func_name': func_name, 'fwid': fw['id']})
+ raise e
+
+ except Exception as e:
+ LOG.exception(
+ _LE("FWaaS RPC failure in %(func_name)s for fw: %(fwid)s"),
+ {'func_name': func_name, 'fwid': fw['id']})
+ raise e
+
+ return
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/fwaas_plugin.py b/networking_brocade/vdx/non_ampp/ml2driver/fwaas_plugin.py
new file mode 100644
index 0000000..8ff2c42
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/fwaas_plugin.py
@@ -0,0 +1,408 @@
+# Copyright 2016 Big Switch Networks, 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 networking_brocade._i18n import _
+from networking_brocade._i18n import _LW
+from neutron.api.v2 import attributes as attr
+from neutron.common import exceptions as n_exception
+from neutron.common import rpc as n_rpc
+from neutron.common import topics
+from neutron import context as neutron_context
+from neutron import manager
+from neutron.plugins.common import constants as const
+from oslo_config import cfg
+from oslo_log import log as logging
+import oslo_messaging
+
+from neutron_fwaas.db.firewall import firewall_db
+from neutron_fwaas.db.firewall import firewall_router_insertion_db
+from neutron_fwaas.extensions import firewall as fw_ext
+
+
+LOG = logging.getLogger(__name__)
+
+
+class FirewallCallbacks(object):
+ target = oslo_messaging.Target(version='1.0')
+
+ def __init__(self, plugin):
+ super(FirewallCallbacks, self).__init__()
+ self.plugin = plugin
+
+ def set_firewall_status(self, context, firewall_id, status, **kwargs):
+ """Agent uses this to set a firewall's status."""
+ LOG.debug("Setting firewall %s to status: %s" % (firewall_id, status))
+ # Sanitize status first
+ if status in (const.ACTIVE, const.DOWN, const.INACTIVE):
+ to_update = status
+ else:
+ to_update = const.ERROR
+ # ignore changing status if firewall expects to be deleted
+ # That case means that while some pending operation has been
+ # performed on the backend, neutron server received delete request
+ # and changed firewall status to PENDING_DELETE
+ updated = self.plugin.update_firewall_status(
+ context, firewall_id, to_update, not_in=(const.PENDING_DELETE,))
+ if updated:
+ LOG.debug("firewall %s status set: %s" % (firewall_id, to_update))
+ return updated and to_update != const.ERROR
+
+ def firewall_deleted(self, context, firewall_id, **kwargs):
+ """Agent uses this to indicate firewall is deleted."""
+ LOG.debug("firewall_deleted() called")
+ with context.session.begin(subtransactions=True):
+ fw_db = self.plugin._get_firewall(context, firewall_id)
+ # allow to delete firewalls in ERROR state
+ if fw_db.status in (const.PENDING_DELETE, const.ERROR):
+ self.plugin.delete_db_firewall_object(context, firewall_id)
+ return True
+ else:
+ LOG.warning(_LW('Firewall %(fw)s unexpectedly'
+ ' deleted by agent, '
+ 'status was %(status)s'),
+ {'fw': firewall_id, 'status': fw_db.status})
+ fw_db.update({"status": const.ERROR})
+ return False
+
+ def get_firewalls_for_tenant(self, context, **kwargs):
+ """Agent uses this to get all firewalls and rules for a tenant."""
+ LOG.debug("get_firewalls_for_tenant() called")
+ fw_list = []
+ for fw in self.plugin.get_firewalls(context):
+ fw_with_rules = self.plugin._make_firewall_dict_with_rules(
+ context, fw['id'])
+ if fw['status'] == const.PENDING_DELETE:
+ fw_with_rules['add-router-ids'] = []
+ fw_with_rules['del-router-ids'] = (
+ self.plugin.get_firewall_routers(context, fw['id']))
+ else:
+ fw_with_rules['add-router-ids'] = (
+ self.plugin.get_firewall_routers(context, fw['id']))
+ fw_with_rules['del-router-ids'] = []
+ fw_list.append(fw_with_rules)
+ return fw_list
+
+ def get_firewalls_for_tenant_without_rules(self, context, **kwargs):
+ """Agent uses this to get all firewalls for a tenant."""
+ LOG.debug("get_firewalls_for_tenant_without_rules() called")
+ fw_list = [fw for fw in self.plugin.get_firewalls(context)]
+ return fw_list
+
+ def get_tenants_with_firewalls(self, context, **kwargs):
+ """Agent uses this to get all tenants that have firewalls."""
+ LOG.debug("get_tenants_with_firewalls() called")
+ ctx = neutron_context.get_admin_context()
+ fw_list = self.plugin.get_firewalls(ctx)
+ fw_tenant_list = list(set(fw['tenant_id'] for fw in fw_list))
+ return fw_tenant_list
+
+
+class FirewallAgentApi(object):
+
+ """Plugin side of plugin to agent RPC API."""
+
+ def __init__(self, topic, host):
+ self.host = host
+ target = oslo_messaging.Target(topic=topic, version='1.0')
+ self.client = n_rpc.get_client(target)
+
+ def create_firewall(self, context, firewall):
+ cctxt = self.client.prepare(fanout=True)
+ cctxt.cast(context, 'create_firewall', firewall=firewall,
+ host=self.host)
+
+ def update_firewall(self, context, firewall):
+ cctxt = self.client.prepare(fanout=True)
+ cctxt.cast(context, 'update_firewall', firewall=firewall,
+ host=self.host)
+
+ def delete_firewall(self, context, firewall):
+ cctxt = self.client.prepare(fanout=True)
+ cctxt.cast(context, 'delete_firewall', firewall=firewall,
+ host=self.host)
+
+
+class FirewallCountExceeded(n_exception.Conflict):
+
+ """Reference implementation specific exception for firewall count.
+
+ Only one firewall is supported per tenant. When a second
+ firewall is tried to be created, this exception will be raised.
+ """
+ message = _("Exceeded allowed count of firewalls for tenant "
+ "%(tenant_id)s. Only one firewall is supported per tenant.")
+
+
+class FirewallPlugin(
+ firewall_db.Firewall_db_mixin,
+ firewall_router_insertion_db.FirewallRouterInsertionDbMixin):
+
+ """Implementation of the Neutron Firewall Service Plugin.
+
+ This class manages the workflow of FWaaS request/response.
+ Most DB related works are implemented in class
+ firewall_db.Firewall_db_mixin.
+ """
+ supported_extension_aliases = ["fwaas"]
+ path_prefix = fw_ext.FIREWALL_PREFIX
+
+ def __init__(self):
+ """Do the initialization for the firewall service plugin here."""
+ self.endpoints = [FirewallCallbacks(self)]
+
+ self.conn = n_rpc.create_connection(new=True)
+ self.conn.create_consumer(
+ topics.FIREWALL_PLUGIN, self.endpoints, fanout=False)
+ self.conn.consume_in_threads()
+
+ self.agent_rpc = FirewallAgentApi(
+ topics.L3_AGENT,
+ cfg.CONF.host
+ )
+ firewall_db.subscribe()
+
+ def _rpc_update_firewall(self, context, firewall_id):
+ status_update = {"firewall": {"status": const.PENDING_UPDATE}}
+ super(FirewallPlugin, self).update_firewall(context, firewall_id,
+ status_update)
+ fw_with_rules = self._make_firewall_dict_with_rules(context,
+ firewall_id)
+ # this is triggered on an update to fw rule or policy, no
+ # change in associated routers.
+ fw_with_rules['add-router-ids'] = self.get_firewall_routers(
+ context, firewall_id)
+ fw_with_rules['del-router-ids'] = []
+ self.agent_rpc.update_firewall(context, fw_with_rules)
+
+ def _rpc_update_firewall_policy(self, context, firewall_policy_id):
+ firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
+ if firewall_policy:
+ for firewall_id in firewall_policy['firewall_list']:
+ self._rpc_update_firewall(context, firewall_id)
+
+ def _ensure_update_firewall(self, context, firewall_id):
+ fwall = self.get_firewall(context, firewall_id)
+ if fwall['status'] in [const.PENDING_CREATE,
+ const.PENDING_UPDATE,
+ const.PENDING_DELETE]:
+ raise fw_ext.FirewallInPendingState(firewall_id=firewall_id,
+ pending_state=fwall['status'])
+
+ def _ensure_update_firewall_policy(self, context, firewall_policy_id):
+ firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
+ if firewall_policy and 'firewall_list' in firewall_policy:
+ for firewall_id in firewall_policy['firewall_list']:
+ self._ensure_update_firewall(context, firewall_id)
+
+ def _ensure_update_firewall_rule(self, context, firewall_rule_id):
+ fw_rule = self.get_firewall_rule(context, firewall_rule_id)
+ if 'firewall_policy_id' in fw_rule and fw_rule['firewall_policy_id']:
+ self._ensure_update_firewall_policy(context,
+ fw_rule['firewall_policy_id'])
+
+ def _get_routers_for_create_firewall(self, tenant_id, context, firewall):
+
+ # pop router_id as this goes in the router association db
+ # and not firewall db
+ router_ids = firewall['firewall'].pop('router_ids', None)
+ if router_ids == attr.ATTR_NOT_SPECIFIED:
+ # old semantics router-ids keyword not specified pick up
+ # all routers on tenant.
+ l3_plugin = manager.NeutronManager.get_service_plugins().get(
+ const.L3_ROUTER_NAT)
+ ctx = neutron_context.get_admin_context()
+ routers = l3_plugin.get_routers(ctx)
+ router_ids = [
+ router['id']
+ for router in routers
+ if router['tenant_id'] == tenant_id]
+ # validation can still fail this if there is another fw
+ # which is associated with one of these routers.
+ self.validate_firewall_routers_not_in_use(context, router_ids)
+ return router_ids
+ else:
+ if not router_ids:
+ # This indicates that user specifies no routers.
+ return []
+ else:
+ # some router(s) provided.
+ self.validate_firewall_routers_not_in_use(context, router_ids)
+ return router_ids
+
+ def create_firewall(self, context, firewall):
+ LOG.debug("create_firewall() called")
+ tenant_id = self._get_tenant_id_for_create(context,
+ firewall['firewall'])
+ fw_count = self.get_firewalls_count(context,
+ filters={'tenant_id': [tenant_id]})
+ if fw_count:
+ raise FirewallCountExceeded(tenant_id=tenant_id)
+
+ fw_new_rtrs = self._get_routers_for_create_firewall(
+ tenant_id, context, firewall)
+
+ if not fw_new_rtrs:
+ # no messaging to agent needed, and fw needs to go
+ # to INACTIVE(no associated rtrs) state.
+ status = const.INACTIVE
+ fw = super(FirewallPlugin, self).create_firewall(
+ context, firewall, status)
+ fw['router_ids'] = []
+ return fw
+ else:
+ fw = super(FirewallPlugin, self).create_firewall(
+ context, firewall)
+ fw['router_ids'] = fw_new_rtrs
+
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context, fw['id']))
+
+ fw_with_rtrs = {'fw_id': fw['id'],
+ 'router_ids': fw_new_rtrs}
+ self.set_routers_for_firewall(context, fw_with_rtrs)
+ fw_with_rules['add-router-ids'] = fw_new_rtrs
+ fw_with_rules['del-router-ids'] = []
+
+ self.agent_rpc.create_firewall(context, fw_with_rules)
+
+ return fw
+
+ def update_firewall(self, context, id, firewall):
+ LOG.debug("update_firewall() called on firewall %s", id)
+
+ self._ensure_update_firewall(context, id)
+ # pop router_id as this goes in the router association db
+ # and not firewall db
+ router_ids = firewall['firewall'].pop('router_ids', None)
+ fw_current_rtrs = self.get_firewall_routers(context, id)
+ if router_ids is not None:
+ if router_ids == []:
+ # This indicates that user is indicating no routers.
+ fw_new_rtrs = []
+ else:
+ self.validate_firewall_routers_not_in_use(
+ context, router_ids, id)
+ fw_new_rtrs = router_ids
+ self.update_firewall_routers(context, {'fw_id': id,
+ 'router_ids': fw_new_rtrs})
+ else:
+ # router-ids keyword not specified for update pick up
+ # existing routers.
+ fw_new_rtrs = self.get_firewall_routers(context, id)
+
+ if not fw_new_rtrs and not fw_current_rtrs:
+ # no messaging to agent needed, and we need to continue
+ # in INACTIVE state
+ firewall['firewall']['status'] = const.INACTIVE
+ fw = super(FirewallPlugin, self).update_firewall(
+ context, id, firewall)
+ fw['router_ids'] = []
+ return fw
+ else:
+ firewall['firewall']['status'] = const.PENDING_UPDATE
+ fw = super(FirewallPlugin, self).update_firewall(
+ context, id, firewall)
+ fw['router_ids'] = fw_new_rtrs
+
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context, fw['id']))
+
+ # determine rtrs to add fw to and del from
+ fw_with_rules['add-router-ids'] = fw_new_rtrs
+ fw_with_rules['del-router-ids'] = list(
+ set(fw_current_rtrs).difference(set(fw_new_rtrs)))
+
+ # last-router drives agent to ack with status to set state to INACTIVE
+ fw_with_rules['last-router'] = not fw_new_rtrs
+
+ LOG.debug("update_firewall %s: Add Routers: %s, Del Routers: %s",
+ fw['id'],
+ fw_with_rules['add-router-ids'],
+ fw_with_rules['del-router-ids'])
+
+ self.agent_rpc.update_firewall(context, fw_with_rules)
+
+ return fw
+
+ def delete_db_firewall_object(self, context, id):
+ super(FirewallPlugin, self).delete_firewall(context, id)
+
+ def delete_firewall(self, context, id):
+ LOG.debug("delete_firewall() called on firewall %s", id)
+ fw_with_rules = (
+ self._make_firewall_dict_with_rules(context, id))
+ fw_with_rules['del-router-ids'] = self.get_firewall_routers(
+ context, id)
+ fw_with_rules['add-router-ids'] = []
+ if not fw_with_rules['del-router-ids']:
+ # no routers to delete on the agent side
+ self.delete_db_firewall_object(context, id)
+ else:
+ status = {"firewall": {"status": const.PENDING_DELETE}}
+ super(FirewallPlugin, self).update_firewall(context, id, status)
+ # Reflect state change in fw_with_rules
+ fw_with_rules['status'] = status['firewall']['status']
+ self.agent_rpc.delete_firewall(context, fw_with_rules)
+
+ def update_firewall_policy(self, context, id, firewall_policy):
+ LOG.debug("update_firewall_policy() called")
+ self._ensure_update_firewall_policy(context, id)
+ fwp = super(FirewallPlugin,
+ self).update_firewall_policy(context, id, firewall_policy)
+ self._rpc_update_firewall_policy(context, id)
+ return fwp
+
+ def update_firewall_rule(self, context, id, firewall_rule):
+ LOG.debug("update_firewall_rule() called")
+ self._ensure_update_firewall_rule(context, id)
+ fwr = super(FirewallPlugin,
+ self).update_firewall_rule(context, id, firewall_rule)
+ firewall_policy_id = fwr['firewall_policy_id']
+ if firewall_policy_id:
+ self._rpc_update_firewall_policy(context, firewall_policy_id)
+ return fwr
+
+ def insert_rule(self, context, id, rule_info):
+ LOG.debug("insert_rule() called")
+ self._ensure_update_firewall_policy(context, id)
+ fwp = super(FirewallPlugin,
+ self).insert_rule(context, id, rule_info)
+ self._rpc_update_firewall_policy(context, id)
+ return fwp
+
+ def remove_rule(self, context, id, rule_info):
+ LOG.debug("remove_rule() called")
+ self._ensure_update_firewall_policy(context, id)
+ fwp = super(FirewallPlugin,
+ self).remove_rule(context, id, rule_info)
+ self._rpc_update_firewall_policy(context, id)
+ return fwp
+
+ def get_firewalls(self, context, filters=None, fields=None):
+ LOG.debug("fwaas get_firewalls() called")
+ fw_list = super(FirewallPlugin, self).get_firewalls(
+ context, filters, fields)
+ for fw in fw_list:
+ fw_current_rtrs = self.get_firewall_routers(context, fw['id'])
+ fw['router_ids'] = fw_current_rtrs
+ return fw_list
+
+ def get_firewall(self, context, id, fields=None):
+ LOG.debug("fwaas get_firewall() called")
+ res = super(FirewallPlugin, self).get_firewall(
+ context, id, fields)
+ fw_current_rtrs = self.get_firewall_routers(context, id)
+ res['router_ids'] = fw_current_rtrs
+ return res
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/l3_router_plugin.py b/networking_brocade/vdx/non_ampp/ml2driver/l3_router_plugin.py
new file mode 100644
index 0000000..a0cf168
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/l3_router_plugin.py
@@ -0,0 +1,346 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# 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 SVI service Plugin."""
+from networking_brocade._i18n import _
+from networking_brocade._i18n import _LE
+from networking_brocade._i18n import _LI
+from networking_brocade._i18n import _LW
+from networking_brocade.vdx.db import models as brocade_db
+from networking_brocade.vdx.non_ampp.ml2driver.nos import nosdriver as driver
+from networking_brocade.vdx.non_ampp.ml2driver import utils
+from neutron.api.v2 import attributes
+from neutron.common import constants as l3_constants
+from neutron.common import utils as neutron_utils
+from neutron.db import models_v2
+from neutron.extensions import extraroute
+from neutron import manager
+from neutron.plugins.common import constants as plugin_constants
+from neutron.services.l3_router import l3_router_plugin as router
+from oslo_log import log as logging
+from oslo_utils import excutils
+
+DEVICE_OWNER_ROUTER_INTF = l3_constants.DEVICE_OWNER_ROUTER_INTF
+DEVICE_OWNER_ROUTER_GW = l3_constants.DEVICE_OWNER_ROUTER_GW
+DEVICE_OWNER_FLOATINGIP = l3_constants.DEVICE_OWNER_FLOATINGIP
+
+LOG = logging.getLogger(__name__)
+
+
+class BrocadeSVIPlugin(router.L3RouterPlugin):
+
+ def __init__(self):
+ """Initialize Brocade Plugin.
+ Specify switch address and db configuration.
+ """
+ super(BrocadeSVIPlugin, self).__init__()
+ self._driver = None
+ self._switch = None
+ self.brocade_init()
+
+ def brocade_init(self):
+ """Brocade specific initialization."""
+ LOG.debug("brocade init brocadeSVIPlugin")
+ self._switch = utils.get_brocade_credentials()
+ self._svi = utils.get_brocade_l3_config()
+ LOG.info(_LI("rbridge id %(rbridge id)s redundancy %(redundancy)s") %
+ {'rbridge id': self._svi['rbridge_ids'],
+ 'redundancy': self._svi['redundancy']})
+
+ self._driver = driver.NOSdriver(self._switch['address'],
+ self._switch['username'],
+ self._switch['password'])
+ if self._svi['redundancy']:
+ for rbridge_id in self._svi['rbridge_ids']:
+ LOG.info(_LI("rbridge id %s protocol vrrp enabled"),
+ rbridge_id)
+ self._driver.configure_protocol_vrrp(rbridge_id)
+ self._driver.close_session()
+
+ def _update_ips_for_port(self, context, port):
+ LOG.info(_LI("_update_ips_for_port called()"))
+ port['fixed_ips'] is not attributes.ATTR_NOT_SPECIFIED
+ filter = {'network_id': [port['network_id']]}
+ subnets = self._core_plugin.get_subnets(context, filters=filter)
+ result = self._core_plugin.ipam._generate_ip(context, subnets)
+ LOG.info(_LI("_update_ips_for_port generated ip %s"), result)
+
+ allocated = models_v2.IPAllocation(network_id=port['network_id'],
+ port_id=port['id'],
+ ip_address=result['ip_address'],
+ subnet_id=result['subnet_id'])
+ context.session.add(allocated)
+ return result
+
+ def _invoke_nos_driver_api(self, func_name, router_id,
+ vlan_id=None,
+ gateway_ip_cidr=None,
+ context=None,
+ port=None,
+ added=None,
+ removed=None):
+ LOG.info(_LI("_invoke_nos_driver_api called()"))
+ self._switch
+ priority = 1
+ if self._svi['redundancy'] and\
+ (func_name == 'delete_svi' or
+ func_name == 'create_svi'):
+ vip, net_len = self.net_addr(gateway_ip_cidr)
+ LOG.info(_LI("_invoke_nos_driver_api vip %(vip)s"
+ " net_len %(net_len)s") %
+ {'vip': vip, 'net_len': net_len})
+
+ for rbridge_id in self._svi['rbridge_ids']:
+ if func_name == 'create_router' or\
+ func_name == 'delete_router':
+ self._driver.__getattribute__(func_name)(rbridge_id,
+ str(router_id))
+ elif func_name == 'create_svi':
+ if self._svi['redundancy']:
+ LOG.info(_LI("calling update_ips_for_port"))
+ res = self._update_ips_for_port(context, port)
+ gateway_ip_cidr = res['ip_address'] + '/' + str(net_len)
+ LOG.info(_LI("after _update_ips_for_port generated"
+ " gate_ip_cidr %s"), gateway_ip_cidr)
+
+ LOG.info(_LI("invoke create svi"))
+ self._driver.__getattribute__(func_name)(rbridge_id,
+ vlan_id,
+ gateway_ip_cidr,
+ str(router_id))
+ if self._svi['redundancy']:
+ vrrp_version = self._svi['vrrp_version']
+ vrrp_group_id = self._svi['vrrp_group_id']
+ vrrp_advt_interval =\
+ self._svi['vrrp_advertisement_interval']
+ try:
+ self._driver.configure_vrrp_on_svi(rbridge_id,
+ vlan_id,
+ vrrp_group_id,
+ vrrp_version,
+ vip,
+ vrrp_advt_interval,
+ priority)
+ priority += 1
+ except Exception:
+ self._driver.delete_svi(rbridge_id, vlan_id,
+ gateway_ip_cidr,
+ str(router_id))
+ raise "vrrp configuration failed on NOS"
+
+ elif func_name == 'delete_svi':
+ self._driver.__getattribute__(func_name)(rbridge_id,
+ vlan_id,
+ gateway_ip_cidr,
+ str(router_id))
+ elif func_name == 'update_router':
+ self._driver.__getattribute__(func_name)(rbridge_id,
+ str(router_id),
+ added,
+ removed)
+
+ def create_router(self, context, router):
+ """creates a vrf on NOS device """
+ LOG.debug("RouterMixin.create_router() called, "
+ "router=%s .", router)
+ r = router['router']
+ self._get_tenant_id_for_create(context, r)
+ with context.session.begin(subtransactions=True):
+ new_router = super(BrocadeSVIPlugin, self).create_router(context,
+ router)
+ try:
+ # Router on VDX
+ self._invoke_nos_driver_api("create_router", new_router['id'])
+ except Exception as e:
+ LOG.error(_LE("Failed to create router Reason %s"), str(e))
+ raise e
+ return new_router
+
+ def _validate_routes_nexthop(self, cidrs, ips, routes, nexthop):
+ # lets skip to check connected routes
+ # lets keep it FR
+ if nexthop in ips:
+ raise extraroute.InvalidRoutes(
+ routes=routes,
+ reason=_('the nexthop is used by router'))
+
+ def update_router(self, context, router_id, router):
+ """Update the router with static route"""
+ r = router['router']
+ old_routes, routes_dict = self._get_extra_routes_dict_by_router_id(
+ context, router_id)
+ added, removed = neutron_utils.diff_list_of_dict(old_routes,
+ r['routes'])
+ try:
+ updated_router = super(BrocadeSVIPlugin, self).\
+ update_router(context, router_id, router)
+ if 'routes' in r:
+ self._invoke_nos_driver_api('update_router',
+ router_id,
+ None,
+ None,
+ None,
+ None,
+ added,
+ removed)
+ except Exception as e:
+ LOG.error(_LE("Failed to modify route %s"), str(e))
+ raise e
+ return updated_router
+
+ def delete_router(self, context, router_id):
+ """delete a vrf on NOS device """
+ LOG.debug("RouterMixin.delete_router() called, "
+ "router_id=%s .", router_id)
+ router = super(BrocadeSVIPlugin, self).get_router(context, router_id)
+ router['tenant_id']
+ with context.session.begin(subtransactions=True):
+ super(BrocadeSVIPlugin, self).delete_router(context, router_id)
+ try:
+ self._invoke_nos_driver_api("delete_router", router['id'])
+ except Exception as e:
+ LOG.error(_LE("Failed to delete router Reason %s"), str(e))
+ raise e
+
+ def add_router_interface(self, context, router_id, interface_info):
+ """creates svi on NOS device and assigns ip addres to SVI"""
+ LOG.debug("BrocadeSVIPlugin.add_router_interface called: "
+ "router_id=%(router_id)s "
+ "interface_info=%(interface_info)r",
+ {'router_id': router_id, 'interface_info': interface_info})
+ with context.session.begin(subtransactions=True):
+ info = super(BrocadeSVIPlugin, self).add_router_interface(
+ context, router_id, interface_info)
+ try:
+ port = self._core_plugin._get_port(context, info["port_id"])
+ # shutting down neutron port to allow NOS to do Arp/Routing
+ # Propose to community to allow this to do more gracefully
+ port.update({"admin_state_up": False})
+ interface_info = info
+ subnet = self._core_plugin._get_subnet(context,
+ interface_info["subnet_id"])
+ cidr = subnet["cidr"]
+ net_addr, net_len = self.net_addr(cidr)
+ gateway_ip = subnet["gateway_ip"]
+ network_id = subnet['network_id']
+ tenant_id = subnet['tenant_id']
+ bnet = brocade_db.get_network(context, network_id)
+ vlan_id = bnet['vlan']
+ gateway_ip_cidr = gateway_ip + '/' + str(net_len)
+ LOG.debug("Allocated cidr (%s) from the pool, network_id(%s)"
+ "bnet (%s) vlan (%d) ", gateway_ip_cidr, network_id,
+ bnet, int(vlan_id))
+ port_filters = {'network_id': [network_id],
+ 'device_owner': [DEVICE_OWNER_ROUTER_INTF]}
+ port_count = self._core_plugin.get_ports_count(context,
+ port_filters)
+ LOG.info(_LI("BrocadeSVIPlugin.add_router_interface"
+ " ports_count %d"), port_count)
+ # port count is checked against 2 since the
+ # current port is already added to db
+ if port_count == 2:
+ # This subnet is already part of some router is not
+ # supported
+ # in this version of brocadesvi plugin
+ LOG.error(_LE("BrocadeSVIPlugin:adding redundent router"
+ "interface is not supported"))
+ raise Exception(_("BrocadeSVIPlugin:adding redundent"
+ "router interface is not supported"))
+ # res = self._update_ips_for_port(context, port)
+ # gateway_ip = res['ip_address']
+ # gateway_ip_cidr = gateway_ip +'/'+str(net_len)
+ brocade_db.create_svi(context, router_id, tenant_id, str(vlan_id),
+ True, gateway_ip, str(net_len))
+ self._invoke_nos_driver_api("create_svi", router_id, vlan_id,
+ gateway_ip_cidr, context, port)
+ self._update_firewall(context, vlan_id, tenant_id)
+
+ except Exception:
+ LOG.error(_LE("Failed to create Brocade resources to add router "
+ "interface. info=%(info)s, router_id=%(router_id)s"),
+ {"info": info, "router_id": router_id})
+ with excutils.save_and_reraise_exception():
+ self._invoke_nos_driver_api("delete_svi", router_id,
+ vlan_id, gateway_ip_cidr)
+ with context.session.begin(subtransactions=True):
+ info = super(BrocadeSVIPlugin, self).\
+ remove_router_interface(context,
+ router_id,
+ interface_info)
+
+ return info
+
+ def remove_router_interface(self, context, router_id, interface_info):
+ """Deletes svi from NOS device"""
+ LOG.debug("BrocadeSVIPlugin.remove_router_interface called: "
+ "router_id=%(router_id)s "
+ "interface_info=%(interface_info)r",
+ {'router_id': router_id, 'interface_info': interface_info})
+ with context.session.begin(subtransactions=True):
+ info = super(BrocadeSVIPlugin, self).remove_router_interface(
+ context, router_id, interface_info)
+
+ try:
+ subnet = self._core_plugin._get_subnet(context, info['subnet_id'])
+ cidr = subnet['cidr']
+ net_addr, net_len = self.net_addr(cidr)
+ gateway_ip = subnet['gateway_ip']
+ network_id = subnet['network_id']
+ tenant_id = subnet['tenant_id']
+ bnet = brocade_db.get_network(context, network_id)
+ vlan_id = bnet['vlan']
+ gateway_ip_cidr = gateway_ip + '/' + str(net_len)
+ LOG.debug("remove_router_interface removed cidr (%s)"
+ "from the pool, network_id (%s) bnet (%s) vlan (%d) ",
+ gateway_ip_cidr, network_id, bnet, int(vlan_id))
+ brocade_db.delete_svi(context, router_id, tenant_id, vlan_id,
+ gateway_ip, str(net_len))
+ self._invoke_nos_driver_api("delete_svi", router_id,
+ vlan_id, gateway_ip_cidr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.error(_LE("Failed to remove interface from brocade router"
+ "interface. info=%(info)s,"
+ " router_id=%(router_id)s"),
+ {"info": info, "router_id": router_id})
+
+ def _update_firewall(self, context, svi, tenant_id):
+ """update newly added interface with firewall rules"""
+ fw_plugin = manager.NeutronManager.get_service_plugins().get(
+ plugin_constants.FIREWALL, None)
+
+ if not fw_plugin:
+ LOG.info(_LI('No Firewall plugin registered!!'))
+ return
+ context.tenant_id = tenant_id
+ if hasattr(fw_plugin, 'handle_router_interface_add'):
+ fw_plugin.handle_router_interface_add(context, svi, tenant_id)
+ else:
+ LOG.warning(_LW("Brocade SVI Plugin is used but brocade firewall"
+ " plugin you may want to configure"
+ " brocade firewall plugin"))
+
+ @staticmethod
+ def net_addr(addr):
+ """Get network address prefix and length from a given address."""
+ if addr is None:
+ return (None, None)
+ nw_addr, nw_len = addr.split('/')
+ nw_len = int(nw_len)
+ return nw_addr, nw_len
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/mechanism_brocade.py b/networking_brocade/vdx/non_ampp/ml2driver/mechanism_brocade.py
new file mode 100644
index 0000000..da4ba56
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/mechanism_brocade.py
@@ -0,0 +1,493 @@
+# 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.
+#
+# Shiv Haris (shivharis@hotmail.com)
+
+
+"""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.db import models as brocade_db
+from networking_brocade.vdx.non_ampp.ml2driver.nos import nosdriver as driver
+from networking_brocade.vdx.non_ampp.ml2driver import utils
+from neutron.common import constants as n_const
+from neutron import context as neutron_context
+from neutron.extensions import portbindings
+from neutron.plugins.common import constants as p_const
+from neutron.plugins.ml2 import driver_api as api
+import sys
+try:
+ from oslo_log import log as logging
+except ImportError:
+ from neutron.openstack.common import log as logging
+
+
+LOG = logging.getLogger(__name__)
+MECHANISM_VERSION = 1.0
+
+
+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._device_dict = {}
+ self._bond_mappings = {}
+ self._lacp_ports = {}
+ self.initialize()
+
+ def initialize(self):
+ """Initilize of variables needed by this class."""
+ self.brocade_init()
+
+ def brocade_init(self):
+ """Brocade specific initialization for this class."""
+ utils.register_brocade_credentials()
+ self._switch = utils.get_brocade_credentials()
+ self._fqdn_supported = utils.is_fqdn_supported()
+ self.initialize_vcs = utils.get_vcs_initialize()
+ self._physical_networks = utils.get_physical_networks()
+ self._driver = driver.NOSdriver(self._switch['address'],
+ self._switch['username'],
+ self._switch['password'])
+ try:
+ self._device_dict, self._bond_mappings, self._mtu,\
+ self._native_vlans = utils._parse_connection_info()
+ except Exception as e:
+ LOG.error(_LE("%s"), e)
+ sys.exit(0)
+
+ if self.initialize_vcs:
+ self.configure_vcs()
+ self._driver.close_session()
+
+ def configure_vcs(self):
+ # configure vcs interfaces based on topology
+ if not utils._is_valid_interface(self._device_dict,
+ self._switch, self._driver):
+ sys.exit(0)
+
+ LOG.debug("device dictionary %s", self._device_dict)
+
+ try:
+ if utils._is_lacp_enabled():
+ LOG.debug("LACP enabled")
+ (self._device_dict, self._lacp_ports) =\
+ utils._aggregate_nics_to_support_lacp(self._device_dict,
+ self._bond_mappings)
+ self._driver.configure_l2_and_trunk_mode_for_interface(
+ self._device_dict, self._lacp_ports,
+ self._mtu, self._native_vlans)
+ except Exception:
+ LOG.exception(
+ _LE("Brocade Mechanism: failed to put"
+ " interface l2 or tr mode"))
+ raise Exception(
+ _("Brocade Mechanism: failed to put interface l2 or tr mode"))
+
+ 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 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 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"))
+
+ 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['network_type']
+ network['tenant_id']
+ vlan_id = network['vlan']
+ segments = mech_context.network_segments
+ # currently supports only one segment per network
+ segment = segments[0]
+ physical_network = segment['physical_network']
+
+ try:
+ self._driver.create_network(self._device_dict,
+ physical_network,
+ 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"))
+
+ 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']
+ 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"))
+
+ def delete_network_postcommit(self, mech_context):
+ """Delete network which 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']
+ vlan_id = network['provider:segmentation_id']
+ network['tenant_id']
+ try:
+ self._driver.delete_network(vlan_id)
+ except Exception:
+ LOG.exception(_LE("Brocade NOS driver: failed to delete network"))
+ raise Exception(
+ _("Brocade switch exception, "
+ "delete_network_postcommit failed"))
+
+ 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)."""
+ LOG.debug("create_port_precommit: called")
+ if self.is_flat_network(mech_context.network.network_segments[0]):
+ return
+
+ port = mech_context.current
+ if not self._is_compute_or_dhcp_port(port, mech_context):
+ return
+ if baremetal_util.is_baremetal_deploy(port):
+ LOG.debug("create_port_precommit: baremetal deploy")
+ return
+ context = neutron_context.get_admin_context()
+ self._create_brocade_port(
+ context, port, mech_context.top_bound_segment)
+
+ def create_port_postcommit(self, mech_context):
+ """Associate the assigned MAC address to the portprofile."""
+ LOG.debug("create_port_postcommit(self: called")
+ if self.is_flat_network(mech_context.network.network_segments[0]):
+ return
+
+ port = mech_context.current
+ if not self._is_compute_or_dhcp_port(port, mech_context):
+ return
+ context = mech_context._plugin_context
+ self._create_nos_port(context, port, mech_context.top_bound_segment)
+
+ 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
+ if not self._is_compute_or_dhcp_port(port, mech_context):
+ return
+
+ context = mech_context._plugin_context
+ self._delete_brocade_port(context, port)
+
+ def delete_port_postcommit(self, mech_context):
+ """Dissociate MAC address from the portprofile."""
+ LOG.debug("delete_port_postcommit(self: called")
+ if self.is_flat_network(mech_context.network.network_segments[0]):
+ return
+
+ port = mech_context.current
+ if not self._is_compute_or_dhcp_port(port, mech_context):
+ return
+
+ context = mech_context._plugin_context
+ self._delete_nos_port(context, port, mech_context.top_bound_segment)
+
+ def update_port_precommit(self, mech_context):
+ """updates brocade db if vm is migrating"""
+ if self.is_flat_network(mech_context.network.network_segments[0]):
+ return
+
+ context = mech_context._plugin_context
+ port = mech_context.current
+ LOG.debug("update_port_precommit(self: called")
+ if not self._is_compute_or_dhcp_port(port, mech_context):
+ return
+
+ if self._is_vm_migration(mech_context):
+ # PortContext.current['binding:host_id']: current (new) value
+ port = mech_context.original
+ LOG.debug("update_port_precommit: VM is migrating to"
+ "new host %s(case 1) port['status'] %s",
+ port[portbindings.HOST_ID], port['status'])
+ self._delete_brocade_port(context, port)
+ else:
+ # PortContext.current['binding:host_id']: previous value
+ if mech_context.top_bound_segment and\
+ port['status'] == n_const.PORT_STATUS_BUILD:
+ LOG.debug("update_port_pretcommit: VM is migrating to"
+ "new host %s(case 2)", port[portbindings.HOST_ID])
+ self._create_brocade_port(context, port,
+ mech_context.top_bound_segment)
+
+ def update_port_postcommit(self, mech_context):
+ """updates brocade nos if vm is migrating"""
+ if self.is_flat_network(mech_context.network.network_segments[0]):
+ return
+
+ port = mech_context.current
+ context = mech_context._plugin_context
+ LOG.debug("update_port_postcommit: called")
+ if not self._is_compute_or_dhcp_port(port, mech_context):
+ return
+
+ if self._is_vm_migration(mech_context):
+ # add new entry to switch
+ # PortContext.current['binding:host_id']: current (new) value
+ port = mech_context.original
+ LOG.debug("update_port_precommit: VM is migrating to"
+ "new host %s(case 1) port['status'] %s",
+ port[portbindings.HOST_ID], port['status'])
+ self._delete_nos_port(context, port,
+ mech_context.original_bound_segment)
+ else:
+ # remove previouse port binings
+ # PortContext.current['binding:host_id']: previous value
+ if mech_context.top_bound_segment and\
+ port['status'] == n_const.PORT_STATUS_BUILD:
+ LOG.debug("update_port_postcommit: VM is migrating to"
+ "new host %s(case 2)", port[portbindings.HOST_ID])
+ self._create_nos_port(context, port,
+ mech_context.top_bound_segment)
+
+ 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")
+
+ def _is_vm_migration(self, context):
+ LOG.debug("_is_vm_migration called")
+ return (context.current.get(portbindings.HOST_ID) !=
+ context.original.get(portbindings.HOST_ID))
+
+ def _is_compute_or_dhcp_port(self, port, context):
+ if (("compute" not in port['device_owner']) and
+ ("dhcp" not in port['device_owner'])):
+ # Not a compute port or dhcp , return
+ return False
+ #if not self._is_profile_bound_to_port(port, context):
+ # it is baremetal port
+ # return False
+ return True
+
+ def _is_profile_bound_to_port(self, port, context):
+ profile = context.current.get(portbindings.PROFILE, {})
+ if not profile:
+ LOG.debug("Missing profile in port binding")
+ return False
+ return True
+
+ def _is_dhcp_port(self, port):
+ if("dhcp" in port['device_owner']):
+ # dhcp port, return
+ return True
+ return False
+
+ def _get_vlanid(self, segment):
+ if (segment and segment[api.NETWORK_TYPE] == p_const.TYPE_VLAN):
+ return segment.get(api.SEGMENTATION_ID)
+
+ def _get_physical_interface(self, segment):
+ if (segment and segment[api.NETWORK_TYPE] == p_const.TYPE_VLAN):
+ return segment.get(api.PHYSICAL_NETWORK)
+
+ def _get_hostname(self, port):
+ host = port[portbindings.HOST_ID]
+ LOG.debug("_get_hostname host %s", host)
+ return host if self._fqdn_supported else host.split('.')[0]
+
+ def _get_port_info(self, port, segment):
+ "get vlan id and physical networkkfrom bound segment"
+ if port and segment:
+ vlan_id = self._get_vlanid(segment)
+ hostname = self._get_hostname(port)
+ physical_interface = self._get_physical_interface(segment)
+ LOG.debug("_get_port_info: hostname %s, vlan_id %s,"
+ " physical_interface %s", hostname, str(vlan_id),
+ physical_interface)
+ return hostname, vlan_id, physical_interface
+ return None, None, None
+
+ def _create_brocade_port(self, context, port, segment):
+ port_id = port['id']
+ network_id = port['network_id']
+ tenant_id = port['tenant_id']
+ admin_state_up = port['admin_state_up']
+ hostname, vlan_id, physical_network = self._get_port_info(
+ port, segment)
+ try:
+ brocade_db.create_port(context, port_id, network_id,
+ physical_network, vlan_id, tenant_id,
+ admin_state_up, hostname)
+ except Exception:
+ LOG.exception(_LE("Brocade Mechanism: "
+ "failed to create port in db"))
+ raise Exception(
+ _("Brocade Mechanism: create_port_precommit failed"))
+
+ def _create_nos_port(self, context, port, segment):
+ hostname, vlan_id, physical_network = self._get_port_info(
+ port, segment)
+ if not hostname or not vlan_id:
+ LOG.info(_LI("hostname or vlan id is empty"))
+ return
+ for (speed, name) in self._device_dict[(hostname, physical_network)]:
+ LOG.debug("_create_nos_port:port %s %s vlan %s",
+ speed, name, str(vlan_id))
+ try:
+ if not brocade_db.is_vm_exists_on_host(context,
+ hostname,
+ physical_network,
+ vlan_id):
+ self._driver.add_or_remove_vlan_from_interface(
+ "add", speed, name, vlan_id)
+ else:
+ LOG.debug("_create_nos_port:port is already trunked")
+ except Exception:
+ self._delete_brocade_port(context, port)
+ LOG.exception(_LE("Brocade NOS driver:failed to trunk vlan"))
+ raise Exception(_("Brocade switch exception:"
+ " create_port_postcommit failed"))
+
+ def _delete_brocade_port(self, context, port):
+ try:
+ port_id = port['id']
+ 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_nos_port(self, context, port, segment):
+
+ hostname, vlan_id, physical_network =\
+ self._get_port_info(port, segment)
+ if not hostname or not vlan_id:
+ LOG.info(_LI("hostname or vlan id is empty"))
+ return
+ for (speed, name) in self._device_dict[(hostname, physical_network)]:
+ try:
+ if brocade_db.is_last_vm_on_host(context,
+ hostname,
+ physical_network, vlan_id)\
+ and not self._is_dhcp_port(port):
+
+ self._driver.add_or_remove_vlan_from_interface("remove",
+ speed,
+ name,
+ vlan_id)
+ else:
+ LOG.info(_LI("more vm exist for network on host hence vlan"
+ " is not removed from port"))
+ except Exception:
+ LOG.exception(
+ _LE("Brocade NOS driver: failed to remove vlan from port"))
+ raise Exception(
+ _("Brocade switch exception: delete_port_postcommit"
+ "failed"))
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/nos/__init__.py b/networking_brocade/vdx/non_ampp/ml2driver/nos/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/nos/nctemplates.py b/networking_brocade/vdx/non_ampp/ml2driver/nos/nctemplates.py
new file mode 100644
index 0000000..12fe087
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/nos/nctemplates.py
@@ -0,0 +1,825 @@
+# Copyright (c) 2016 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.
+#
+# Authors:
+# Varma Bhupatiraju (vbhupati@#brocade.com)
+# Shiv Haris (sharis@brocade.com)
+
+
+"""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'"
+)
+
+NOS_VERSION = "./*/{urn:brocade.com:mgmt:brocade-firmware-ext}os-version"
+
+#
+# L2 Forwarding Life-cycle Management Configuration Commands
+#
+
+CREATE_INTERFACE = """
+
+
+
+ {name}
+
+
+
+"""
+
+PORT_CHANNEL_SPEED = """
+
+
+
+ {name}
+ {po_speed}
+
+
+
+"""
+PORT_CHANNEL_LB_MODE = """
+
+
+
+ {name}
+ {po_lb_mode}
+
+
+
+"""
+
+ACTIVATE_INTERFACE = """
+
+
+ <{speed}>
+ {name}
+
+ {speed}>
+
+
+"""
+REMOVE_CHANNEL_GROUP = """
+
+
+ <{speed}>
+ {name}
+
+
+ {speed}>
+
+
+"""
+CONFIGURE_CHANNEL_GROUP = """
+
+
+ <{speed}>
+ {name}
+
+ {port}
+ {po_mode}
+ {po_type}
+
+ {speed}>
+
+
+"""
+CONFIGURE_INTERFACE_SWITCHPORT_V1 = """
+
+
+ <{speed}>
+ {name}
+
+
+
+ {speed}>
+
+
+"""
+REMOVE_INTERFACE_SWITCHPORT_V1 = """
+
+
+ <{speed}>
+ {name}
+
+
+
+ {speed}>
+
+
+"""
+
+REMOVE_PORT_PROFILE_PORT = """
+
+
+ <{speed}>
+ {name}
+
+ {speed}>
+
+
+
+"""
+
+CONFIGURE_MTU_ON_INTERFACE = """
+
+
+ <{speed}>
+ {name}
+ {mtu}
+ {speed}>
+
+
+"""
+
+CONFIGURE_INTERFACE_SWITCHPORT_V2 = """
+
+
+ <{speed}>
+ {name}
+
+
+
+ {speed}>
+
+
+"""
+
+REMOVE_INTERFACE_SWITCHPORT_V2 = """
+
+
+ <{speed}>
+ {name}
+
+
+
+ {speed}>
+
+
+"""
+
+CONFIGURE_INTERFACE_SWITCHPORT_TRUNK = """
+
+
+ <{speed}>
+ {name}
+
+
+ trunk
+
+
+ {speed}>
+
+
+"""
+
+ADD_OR_REMOVE_VLAN_TO_INTERFACE = """
+
+
+ <{speed}>
+ {name}
+
+
+
+
+ <{action}>{vlan_id}{action}>
+
+
+
+
+ {speed}>
+
+
+"""
+
+ALLOW_UNTAG_TRAF_ON_INTERFACE = """
+
+
+ <{speed}>
+ {name}
+
+
+
+
+
+
+
+ {speed}>
+
+
+"""
+
+
+ADD_NATIVE_VLAN_TO_INTERFACE = """
+
+
+ <{speed}>
+ {name}
+
+
+
+ {vlan_id}
+
+
+
+ {speed}>
+
+
+"""
+
+REMOVE_NATIVE_VLAN_FROM_INTERFACE = """
+
+
+ <{speed}>
+ {name}
+
+
+
+
+
+
+
+ {speed}>
+
+
+"""
+# Create VLAN (vlan_id)
+CREATE_VLAN_INTERFACE = """
+
+
+
+
+ {vlan_id}
+
+
+
+
+"""
+
+# Delete VLAN (vlan_id)
+DELETE_VLAN_INTERFACE = """
+
+
+
+
+ {vlan_id}
+
+
+
+
+"""
+
+#
+# L3 Life-cycle Management Configuration Commands
+#
+
+# configure SVI (rbridge_id,vlan_id)
+CONFIGURE_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+
+"""
+# Create IP static routes (rbridge_id,vrf_name,destination_ip,next_hop)
+CONFIGURE_VRF_IP_STATIC_ROUTE = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+
+
+
+ {destination_ip}
+ {next_hop}
+
+
+
+
+
+
+
+
+
+"""
+# Delete IP static routes (rbridge_id,vrf_name,destination_ip,next_hop)
+DELETE_VRF_IP_STATIC_ROUTE = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+
+
+
+ {destination_ip}
+ {next_hop}
+
+
+
+
+
+
+
+
+
+"""
+# Create IP static routes (rbridge_id,destination_ip,next_hop)
+CONFIGURE_IP_STATIC_ROUTE = """
+
+
+ {rbridge_id}
+
+
+
+
+ {destination_ip}
+ {next_hop}
+
+
+
+
+
+
+"""
+# Delete IP static routes (rbridge_id,destination_ip,next_hop)
+DELETE_IP_STATIC_ROUTE = """
+
+
+ {rbridge_id}
+
+
+
+
+ {destination_ip}
+ {next_hop}
+
+
+
+
+
+
+"""
+# Create SVI and assign ippaddres (rbridge_id,vlan_id,ip_address)
+CONFIGURE_SVI_WITH_IP_ADDRESS = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+ {ip_address}
+
+
+
+
+
+
+
+"""
+
+# delete SVI (rbridge_id,vlan_id)
+DELETE_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+
+"""
+
+# Activate SVI (rbridge_id,vlan_id)
+ACTIVATE_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+
+
+"""
+
+# Remove ipaddress from SVI (rbridge_id,vlan_id)
+DECONFIGURE_IP_FROM_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+ {gw_ip}
+
+
+
+
+
+
+
+"""
+
+# create vrf (rbridge_id,vrf_name)
+CREATE_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+"""
+
+
+# delete vrf (rbridge_id,vrf_name)
+DELETE_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+"""
+
+# configure route distinguisher for vrf (rbridge_id,vrf_name, rd)
+CONFIGURE_RD_FOR_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+ {rd}
+
+
+
+"""
+
+# configure address-family for vrf (rbridge_id,vrf_name)
+ADD_ADDRESS_FAMILY_FOR_VRF_V1 = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+ 1200
+
+
+
+
+
+"""
+
+# configure address-family for vrf (rbridge_id,vrf_name)
+ADD_ADDRESS_FAMILY_FOR_VRF = """
+
+
+ {rbridge_id}
+
+ {vrf_name}
+
+
+
+
+
+
+
+
+"""
+
+# Bind vrf to SVI (rbridge_id,vlan_idi, vrf)
+ADD_VRF_TO_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrf_name}
+
+
+
+
+
+"""
+
+# unbind vrf from SVI (rbridge_id,vlan_idi, vrf)
+DELETE_VRF_FROM_SVI = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrf_name}
+
+
+
+
+
+"""
+
+# Acl Policy Life cycle Management
+REMOVE_ACL_POLICY = """
+
+
+
+
+
+ {acl_name}
+
+
+
+
+
+"""
+IP_ACL_RULE_BULKING_START = """
+
+
+
+
+
+ {name}
+
+"""
+IP_ACL_RULE_BULKING_END = """
+
+
+
+
+
+
+"""
+
+
+IP_ACL_RULE = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+SVI_IP_ACL = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+ {name}
+ {direction}
+
+
+
+
+
+
+
+"""
+
+REMOVE_SVI_IP_ACL = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+
+
+ {name}
+ {direction}
+
+
+
+
+
+
+
+"""
+
+# L3 HA management commands
+ENABLE_VRRP = """
+
+
+ {rbridge_id}
+
+
+
+
+
+
+
+"""
+
+CONFIGURE_VRRP_GROUP = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrid}
+ {version}
+
+
+
+
+
+"""
+
+
+CONFIGURE_VRRP_VIP = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrid}
+ {version}
+
+ {vip}
+
+
+
+
+
+
+"""
+
+CONFIGURE_VRRP_PRIORITY = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrid}
+ {version}
+ {priority}
+
+
+
+
+
+"""
+
+CONFIGURE_VRRP_ADVERTISEMENT_INTERVEL = """
+
+
+ {rbridge_id}
+
+
+ {vlan_id}
+
+ {vrid}
+ {version}
+ {advt_int}
+
+
+
+
+
+"""
+
+
+# Constants
+#
+
+# Constants
+#
+# ip acl naming convention
+ROUTER_OBJ_PREFIX = 'openstack-acl-'
+
+# vrf naming convention
+OS_VRF_NAME = "openstack-vrf-{id}"
+
+BAD_ELE = "bad-element"
+IP_ACL_APPLIED = "SSM_DCM_ERR_IP_ACL_APPLIED"
+
+SEQ_ID_EXISTS = "/ip-acl/ip/access-list/extended[name='{name}']/hide-ip-acl-"\
+ "ext/seq [seq-id='{id}']"
+
+IP_ACL_NAME_XPATH_FILTER = "/ip-acl/ip/access-list/extended[name='{name}']/"\
+ "name"
+
+ACL_ON_SVIS_XPATH_FILTER = "/rbridge-id[rbridge-id='{rbridge_id}']/interface/"\
+ "ve/ip-acl-interface/ip/access-group/ip-access-list"
+
+ACL_ON_SVI_XPATH_FILTER = "/rbridge-id[rbridge-id='{rbridge_id}']/interface/"\
+ "ve[name='{svi}']/ip-acl-interface/ip/access-group/"\
+ "ip-access-list"
+
+SVI_STATUS_XPATH_FILTER = "/rbridge-id[rbridge-id='{rbridge_id}']/interface/"\
+ "ve[name='{name}']/shutdown"
+
+INTERFACE_XPATH_FILTER = "/interface/{speed}[name='{name}']/name"
+
+SVI_EXISTS_XPATH_FILTER = "/rbridge-id[rbridge-id='{rbridge_id}']/interface/"\
+ "ve[name='{name}']/name"
+
+INTERFACE_STATUS_XPATH_FILTER = "/interface/{speed}[name='{name}']/shutdown"
+
+INTERFACE_PP_STATUS_XPATH_FILTER = "/interface/{speed}[name='{name}']/*"\
+ "[local-name()='port-profile-port']"
+INTERFACE_CG_STATUS_XPATH_FILTER = "/interface/{speed}[name='{name}']/*"\
+ "[local-name()='channnel-group']"
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/nos/nosdriver.py b/networking_brocade/vdx/non_ampp/ml2driver/nos/nosdriver.py
new file mode 100644
index 0000000..2d307e8
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/nos/nosdriver.py
@@ -0,0 +1,1013 @@
+# 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.
+#
+# Authors:
+# Varma Bhupatiraju (vbhupati@brocade.com)
+# Shiv Haris (shivharis@hotmail.com)
+
+
+"""Brocade NOS Driver implements NETCONF over SSHv2 for
+Neutron network life-cycle management.
+"""
+
+from ncclient import manager
+from ncclient.operations.errors import TimeoutExpiredError
+from ncclient.transport.errors import TransportError
+from networking_brocade._i18n import _
+from networking_brocade._i18n import _LE
+from networking_brocade._i18n import _LI
+from networking_brocade._i18n import _LW
+from networking_brocade.vdx.non_ampp.ml2driver import utils
+from networking_brocade.vdx.non_ampp.ml2driver.nos import(
+ nctemplates as template)
+from neutron.common import exceptions
+from oslo_log import log as logging
+from oslo_utils import excutils
+import six
+import sys
+import time
+
+LOG = logging.getLogger(__name__)
+SSH_PORT = 22
+RETRYABLE_ERRORS = ["NODE_IS_NOT_READY",
+ "CLUSTER_FORMATION_IS_IN_PROGRESS",
+ "NODE_IS_ZEROIZED",
+ "WAVE_FRAMEWORK_STATE_CLUSTER_FORMATION"
+ ]
+
+_RETRIES, _NDELAY, _NBACKOFF = utils.get_retry_args()
+
+
+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
+
+# 5 retries is equal to 8 mins
+
+
+def retry(ExceptionToCheck, tries=_RETRIES, delay=_NDELAY, backoff=_NBACKOFF):
+ """Retry decorator
+ """
+ def deco_retry(f):
+ def f_retry(*args, **kwargs):
+ mtries, mdelay = tries, delay
+ while mtries > 0:
+ try:
+ return f(*args, **kwargs)
+ except ExceptionToCheck as e:
+ LOG.warning(_LW("Retrying in %d seconds..."), mdelay)
+ time.sleep(mdelay)
+ mtries -= 1
+ mdelay *= backoff
+ lastException = e
+ raise lastException
+ return f_retry # true decorator
+ return deco_retry
+
+
+class RetryableException(exceptions.NeutronException):
+ message = _("Transient errors Try again after some time."
+ " Reason: %(exc)s.")
+
+
+class NOSdriver(object):
+
+ """NOS NETCONF interface driver for Neutron network.
+ Handles life-cycle management of Neutron network
+ """
+
+ def __init__(self, host, username, password):
+ self.mgr = None
+ self.host = host
+ self.username = username
+ self.password = password
+ self.osversion = self.get_nos_version().split('.', 2)
+
+ def _set_default_timeout_ncclient(self):
+ mgr = self.connect(self.host, self.username, self.password)
+ mgr.timeout = 30
+
+ @retry(RetryableException)
+ def _edit_config(self, target, config, timeout=30):
+ """Modify switch config for a target config type."""
+ try:
+ mgr = self.connect(self.host, self.username, self.password)
+ if timeout != 30:
+ mgr.timeout = timeout
+ mgr.edit_config(target=target, config=config)
+ except TransportError as e:
+ self.close_session()
+ LOG.warning(_LW("_edit_config()TransportErrorFailed"
+ "for Reason %(exc)s"), {'exc': e})
+ raise RetryableException(exc=e)
+ except TimeoutExpiredError as e:
+ LOG.warning(_LW("_edit_config(TimeoutExpiredError)"
+ "for Reason %(exc)s"), {'exc': e})
+ raise RetryableException(exc=e)
+ except Exception as e:
+ LOG.warning(_LW("_edit_config(CLUSTER ERRORS)"
+ "for Reason %(exc)s"), {'exc': e})
+ for exc_str in RETRYABLE_ERRORS:
+ if exc_str in str(e):
+ raise RetryableException(exc=e)
+ raise e
+ finally:
+ if timeout != 30:
+ self._set_default_timeout_ncclient()
+
+ @retry(RetryableException)
+ def _get_config(self, source, filterstr):
+ """get switch config for a source config type."""
+ try:
+ mgr = self.connect(self.host, self.username, self.password)
+ response = mgr.get_config(source=source,
+ filter=('xpath', filterstr)).data_xml
+ return response
+ except TransportError as e:
+ LOG.warning(_LW("_edit_config()TransportErrorFailed"
+ "for Reason %s"), unicode(str(e)))
+ self.close_session()
+ raise RetryableException(exc=e)
+ except TimeoutExpiredError as e:
+ LOG.warning(_LW("_edit_config(TimeoutExpiredError)"
+ "for Reason %s"), unicode(str(e)))
+ raise RetryableException(exc=e)
+ except Exception as e:
+ LOG.warning(_LW("_edit_config(CLUSTER ERRORS)"
+ "for Reason %s"), unicode(str(e)))
+ for exc_str in RETRYABLE_ERRORS:
+ if exc_str in str(e):
+ raise RetryableException(exc=e)
+ raise e
+
+ 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 and self.mgr.connected:
+ self.mgr.close_session()
+ self.mgr = None
+
+ def get_nos_version(self):
+ """Show version of NOS."""
+ try:
+ mgr = self.connect(self.host, self.username, self.password)
+ return self.nos_version_request(mgr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ self.close_session()
+
+ def create_network(self, topology,
+ physical_network, net_id):
+ """Creates a new virtual network."""
+ try:
+ self.create_vlan_interface(net_id)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def delete_network(self, net_id):
+ """Deletes a virtual network."""
+ try:
+ self.delete_vlan_interface(net_id)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def configure_l2_and_trunk_mode_for_interface(self, devices, lacp,
+ mtu, native_vlans):
+ """configure interface in switchport and trunk mode."""
+ for key in lacp.keys():
+ lacp_args = utils.get_lacp_args()
+ create_interface = template.CREATE_INTERFACE.format(name=key)
+ configure_lb_mode = template.PORT_CHANNEL_LB_MODE.format(
+ name=key,
+ po_lb_mode=lacp_args['po_lb_mode'])
+ configure_po_speed = template.PORT_CHANNEL_SPEED.format(
+ name=key,
+ po_speed=lacp_args['po_speed'])
+ if not self.is_interface_id_exists('port-channel', key):
+ self._edit_config('running', create_interface)
+ self._edit_config('running', configure_lb_mode)
+ self._edit_config('running', configure_po_speed)
+
+ for (speed, interface_name) in lacp[key]:
+ self.remove_l2_mode_for_interface(speed, interface_name,
+ lacp_args['remove_ch_grp'])
+
+ for (speed, interface_name) in lacp[key]:
+ confstr_channel_group = template.\
+ CONFIGURE_CHANNEL_GROUP.format(
+ speed=speed, name=interface_name,
+ port=key, po_mode=lacp_args[
+ 'po_mode'],
+ po_type=lacp_args['po_type'])
+ self._edit_config('running', confstr_channel_group)
+ self.activate_interface(speed, interface_name)
+
+ for key in devices.keys():
+ for (interface_speed, interface_name) in devices[key]:
+
+ confstr_trunk = template.CONFIGURE_INTERFACE_SWITCHPORT_TRUNK.\
+ format(speed=interface_speed,
+ name=interface_name)
+ try:
+ if not self.is_interface_id_exists(interface_speed,
+ interface_name):
+ LOG.error(_LE("topology incorrect incorrect"
+ " VDX interface"
+ "%(interface_speed)s,"
+ "%(interface_name)s")
+ % {'interface_speed': interface_speed,
+ 'interface_name': interface_name})
+ sys.exit(0)
+ self.configure_l2_mode_for_interface(interface_speed,
+ interface_name)
+ self.configure_interface_in_trunk_mode(confstr_trunk)
+ self.activate_interface(interface_speed, interface_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE(
+ "VDX interfaces may not"
+ " be in proper "
+ "mode configure switchport mode"))
+
+ for (speed, name), mtu in six.iteritems(mtu):
+ self.configure_mtu_on_interface(speed, name, mtu)
+
+ for (speed, name), vlan_id in six.iteritems(native_vlans):
+ self.create_vlan_interface(vlan_id)
+ self.configure_native_vlan_on_interface(speed, name, vlan_id)
+
+ def configure_interface_in_trunk_mode(self, confstr_trunk):
+ self._edit_config('running', confstr_trunk)
+
+ def activate_interface(self, interface_speed, interface_name):
+ """Activate physical interface """
+ if not self.is_interface_shutdown(interface_speed, interface_name):
+ return
+ confstr_activate = template.ACTIVATE_INTERFACE.format(
+ speed=interface_speed, name=interface_name)
+ try:
+ self._edit_config('running', confstr_activate)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("interface already in active state"))
+ ctxt.reraise = False
+
+ def remove_l2_mode_for_interface(self, interface_speed,
+ interface_name, remove_ch_grp=False):
+ """Configures given interface in L2 mode"""
+ version = self.osversion
+ if int(version[0]) >= 5 or (int(version[0]) >= 4 and
+ int(version[1]) >= 1):
+ confstr = template.REMOVE_INTERFACE_SWITCHPORT_V1.format(
+ speed=interface_speed, name=interface_name)
+ else:
+ confstr = template.REMOVE_INTERFACE_SWITCHPORT_V2.format(
+ speed=interface_speed, name=interface_name)
+ confstr_rm_cg = template.REMOVE_CHANNEL_GROUP.format(
+ speed=interface_speed, name=interface_name)
+ try:
+ try:
+ self._edit_config('running', confstr)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ ctxt.reraise = False
+ if remove_ch_grp:
+ self._edit_config('running', confstr_rm_cg)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ ctxt.reraise = False
+
+ def configure_l2_mode_for_interface(self, interface_speed,
+ interface_name):
+ """Configures given interface in L2 mode"""
+ if self.is_interface_in_port_profile_mode(interface_speed,
+ interface_name):
+ try:
+ self.set_interface_to_accept_l2_mode(interface_speed,
+ interface_name)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("interface already in active state"))
+ ctxt.reraise = False
+
+ try:
+ if (interface_speed != 'port-channel'):
+ confstr = template.REMOVE_CHANNEL_GROUP.format(
+ speed=interface_speed, name=interface_name)
+ self._edit_config('running', confstr)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("exception cg removing"))
+ ctxt.reraise = False
+
+ try:
+ version = self.osversion
+ if int(version[0]) >= 5 or (int(version[0]) >= 4 and
+ int(version[1]) >= 1):
+ confstr = template.CONFIGURE_INTERFACE_SWITCHPORT_V1.format(
+ speed=interface_speed, name=interface_name)
+ else:
+ confstr = template.CONFIGURE_INTERFACE_SWITCHPORT_V2.format(
+ speed=interface_speed, name=interface_name)
+ self._edit_config('running', confstr)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW(
+ "interface not accepting switching please check"
+ "innterface status"))
+
+ def add_or_remove_vlan_from_interface(self, action, interface_speed,
+ interface_name, vlan_id):
+ """add or remove vlan on interface"""
+
+ confstr = template.ADD_OR_REMOVE_VLAN_TO_INTERFACE.format(
+ speed=interface_speed, name=interface_name,
+ action=action, vlan_id=vlan_id)
+ try:
+ self._edit_config('running', confstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def create_svi(self, rbridge_id, vlan_id,
+ ip_address, router_id):
+ """create svi on configured rbridge-id"""
+ try:
+ self.configure_svi(rbridge_id, vlan_id)
+ self.bind_vrf_to_svi(rbridge_id, vlan_id, router_id)
+ self.configure_svi_with_ip_address(rbridge_id, vlan_id, ip_address)
+ self.activate_svi(rbridge_id, vlan_id)
+ except Exception as ex:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error: %s"), ex)
+ self.delete_svi(rbridge_id, vlan_id, ip_address, router_id)
+
+ def delete_svi(self, rbridge_id, vlan_id,
+ gw_ip, router_id):
+ """delete svi from configured rbridge-id"""
+ try:
+ if self.is_svi_exists(rbridge_id, vlan_id):
+ self.remove_svi(rbridge_id, vlan_id)
+ except Exception as ex:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error: %s"), ex)
+
+ def create_router(self, rbridge_id, router_id):
+ """create vrf NOS"""
+ if not utils.is_vrf_required():
+ LOG.warning(_LW("not requested to created vrf there will"
+ "no L5 traffic isolation and no overlapping IP"
+ "supported"))
+ return
+ vrf_name = template.OS_VRF_NAME.format(id=router_id)
+ vrf_name = vrf_name[:32]
+ # 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]
+ try:
+ self.create_vrf(rbridge_id, vrf_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ try:
+ self.configure_rd_for_vrf(rbridge_id, vrf_name, rd)
+ self.configure_address_family_for_vrf(rbridge_id, vrf_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def delete_router(self, rbridge_id, router_id):
+ """create vrf NOS"""
+ if not utils.is_vrf_required():
+ return
+ vrf_name = template.OS_VRF_NAME.format(id=router_id)
+ vrf_name = vrf_name[:32]
+ try:
+ self.delete_vrf(rbridge_id, vrf_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def update_router(self, rbridge_id, router_id, added, removed):
+ """update router"""
+ LOG.info(_LI("Inside update_router before sending to switch"))
+ if added is None:
+ LOG.info(_LI("Added is None"))
+
+ if removed is None:
+ LOG.info(_LI("Removed is None"))
+
+ if not utils.is_vrf_required():
+ """ Configure the static route at the rbridge mode"""
+ try:
+ if added is not None:
+ LOG.info(_LI("Adding new route"))
+ for route in added:
+ self.configure_static_route(rbridge_id,
+ route['destination'],
+ route['nexthop'])
+ if removed is not None:
+ LOG.info(_LI("Deleting new route"))
+ for route in removed:
+ self.delete_static_route(rbridge_id,
+ route['destination'],
+ route['nexthop'])
+ except Exception as e:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(
+ _LE("Failed to create static route %s"), str(e))
+ else:
+ """ config the static route in for the VRF"""
+ vrf_name = template.OS_VRF_NAME.format(id=router_id)
+ vrf_name = vrf_name[:32]
+ try:
+ if added is not None:
+ LOG.info(_LI("Adding new route with VRF %s"), vrf_name)
+ for route in added:
+ self.configure_vrf_static_route(rbridge_id,
+ vrf_name,
+ route['destination'],
+ route['nexthop'])
+ if removed is not None:
+ LOG.info(_LI("Deleting new route from vrf %s"), vrf_name)
+ for route in removed:
+ self.delete_vrf_static_route(rbridge_id,
+ vrf_name,
+ route['destination'],
+ route['nexthop'])
+ except Exception as e:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(
+ _LE("Failed to create static route %s"), str(e))
+
+ def bind_vrf_to_svi(self, rbridge_id,
+ vlan_id, router_id):
+ """binds vrf on svi"""
+ if not utils.is_vrf_required():
+ return
+ vrf_name = template.OS_VRF_NAME.format(id=router_id)
+ vrf_name = vrf_name[:32]
+ try:
+ self.add_vrf_to_svi(rbridge_id, vlan_id, vrf_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def unbind_vrf_to_svi(self, rbridge_id,
+ vlan_id, router_id):
+ """binds vrf on svi"""
+ if not utils.is_vrf_required():
+ return
+ vrf_name = template.OS_VRF_NAME.format(id=router_id)
+ vrf_name = vrf_name[:32]
+ try:
+ self.delete_vrf_from_svi(rbridge_id, vlan_id, vrf_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+# L3 HA Lifecycle
+ def configure_protocol_vrrp(self, rbridge_id):
+ """enable protocol vrrp """
+ try:
+ self.enable_protocol_vrrp(rbridge_id)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def configure_vrrp_group(self, rbridge_id,
+ vlan_id, vrid, version):
+ """config vrrp virtual ip on svi"""
+ try:
+ self.create_vrrp_group(rbridge_id, vlan_id, vrid, version)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def configure_vrrp_on_svi(self, rbridge_id, vlan_id, vrid,
+ version, vip, advt_int,
+ priority=100):
+ """config vrrp virtual ip on svi"""
+ try:
+ self.create_vrrp_group(rbridge_id, vlan_id, vrid, version)
+ self.configure_vrrp_priority(rbridge_id, vlan_id, vrid,
+ version, priority)
+ self.create_vrrp_virtual_ip(
+ rbridge_id, vlan_id, vrid, version, vip)
+ self.configure_vrrp_advt_intervel(rbridge_id, vlan_id, vrid,
+ version, advt_int)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+# Acl Life Cycle Management
+ def create_policy(self, policy_name):
+ """Remove Acl Policy From VDX"""
+ try:
+ self.create_acl(policy_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def create_acl_rule(self, acl):
+ """Remove Acl Policy From VDX"""
+ try:
+ self.create_rule(acl)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def delete_policy(self, rbridge_id, policy_name):
+ """Remove Acl Policy From VDX"""
+ try:
+ self.delete_acl(rbridge_id, policy_name)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def configure_policy_on_interface(self,
+ rbridge_id, vlan_id, name, direction):
+ """provision Acl Policy on VE"""
+ try:
+ self.configure_acl_on_svi(rbridge_id, vlan_id, name, direction)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def remove_policy_on_interface(self,
+ rbridge_id, vlan_id, name, direction):
+ """provision Acl Policy on VE"""
+ try:
+ self.remove_acl_on_svi(rbridge_id, vlan_id, name, direction)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ def create_vlan_interface(self, vlan_id):
+ """Configures a VLAN interface."""
+
+ confstr = template.CREATE_VLAN_INTERFACE.format(vlan_id=vlan_id)
+ self._edit_config('running', confstr)
+
+ def delete_vlan_interface(self, vlan_id):
+ """Deletes a VLAN interface."""
+
+ confstr = template.DELETE_VLAN_INTERFACE.format(vlan_id=vlan_id)
+ self._edit_config('running', confstr)
+
+ def create_vrf(self, rbridge_id, vrf_name):
+ """create vrf on rbridge."""
+
+ confstr = template.CREATE_VRF.format(rbridge_id=rbridge_id,
+ vrf_name=vrf_name)
+ self._edit_config('running', confstr)
+
+ def delete_vrf(self, rbridge_id, vrf_name):
+ """delete vrf on rbridge."""
+
+ confstr = template.DELETE_VRF.format(rbridge_id=rbridge_id,
+ vrf_name=vrf_name)
+ self._edit_config('running', confstr)
+
+ def configure_rd_for_vrf(self, 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)
+ self._edit_config('running', confstr)
+
+ def configure_address_family_for_vrf_v1(self, 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)
+ self._edit_config('running', confstr)
+
+ def configure_address_family_for_vrf(self, 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)
+ self._edit_config('running', confstr)
+
+ def configure_svi(self, rbridge_id, vlan_id):
+ """configure SVI with ip address on rbridge."""
+ template.CONFIGURE_SVI.format(
+ rbridge_id=rbridge_id, vlan_id=vlan_id)
+
+ def configure_svi_with_ip_address(self, 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)
+ self._edit_config('running', confstr)
+
+ def activate_svi(self, rbridge_id, vlan_id):
+ """configure SVI with ip address on rbridge."""
+ if not self.is_svi_shutdown(rbridge_id, vlan_id):
+ return
+ confstr = template.ACTIVATE_SVI.format(rbridge_id=rbridge_id,
+ vlan_id=vlan_id)
+ self._edit_config('running', confstr)
+
+ def add_vrf_to_svi(self, 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)
+ self._edit_config('running', confstr)
+
+ def delete_vrf_from_svi(self, 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)
+ self._edit_config('running', confstr)
+
+ def remove_svi(self, rbridge_id, vlan_id):
+ """delete vrf from svi on rbridge."""
+ confstr = template.DELETE_SVI.format(rbridge_id=rbridge_id,
+ vlan_id=vlan_id)
+ self._edit_config('running', confstr)
+
+ def enable_protocol_vrrp(self, rbridge_id):
+ """enable protocol vrrp on given rbridge."""
+ confstr = template.ENABLE_VRRP.format(rbridge_id=rbridge_id)
+ self._edit_config('running', confstr)
+
+ def create_vrrp_group(self, rbridge_id, vlan_id, vrid, version):
+ """configure vrrp virtual ip on svi"""
+ confstr = template.CONFIGURE_VRRP_GROUP.format(rbridge_id=rbridge_id,
+ vlan_id=vlan_id,
+ vrid=vrid,
+ version=version)
+ confstr = self.strip_vrrp_version(confstr, version)
+ self._edit_config('running', confstr)
+
+ def create_vrrp_virtual_ip(self, rbridge_id, vlan_id, vrid, version, vip):
+ """configure vrrp virtual ip on svi"""
+ confstr = template.CONFIGURE_VRRP_VIP.format(rbridge_id=rbridge_id,
+ vlan_id=vlan_id,
+ vrid=vrid,
+ version=version,
+ vip=vip)
+ confstr = self.strip_vrrp_version(confstr, version)
+ self._edit_config('running', confstr)
+
+ def configure_vrrp_priority(self, rbridge_id, vlan_id,
+ vrid, version, priority=100):
+ """configure vrrp virtual ip on svi"""
+ confstr = template.CONFIGURE_VRRP_PRIORITY.format(
+ rbridge_id=rbridge_id, vlan_id=vlan_id, vrid=vrid,
+ version=version, priority=priority)
+ confstr = self.strip_vrrp_version(confstr, version)
+ self._edit_config('running', confstr)
+
+ def configure_vrrp_advt_intervel(self, rbridge_id, vlan_id, vrid, version,
+ advt_intervel):
+ """configure vrrp virtual ip on svi"""
+ confstr = template.CONFIGURE_VRRP_ADVERTISEMENT_INTERVEL.format(
+ rbridge_id=rbridge_id, vlan_id=vlan_id, vrid=vrid,
+ version=version, advt_int=advt_intervel)
+ confstr = self.strip_vrrp_version(confstr, version)
+ self._edit_config('running', confstr)
+
+ def delete_vrf_static_route(self, rbridge_id, vrf_name, dest_ip, next_hop):
+ configure_static_route = template.\
+ DELETE_VRF_IP_STATIC_ROUTE.\
+ format(rbridge_id=rbridge_id,
+ vrf_name=vrf_name,
+ destination_ip=dest_ip,
+ next_hop=next_hop)
+ try:
+ self._edit_config('running', configure_static_route)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW(
+ "Configuration of static route failed for vrf %s"),
+ vrf_name)
+ ctxt.reraise = False
+
+ def configure_vrf_static_route(
+ self, rbridge_id, vrf_name, dest_ip, next_hop):
+ configure_static_route = template.\
+ CONFIGURE_VRF_IP_STATIC_ROUTE.\
+ format(rbridge_id=rbridge_id,
+ vrf_name=vrf_name,
+ destination_ip=dest_ip,
+ next_hop=next_hop)
+ try:
+ self._edit_config('running', configure_static_route)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(
+ _LW("Configuration of static route failed for vrf %s"),
+ vrf_name)
+ ctxt.reraise = False
+
+ def delete_static_route(self, rbridge_id, dest_ip, next_hop):
+ configure_static_route = template.\
+ DELETE_IP_STATIC_ROUTE.\
+ format(rbridge_id=rbridge_id,
+ destination_ip=dest_ip,
+ next_hop=next_hop)
+ try:
+ self._edit_config('running', configure_static_route)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("Configuration of static route failed"))
+ ctxt.reraise = False
+
+ def configure_static_route(self, rbridge_id, dest_ip, next_hop):
+ configure_static_route = template.\
+ CONFIGURE_IP_STATIC_ROUTE.\
+ format(rbridge_id=rbridge_id,
+ destination_ip=dest_ip,
+ next_hop=next_hop)
+ try:
+ self._edit_config('running', configure_static_route)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("Configuration of static route failed"))
+ ctxt.reraise = False
+
+ def strip_vrrp_version(self, confstr, version):
+ nos_version = self.osversion
+ if (int(nos_version[0]) >= 5):
+ return confstr
+ vrrp_version = '{0}'.format(version)
+ confstr = confstr.replace(vrrp_version, '')
+ return confstr
+
+ def create_rule(self, confstr):
+ """delete Acl Policy from VDX"""
+ self._edit_config('running', confstr, timeout=2000)
+
+ def create_acl(self, policy_name):
+ """delete Acl Policy from VDX"""
+ confstr = template.CREATE_ACL_POLICY.format(acl_name=policy_name)
+ self._edit_config('running', confstr)
+
+ def delete_acl(self, rbridge_id, policy_name):
+ """delete Acl Policy from VDX"""
+ confstr = template.REMOVE_ACL_POLICY.format(acl_name=policy_name)
+ if self.is_ip_acl_exists(policy_name) and \
+ not self.is_ip_acl_applied_on_any_svi(rbridge_id,
+ policy_name):
+ self._edit_config('running', confstr)
+
+ def configure_acl_on_svi(self, rbridge_id, vlan_id, name, direction):
+ """delete Acl Policy from VDX"""
+ confstr = template.SVI_IP_ACL.format(rbridge_id=rbridge_id,
+ vlan_id=vlan_id,
+ name=name,
+ direction=direction)
+ if self.is_ip_acl_exists(name):
+ self._edit_config('running', confstr)
+
+ def configure_native_vlan_on_interface(self, speed, name, vlan_id):
+ """configure native vlan on interface"""
+ confstr1 = template.ALLOW_UNTAG_TRAF_ON_INTERFACE.format(speed=speed,
+ name=name)
+ confstr2 = template.ADD_NATIVE_VLAN_TO_INTERFACE.format(
+ speed=speed,
+ name=name,
+ vlan_id=vlan_id)
+ confstr_trunk = template.CONFIGURE_INTERFACE_SWITCHPORT_TRUNK.format(
+ speed=speed, name=name)
+ self.configure_l2_mode_for_interface(speed, name)
+ self.configure_interface_in_trunk_mode(confstr_trunk)
+ self.activate_interface(speed, name)
+ try:
+ self._edit_config('running', confstr1)
+ except Exception:
+ LOG.warning(_LW("interface ready to accept untagged traffic"))
+ try:
+ self._edit_config('running', confstr2)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("Error configuring native vlan"
+ " on interface {}"))
+ ctxt.reraise = False
+
+ def remove_native_vlan_from_interface(self, speed, name):
+ """configure native vlan on interface"""
+ confstr = template.REMOVE_NATIVE_VLAN_FROM_INTERFACE.format(
+ speed=speed, name=name)
+ try:
+ self._edit_config('running', confstr)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("Error remove native vlan on interface {}"))
+ ctxt.reraise = False
+
+ def configure_mtu_on_interface(self, speed, name, mtu):
+ """native vlan on interfacew"""
+ confstr = template.CONFIGURE_MTU_ON_INTERFACE.format(
+ speed=speed,
+ name=name,
+ mtu=mtu)
+ try:
+ self._edit_config('running', confstr)
+ except Exception:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.warning(_LW("Error configuring Mtu on interface {}"))
+ ctxt.reraise = False
+
+ def remove_acl_on_svi(self, rbridge_id, vlan_id, name, direction):
+ """delete Acl Policy from VDX"""
+ confstr = template.REMOVE_SVI_IP_ACL.format(rbridge_id=rbridge_id,
+ vlan_id=vlan_id,
+ name=name,
+ direction=direction)
+ if self.is_ip_acl_applied_on_svi(rbridge_id, vlan_id, name):
+ self._edit_config('running', confstr)
+
+ def is_ip_acl_exists(self, name):
+ """checks if ip Acl exists on VDX box"""
+ filterstr = template.IP_ACL_NAME_XPATH_FILTER.format(name=name)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if name in response:
+ return True
+ return False
+
+ def is_ip_acl_applied_on_any_svi(self, rbridge_id, name):
+ """checks if ip acl is applied on any of svi interface"""
+
+ filterstr = template.ACL_ON_SVIS_XPATH_FILTER.format(
+ rbridge_id=rbridge_id)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if name in response:
+ return True
+ return False
+
+ def is_ip_acl_applied_on_svi(self, rbridge_id, svi, name):
+ """checks if ip acl is applied on given of svi interface"""
+ filterstr = template.ACL_ON_SVI_XPATH_FILTER.format(
+ rbridge_id=rbridge_id, svi=svi)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if name in response:
+ return True
+ return False
+
+ def is_sequence_id_exists(self, name, seq_id):
+ """checks if sequence id configured for given Acl"""
+ filterstr = template.SEQ_ID_EXISTS.format(name=name, id=seq_id)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+
+ if name in response:
+ return True
+ return False
+
+ def is_interface_id_exists(self, speed, name):
+ """checks if given interface is present"""
+ filterstr = template.INTERFACE_XPATH_FILTER.format(speed=speed,
+ name=name)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if name in response:
+ return True
+ return False
+
+ def is_svi_exists(self, rbridge_id, name):
+ """checks if given interface is present"""
+ filterstr = template.SVI_EXISTS_XPATH_FILTER.format(
+ rbridge_id=rbridge_id, name=name)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if name in response:
+ return True
+
+ def is_svi_shutdown(self, rbridge_id, name):
+ """checks if given interface is active"""
+ filterstr = template.SVI_STATUS_XPATH_FILTER.format(
+ rbridge_id=rbridge_id, name=name)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if "shutdown" in response:
+ return True
+ return False
+
+ def is_interface_shutdown(self, speed, name):
+ """checks if given interface is active"""
+ filterstr = template.INTERFACE_STATUS_XPATH_FILTER.format(
+ speed=speed, name=name)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if "shutdown" in response:
+ return True
+ return False
+
+ def is_interface_in_channel_group_mode(self, speed, name):
+ """checks if given interface is active"""
+ filterstr = template.INTERFACE_CG_STATUS_XPATH_FILTER.format(
+ speed=speed, name=name)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if "channel-group" in response:
+ return True
+ return False
+
+ def is_interface_in_port_profile_mode(self, speed, name):
+ """checks if given interface is active"""
+ filterstr = template.INTERFACE_PP_STATUS_XPATH_FILTER.format(
+ speed=speed, name=name)
+ try:
+ response = self._get_config('running', filterstr)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_LE("NETCONF error"))
+ if "port-profile-port" in response:
+ return True
+ return False
+
+ def set_interface_to_accept_l2_mode(self, speed, name):
+
+ confstr = template.REMOVE_PORT_PROFILE_PORT.format(speed=speed,
+ name=name)
+ self._edit_config('running', confstr)
+
+ @retry(RetryableException)
+ def nos_version_request(self, mgr):
+ """Get firmware information using NETCONF rpc."""
+ # reply = mgr.dispatch(template.SHOW_FIRMWARE_VERSION, None, None)
+ # LOG.info(_LI("msg {}".format(reply)))
+ # et = ElementTree.fromstring(str(reply))
+ # return et.find(template.NOS_VERSION).text
+ return "7.0.0"
diff --git a/networking_brocade/vdx/non_ampp/ml2driver/utils.py b/networking_brocade/vdx/non_ampp/ml2driver/utils.py
new file mode 100644
index 0000000..8e59f09
--- /dev/null
+++ b/networking_brocade/vdx/non_ampp/ml2driver/utils.py
@@ -0,0 +1,615 @@
+# 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.
+#
+# Shiv Haris (shivharis@hotmail.com)
+from lxml import etree
+from networking_brocade._i18n import _
+from networking_brocade._i18n import _LE
+from networking_brocade._i18n import _LI
+from networking_brocade.vdx.non_ampp.ml2driver.nos import(
+ nctemplates as template)
+from oslo_config import cfg
+from oslo_log import log as logging
+from six import moves
+import sys
+# we will not use this for now
+wellknown_dscp = {'af11': 11, 'af12': 12, 'af13': 14,
+ 'af21': 18, 'af22': 20, 'af23': 22,
+ 'af31': 26, 'af32': 28, 'af33': 30,
+ 'af41': 38, 'af42': 36, 'af43': 38,
+ 'cs1': 8, 'cs2': 16, 'cs3': 24,
+ 'cs4': 32, 'cs5': 40, 'cs6': 48,
+ 'cs7': 56, 'default': 0, 'ef': 46}
+
+LOG = logging.getLogger(__name__)
+OBJ_PREFIX_LEN = 8
+
+DEFAULT_TOPOLOGY = []
+DEFAULT_MTU = []
+DEFAULT_NATIVE_VLAN = []
+DEFAULT_RBRIDGE_RANGE = []
+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.BoolOpt('initialize_vcs', default='True',
+ help=_('initialize vcs')),
+ cfg.StrOpt('ostype', default='NOS',
+ help=_('OS Type of the switch')),
+ # cfg.StrOpt('osversion', default='autodetect',
+ # help=_('OS Version number')),
+ cfg.IntOpt('nretries', default=5,
+ help=_(
+ 'Number of retries when retieable'
+ 'exception occurs')),
+ cfg.IntOpt('ndelay', default=10,
+ help=_('Number of seconds to sleep')),
+ cfg.IntOpt('nbackoff', default=2,
+ help=_('backoff interval')),
+ cfg.BoolOpt('fqdn', default=False,
+ help=_(
+ 'Fully qualified Domain names will be used')),
+ cfg.BoolOpt('lacp_enabled', default=False,
+ help=_('lacp enabled')),
+ cfg.BoolOpt('remove_ch_grp', default=False,
+ help=_(
+ 'initialization stage remove channel group')),
+ cfg.StrOpt('port_channels', default='1024:1152',
+ help=_('Allowed port channel number for openstack')),
+ cfg.StrOpt('po_speed', default='10000',
+ help=_('port channel speed')),
+ cfg.StrOpt('port_channel_mode', default='on',
+ help=_('port-channel mode')),
+ cfg.StrOpt('port_channel_type', default='type',
+ help=_('port-channel type')),
+ cfg.StrOpt('port_channel_lb_mode',
+ default='src-dst-ip-mac-vid-port',
+ help=_('port-channel LB mode')),
+ ]
+
+TOPOLOGY_OPTS = [cfg.ListOpt('connections', default=DEFAULT_TOPOLOGY,
+ help=_('topology info :'
+ ':'
+ ':')),
+ cfg.ListOpt('bond_mappings', default=DEFAULT_TOPOLOGY,
+ help=_('bond mapping to port-channel'
+ '::'
+ '')),
+ cfg.ListOpt('mtu', default=DEFAULT_MTU,
+ help=_('MTU options to interface'
+ '::')),
+ cfg.ListOpt('native_vlans', default=DEFAULT_NATIVE_VLAN,
+ help=_('native for interface'
+ '::'))
+ ]
+
+RBRIDGE_OPTS = [cfg.ListOpt('rbridge_ids', default=DEFAULT_RBRIDGE_RANGE,
+ help=_('rbridges range 1,2,3')),
+ cfg.StrOpt('redundancy', default='disabled',
+ help=_('enable/disable L3 redundancy')),
+ cfg.BoolOpt('is_vrf_required', default=True,
+ help=_('VRFs will be created if True')),
+ cfg.StrOpt('vrrp_version', default='2',
+ help=_('vrrp version to be used')),
+ cfg.StrOpt('vrrp_group_id', default='100',
+ help=_('vrrp group to be used')),
+ cfg.IntOpt('vrrp_advertisement_interval', default=1,
+ help=_('vrrp advertisement interval'))
+ ]
+
+FWAAS = [cfg.StrOpt('seq_ids', default='1:50000',
+ help=_('seq ids to be used in acl rule')),
+ cfg.StrOpt('direction', default='both',
+ help=_('direction for acl to be applied')),
+ cfg.BoolOpt('count', default=False,
+ help=_('count the number of times acls are hit')),
+ cfg.BoolOpt('log', default=False,
+ help=_('count the number of times acls are hit')),
+ cfg.StrOpt('acl_file', default='/etc/neutron/acl.json',
+ help=_('pre acls and post acls on svi'))
+ ]
+
+
+def register_brocade_credentials():
+ cfg.CONF.register_opts(ML2_BROCADE, "ml2_brocade")
+
+
+def reigister_brocade_topology():
+ cfg.CONF.register_opts(TOPOLOGY_OPTS, "TOPOLOGY")
+
+
+def register_brocade_l3_config():
+ cfg.CONF.register_opts(RBRIDGE_OPTS, "svi")
+
+
+def register_brocade_fwaas_config():
+ cfg.CONF.register_opts(FWAAS, "fwaas")
+
+
+def get_brocade_fwaas_config():
+ register_brocade_fwaas_config()
+ fwaas = {'seq_ids': cfg.CONF.fwaas.seq_ids,
+ 'direction': cfg.CONF.fwaas.direction,
+ 'count': cfg.CONF.fwaas.count,
+ 'log': cfg.CONF.fwaas.log,
+ 'acl_file': cfg.CONF.fwaas.acl_file
+ }
+ return fwaas
+
+
+def get_acl_files():
+ fwaas = get_brocade_fwaas_config()
+ return fwaas['acl_file']
+
+
+def get_brocade_credentials():
+ register_brocade_credentials()
+ switch = {'address': cfg.CONF.ml2_brocade.address,
+ 'username': cfg.CONF.ml2_brocade.username,
+ 'password': cfg.CONF.ml2_brocade.password,
+ 'os': cfg.CONF.ml2_brocade.ostype,
+ 'osversion': '5.0.0'}
+ return switch
+
+
+def get_lacp_args():
+ register_brocade_credentials()
+ po = cfg.CONF.ml2_brocade.port_channels
+ po_lo, po_hi = po.split(':')
+ lacp = {'lacp_enabled': cfg.CONF.ml2_brocade.lacp_enabled,
+ 'po_lo': po_lo,
+ 'po_hi': po_hi,
+ 'po_mode': cfg.CONF.ml2_brocade.port_channel_mode,
+ 'po_type': cfg.CONF.ml2_brocade.port_channel_type,
+ 'po_lb_mode': cfg.CONF.ml2_brocade.port_channel_lb_mode,
+ 'po_speed': cfg.CONF.ml2_brocade.po_speed,
+ 'remove_ch_grp': cfg.CONF.ml2_brocade.remove_ch_grp}
+ return lacp
+
+
+def _is_lacp_enabled():
+ return get_lacp_args()['lacp_enabled']
+
+
+def get_port_channel_lo_hi():
+ lacp = get_lacp_args()
+ return int(lacp['po_lo']), int(lacp['po_hi'])
+
+
+def is_fqdn_supported():
+ register_brocade_credentials()
+ return cfg.CONF.ml2_brocade.fqdn
+
+
+def get_vcs_initialize():
+ register_brocade_credentials()
+ return cfg.CONF.ml2_brocade.initialize_vcs
+
+
+def get_retry_args():
+ register_brocade_credentials()
+ return cfg.CONF.ml2_brocade.nretries,\
+ cfg.CONF.ml2_brocade.ndelay,\
+ cfg.CONF.ml2_brocade.nbackoff
+
+
+def get_physical_networks():
+ register_brocade_credentials()
+ return cfg.CONF.ml2_brocade.physical_networks
+
+
+def get_brocade_l3_config():
+ register_brocade_l3_config()
+ svi = {'rbridge_ids': cfg.CONF.svi.rbridge_ids,
+ 'redundancy': True if ((cfg.CONF.svi.redundancy == 'enabled') and
+ (len(cfg.CONF.svi.rbridge_ids) > 1))
+ else False,
+ 'vrrp_version': cfg.CONF.svi.vrrp_version,
+ 'vrrp_group_id': cfg.CONF.svi.vrrp_group_id,
+ 'vrrp_advertisement_interval':
+ cfg.CONF.svi.vrrp_advertisement_interval,
+ 'is_vrf_required': cfg.CONF.svi.is_vrf_required
+ }
+
+ if not svi['redundancy'] and len(svi['rbridge_ids']) > 1:
+ # redundancy disabled consider only first rbride configured
+ del svi['rbridge_ids'][1:]
+ LOG.info(_LI("rbridge_ids %(rbridge_ids)s"
+ " redundancy %(redundancy)s"
+ " vrrp_version %(vrrp_version)s"
+ " vrrp_group_id %(vrrp_group_id)s") %
+ {'rbridge_ids': svi['rbridge_ids'],
+ 'redundancy': svi['redundancy'],
+ 'vrrp_version': svi['vrrp_version'],
+ 'vrrp_group_id': svi['vrrp_group_id']})
+
+ return svi
+
+
+def is_vrf_required():
+ register_brocade_l3_config()
+ return cfg.CONF.svi.is_vrf_required
+
+
+def remove_from_xml_tree(the_config, tag):
+ """removed unused xml tag"""
+ for elt in the_config.iterdescendants():
+ if tag in elt.tag:
+ elt.getparent().remove(elt)
+
+
+def add_text_to_ele(elt, text):
+ """add text to xml tag"""
+ elt.text = text
+
+
+def remove_unused_tags(the_config, name, action, protocol, src_ip, dst_ip,
+ sport_operator, src_port, dport_operator, dst_port,
+ count, log, dscp):
+ """This function removes unused xml tags gor the given paramaters"""
+ if not dscp or dscp == '':
+ remove_from_xml_tree(the_config, 'dscp')
+# handle count and log tags
+ if not count:
+ remove_from_xml_tree(the_config, 'count')
+
+ if not log:
+ remove_from_xml_tree(the_config, 'log')
+# handle ip tags
+ if src_ip == 'any':
+ remove_from_xml_tree(the_config, 'src-mask')
+
+ if dst_ip == 'any':
+ remove_from_xml_tree(the_config, 'dst-mask')
+
+# handle protocol tags
+ if protocol == 'tcp':
+ remove_from_xml_tree(the_config, 'udp')
+ elif protocol == 'udp':
+ remove_from_xml_tree(the_config, 'tcp')
+
+ if ((src_port == '') & (dst_port == '')):
+ remove_from_xml_tree(the_config, 'port')
+ elif (src_port == ''):
+ remove_from_xml_tree(the_config, 'sport')
+ elif (dst_port == ''):
+ remove_from_xml_tree(the_config, 'dport')
+
+ if (sport_operator == 'range'):
+ remove_from_xml_tree(the_config, 'sport-number-eq-neq')
+ elif (sport_operator == 'eq'):
+ remove_from_xml_tree(the_config, 'sport-number-range')
+ else:
+ remove_from_xml_tree(the_config, 'sport')
+
+ if (dport_operator == 'range'):
+ remove_from_xml_tree(the_config, 'dport-number-eq-neq')
+ elif (dport_operator == 'eq'):
+ remove_from_xml_tree(the_config, 'dport-number-range')
+ else:
+ remove_from_xml_tree(the_config, 'dport')
+
+
+class SeqIdBitmap(object):
+
+ """This class manages generating sequence ids for ip acls"""
+
+ def __init__(self, min_search_seqid, max_search_seqid):
+ self._min_search_seqid = min_search_seqid
+ self._max_search_seqid = max_search_seqid
+
+ def get_seq_ids(self, acl, howmany):
+ """Try to get a specific vlan if requested or get the next vlan."""
+ ids = []
+ it = 0
+ for seq_id in moves.range(self._min_search_seqid,
+ self._max_search_seqid):
+ ids.append(seq_id)
+ it += 1
+ if(it >= howmany):
+ break
+ return ids
+
+
+def get_firewall_object_prefix(fw):
+ """Get Acl policy name using firewal id and tenant_id"""
+ policy_name = template.ROUTER_OBJ_PREFIX +\
+ fw['tenant_id'][:OBJ_PREFIX_LEN] +\
+ fw['id'][:OBJ_PREFIX_LEN]
+ return policy_name
+
+
+def make_rule(name, seq_id, action, protocol, src_ip, src_mask, dst_ip,
+ dst_mask, sport_operator, sport_low, sport_high,
+ dport_operator, dport_low, dport_high, count, log, dscp):
+ """create xml template to create acl rule on VDX"""
+ xml_tring = template.IP_ACL_RULE.format()
+ the_config = etree.fromstring(xml_tring)
+ remove_unused_tags(the_config, name, action, protocol, src_ip, dst_ip,
+ sport_operator, (sport_low, sport_high), dport_operator,
+ (dport_low, dport_high), count, log, dscp)
+
+ for elt in the_config.iterdescendants():
+ if elt.tag == ('seq-id'):
+ add_text_to_ele(elt, seq_id)
+ elif elt.tag == ('action'):
+ add_text_to_ele(elt, action)
+ elif elt.tag == ('protocol-type'):
+ add_text_to_ele(elt, protocol)
+ elif elt.tag == ('src-host-any-sip'):
+ add_text_to_ele(elt, src_ip)
+ elif elt.tag == ('src-mask'):
+ add_text_to_ele(elt, src_mask)
+ elif elt.tag == ('dst-host-any-dip'):
+ add_text_to_ele(elt, dst_ip)
+ elif elt.tag == ('dst-mask'):
+ add_text_to_ele(elt, dst_mask)
+ elif elt.tag == ('sport'):
+ add_text_to_ele(elt, sport_operator)
+ elif "sport-number-eq-neq" in elt.tag:
+ add_text_to_ele(elt, sport_low)
+ elif "sport-number-range-lower" in elt.tag:
+ add_text_to_ele(elt, sport_low)
+ elif "sport-number-range-higher" in elt.tag:
+ add_text_to_ele(elt, sport_high)
+ elif elt.tag == ('dport'):
+ add_text_to_ele(elt, dport_operator)
+ elif "dport-number-eq-neq" in elt.tag:
+ add_text_to_ele(elt, dport_low)
+ elif "dport-number-range-lower" in elt.tag:
+ add_text_to_ele(elt, dport_low)
+ elif "dport-number-range-higher" in elt.tag:
+ add_text_to_ele(elt, dport_high)
+ elif "dscp" in elt.tag:
+ add_text_to_ele(elt, dscp)
+
+ xml_request = etree.tostring(the_config, pretty_print=True)
+ return xml_request
+
+
+def len_to_wild_mask(len):
+ """Convert a bit length to a dotted netmask (aka. CIDR to netmask)"""
+ mask = ''
+ if not isinstance(len, int) or len < 0 or len > 32:
+ return None
+
+ for t in range(4):
+ if len > 7:
+ mask += '0.'
+ else:
+ dec = ((255 - (2 ** (8 - len) - 1)) ^ 255)
+ mask += str(dec) + '.'
+ len -= 8
+ if len < 0:
+ len = 0
+
+ return mask[:-1]
+
+
+def cidr_2_nwm(addr):
+ """Get network address prefix and wild mask from a given address."""
+ if addr is None:
+ return (None, None)
+ nw_addr, nw_len = addr.split('/')
+ nw_len = len_to_wild_mask(int(nw_len))
+ return nw_addr, nw_len
+
+
+def get_seq_ids(seq_ids):
+ if seq_ids:
+ if ':' in seq_ids:
+ seq_id_low, seq_id_high = seq_ids.split(':')
+ return seq_id_low, seq_id_high
+ return None, None
+
+
+def get_ports(port):
+ port_low = ''
+ port_high = ''
+ if ((port is None) | (port == '')):
+ return (port_low, port_high)
+ if ':' in port:
+ port_low, port_high = port.split(':')
+ else:
+ port_low, port_high = port, port_high
+ return port_low, port_high
+
+
+def get_port_operator(port_low, port_high):
+ """detect if user has entered single port or range of ports"""
+
+ if ((port_low) and (port_high)):
+ return "range"
+ elif((port_low) or (port_high)):
+ return "eq"
+ else:
+ return None
+
+
+def _parse_info_entry(info):
+ """parses string ::info"""
+ entry = info.strip()
+ if ':' in entry:
+ try:
+ speed, port, info = entry.split(':')
+ speed = _get_long_speed(speed)
+ speed_port = (speed, port)
+ return speed_port, info
+ except Exception:
+ raise Exception("Brocade Plugin raised exception parsing port info"
+ "Failed")
+ return entry, None
+
+
+def _parse_connection_entry(connection):
+ """parses string :::"""
+ entry = connection.strip()
+ if ':' in entry:
+ try:
+ host, network, speed, port = entry.split(':')
+ speed = _get_long_speed(speed)
+ speed_port = (speed, port)
+ return (host, network), speed_port
+ except Exception:
+ raise Exception("Brocade Plugin raised exception parsing topology"
+ "Failed")
+ return entry, None
+
+
+def _parse_connection_info():
+ """parses connection info"""
+ reigister_brocade_topology()
+ connections_info = cfg.CONF.TOPOLOGY.connections
+ mtu_info = cfg.CONF.TOPOLOGY.mtu
+ native_vlan_info = cfg.CONF.TOPOLOGY.native_vlans
+ device_dict = {}
+ bond_mappings = {}
+ mtu_dict = {}
+ native_vlans_dict = {}
+ for entry in connections_info:
+ host_physnet, speed_port = _parse_connection_entry(entry)
+ device_dict.setdefault(host_physnet, []).append(speed_port)
+
+ for entry in native_vlan_info:
+ try:
+ speed_port, native_vlan = _parse_info_entry(entry)
+ native_vlans_dict[speed_port] = native_vlan
+ except Exception:
+ raise Exception("parsing native vlan Failed")
+
+ for entry in mtu_info:
+ try:
+ speed_port, mtu = _parse_info_entry(entry)
+ mtu_dict[speed_port] = mtu
+ except Exception:
+ raise Exception("parsing MTU Failed")
+
+ if _is_lacp_enabled():
+ bond_info = cfg.CONF.TOPOLOGY.bond_mappings
+ for entry in bond_info:
+ entry = entry.strip()
+ if ':' in entry:
+ try:
+ host, network, port_channel = entry.split(':')
+ bond_mappings.setdefault((host, network), []).append(
+ port_channel)
+ except Exception:
+ raise Exception("parsing bond to portchannel"
+ " mapping failed")
+ return device_dict, bond_mappings, mtu_dict, native_vlans_dict
+
+
+def _aggregate_nics_to_support_lacp(topology, bond_info):
+ device_dict = {}
+ lacp_ports = {}
+ if not bond_info:
+ po_lo, po_hi = get_port_channel_lo_hi()
+ for host_physnet in topology.keys():
+ if len(topology[host_physnet]) >= 1:
+ for item in topology[host_physnet]:
+ if ((not bond_info) and (po_hi >= po_lo)):
+ lacp_ports.setdefault(po_lo, []).append(item)
+ LOG.debug("po lo %d po_hi %d", po_lo, po_hi)
+ elif bond_info:
+ # only one port-channel
+ po_lo = bond_info[host_physnet][0]
+ lacp_ports.setdefault(po_lo, []).append(item)
+ else:
+ LOG.error(_LE("exhausted all port-channels increase"
+ "port-channel range or bond mappings"
+ " not provided"))
+ sys.exit(0)
+
+ device_dict.setdefault(host_physnet, []).append(("port-channel",
+ str(po_lo)))
+ if not bond_info:
+ po_lo = po_lo + 1
+ LOG.debug("device_dict %s lacp_ports %s", device_dict, lacp_ports)
+ return device_dict, lacp_ports
+
+
+def _get_interface_speed_name(topology, physical_network):
+ """given a physical network return interface speed and name"""
+ interfaces = []
+ for key in topology.keys():
+ host, network = key
+ if network == physical_network:
+ interfaces.append(topology[key])
+ return interfaces
+
+
+def _is_valid_three_tupple(interface):
+ """verify if given interface is threee tupple"""
+ if '/' in interface:
+ s = interface.split('/')
+ # Length is checked against three because of rbridge,slot,port
+ if len(s) != 3:
+ LOG.error(_LE("_is_valid_three_tupple:"
+ "invalid interface %s configure"
+ "valid interface"), interface)
+ return False
+ return True
+ return False
+
+
+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 _is_valid_interface_speed(speed):
+ """Check if given speed is valid"""
+ if 'ten' in speed:
+ speed = "tengigabitethernet"
+ return True
+ elif 'gig' in speed:
+ speed = "gigabitEthernet"
+ return True
+ elif 'for' in speed:
+ speed = "fortyGigabitEthernet"
+ return True
+ elif 'hun' in speed:
+ speed = "hundredGigabitEthernet"
+ return True
+ else:
+ LOG.error(_LE("_is_valid_interface_speed:invalid speed parameter %s"
+ " configure valid speed"), speed)
+ return False
+
+
+def _is_valid_interface(device, switch, nos_driver):
+ """validate if given interfaces are valid"""
+ for key in device.keys():
+ for (speed, interface) in device[key]:
+ if not _is_valid_three_tupple(interface):
+ return False
+ if not _is_valid_interface_speed(speed):
+ return False
+ return True
diff --git a/networking_brocade/vdx/services/l3_router/l3_router_plugin.py b/networking_brocade/vdx/services/l3_router/l3_router_plugin.py
index c1a43ff..99a7896 100644
--- a/networking_brocade/vdx/services/l3_router/l3_router_plugin.py
+++ b/networking_brocade/vdx/services/l3_router/l3_router_plugin.py
@@ -17,13 +17,13 @@
"""Implentation of Brocade SVI service 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 networking_brocade.vdx.ml2driver.nos import nosdriver as driver
from neutron.common import constants as l3_constants
-from neutron.i18n import _LE
-from neutron.i18n import _LI
from neutron.plugins.ml2 import db
-from neutron.plugins.ml2.drivers.brocade.db import models as brocade_db
from neutron.services.l3_router import l3_router_plugin as router
from oslo_config import cfg
from oslo_log import log as logging
diff --git a/networking_brocade/vdx/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py b/networking_brocade/vdx/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py
index 881f714..801b30b 100644
--- a/networking_brocade/vdx/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py
+++ b/networking_brocade/vdx/tests/unit/ml2/drivers/brocade/test_brocade_l3_plugin.py
@@ -16,8 +16,8 @@
#
import mock
+from networking_brocade._i18n import _LI
from neutron.db import api as db
-from neutron.i18n import _LI
from neutron.tests.unit.extensions import test_l3
from oslo_config import cfg
from oslo_context import context as oslo_context
diff --git a/setup.cfg b/setup.cfg
index a532e73..21f17da 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -32,12 +32,18 @@ data_files =
etc/neutron/plugins/brocade/brocade_mlx.ini
[entry_points]
+neutron.db.alembic_migrations =
+ networking-brocade = networking_brocade.vdx.db.migration:alembic_migrations
neutron.ml2.mechanism_drivers =
brocade_fi_ni = networking_brocade.mlx.ml2.fi_ni.mechanism_brocade_fi_ni:BrocadeFiNiMechanism
+ brocade_vdx_ampp = networking_brocade.vdx.ampp.ml2driver.mechanism_brocade:BrocadeMechanism
+ brocade_vdx_vlan = networking_brocade.vdx.non_ampp.ml2driver.mechanism_brocade:BrocadeMechanism
+ brocade_vdx_baremetal = networking_brocade.vdx.bare_metal.mechanism_brocade:BrocadeMechanism
# Service Plugins
neutron.service_plugins =
brocade_mlx_l3 = networking_brocade.mlx.services.l3_router.brocade.l3_router_plugin.BrocadeRouterPlugin
+ brocade_vdx_l3 = networking_brocade.vdx.services.l3_router.l3_router_plugin.BrocadeSVIPlugin
[build_sphinx]
source-dir = doc/source
diff --git a/tox.ini b/tox.ini
index 6187198..a7d6d7a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -32,7 +32,7 @@ commands = python setup.py build_sphinx
# E123, E125 skipped as they are invalid PEP-8.
show-source = True
-ignore = E125,E126,E128,E129,E265,H305,H404,H405,H703
+ignore = E125,E126,E128,E129,E265,H305,H404,H405,H703,N340,N341
builtins = _
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build