Implementaion of Mechanism driver for

Brocade VDX cluster of switches

Change-Id: Ic1649f7cee73a41f286e12d8ba6ca30be6261cfe
Implements: blueprint brocade-ml2-mechanism-driver
This commit is contained in:
Shiv Haris 2013-12-04 18:02:17 -08:00
parent fe76c155fd
commit 0b5a2fac5d
14 changed files with 1174 additions and 0 deletions

View File

@ -18,6 +18,8 @@
# Example: mechanism drivers = openvswitch,mlnx
# Example: mechanism_drivers = arista
# Example: mechanism_drivers = cisco,logger
# Example: mechanism_drivers = openvswitch,brocade
# Example: mechanism_drivers = linuxbridge,brocade
[ml2_type_flat]
# (ListOpt) List of physical_network names with which flat networks

View File

@ -0,0 +1,13 @@
[ml2_brocade]
# username = <mgmt admin username>
# password = <mgmt admin password>
# address = <switch mgmt ip address>
# ostype = NOS
# physical_networks = physnet1,physnet2
#
# Example:
# username = admin
# password = password
# address = 10.24.84.38
# ostype = NOS
# physical_networks = physnet1,physnet2

View File

@ -0,0 +1,70 @@
# Copyright 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.
#
"""Brocade ML2 Mech. Driver
Revision ID: 492a106273f8
Revises: fcac4c42e2cc
Create Date: 2014-03-03 15:35:46.974523
"""
# revision identifiers, used by Alembic.
revision = '492a106273f8'
down_revision = 'fcac4c42e2cc'
# Change to ['*'] if this migration applies to all plugins
migration_for_plugins = [
'neutron.plugins.ml2.plugin.Ml2Plugin'
]
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
def upgrade(active_plugins=None, options=None):
if not migration.should_run(active_plugins, migration_for_plugins):
return
op.create_table(
'ml2_brocadenetworks',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('vlan', sa.String(length=10), nullable=True),
sa.Column('segment_id', sa.String(length=36), nullable=True),
sa.Column('network_type', sa.String(length=10), nullable=True),
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'))
op.create_table(
'ml2_brocadeports',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('network_id', sa.String(length=36), nullable=False),
sa.Column('admin_state_up', sa.Boolean()),
sa.Column('physical_interface', sa.String(length=36), nullable=True),
sa.Column('vlan_id', sa.String(length=36), nullable=True),
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'))
def downgrade(active_plugins=None, options=None):
if not migration.should_run(active_plugins, migration_for_plugins):
return
op.drop_table('ml2_brocadenetworks')
op.drop_table('ml2_brocadeports')

View File

@ -0,0 +1,60 @@
Brocade ML2 Mechanism driver from ML2 plugin
============================================
* up-to-date version of these instructions are located at:
http://50.56.236.34/docs/brocade-ml2-mechanism.txt
* N.B.: Please see Prerequisites section regarding ncclient (netconf client library)
* Supports VCS (Virtual Cluster of Switches)
* Issues/Questions/Bugs: sharis@brocade.com
1. VDX 67xx series of switches
2. VDX 87xx series of switches
ML2 plugin requires mechanism driver to support configuring of hardware switches.
Brocade Mechanism for ML2 uses NETCONF at the backend to configure the Brocade switch.
Currently the mechanism drivers support VLANs only.
+------------+ +------------+ +-------------+
| | | | | |
Neutron | | | | | Brocade |
v2.0 | Openstack | | Brocade | NETCONF | VCS Switch |
----+ Neutron +--------+ Mechanism +----------+ |
| ML2 | | Driver | | VDX 67xx |
| Plugin | | | | VDX 87xx |
| | | | | |
| | | | | |
+------------+ +------------+ +-------------+
Configuration
In order to use this mechnism the brocade configuration file needs to be edited with the appropriate
configuration information:
% cat /etc/neutron/plugins/ml2/ml2_conf_brocade.ini
[switch]
username = admin
password = password
address = <switch mgmt ip address>
ostype = NOS
physical_networks = phys1
Additionally the brocade mechanism driver needs to be enabled from the ml2 config file:
% cat /etc/neutron/plugins/ml2/ml2_conf.ini
[ml2]
tenant_network_types = vlan
type_drivers = local,flat,vlan,gre,vxlan
mechanism_drivers = openvswitch,brocade
# OR mechanism_drivers = openvswitch,linuxbridge,hyperv,brocade
...
...
...
Required L2 Agent
This mechanism driver works in conjuction with an L2 Agent. The agent should be loaded as well in order for it to configure the virtual network int the host machine. Please see the configuration above. Atleast one of linuxbridge or openvswitch must be specified.

View File

@ -0,0 +1,139 @@
# 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.
#
# Authors:
# Shiv Haris (sharis@brocade.com)
# Varma Bhupatiraju (vbhupati@#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))
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='vlan',
tenant_id=tenant_id)
session.add(net)
return net
def delete_network(context, net_id):
"""Delete a brocade specific network/port-profiles."""
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):
"""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)
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 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})

View File

@ -0,0 +1,384 @@
# 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.
#
# Author:
# Shiv Haris (shivharis@hotmail.com)
"""Implentation of Brocade ML2 Mechanism driver for ML2 Plugin."""
from oslo.config import cfg
from neutron.openstack.common import importutils
from neutron.openstack.common import log as logging
from neutron.plugins.ml2.drivers.brocade.db import models as brocade_db
LOG = logging.getLogger(__name__)
MECHANISM_VERSION = 0.9
NOS_DRIVER = 'neutron.plugins.ml2.drivers.brocade.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=_('Unused'))
]
cfg.CONF.register_opts(ML2_BROCADE, "ml2_brocade")
class BrocadeMechanism():
"""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()
def brocade_init(self):
"""Brocade specific initialization for this class."""
self._switch = {'address': cfg.CONF.ml2_brocade.address,
'username': cfg.CONF.ml2_brocade.username,
'password': cfg.CONF.ml2_brocade.password
}
self._driver = importutils.import_object(NOS_DRIVER)
def create_network_precommit(self, mech_context):
"""Create Network in the mechanism specific database table."""
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 != '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(
_("Brocade Mechanism: failed to create network in db"))
raise Exception(
_("Brocade Mechanism: create_network_precommit failed"))
LOG.info(_("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"))
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(_("Brocade NOS driver: failed in create network"))
brocade_db.delete_network(context, network_id)
raise Exception(
_("Brocade Mechanism: create_network_postcommmit failed"))
LOG.info(_("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"))
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(
_("Brocade Mechanism: failed to delete network in db"))
raise Exception(
_("Brocade Mechanism: delete_network_precommit failed"))
LOG.info(_("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 which translates to removng portprofile
from the switch.
"""
LOG.debug(_("delete_network_postcommit: called"))
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(_("Brocade NOS driver: failed to delete network"))
raise Exception(
_("Brocade switch exception, "
"delete_network_postcommit failed"))
LOG.info(_("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"))
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)
except Exception:
LOG.exception(_("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"))
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)
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(
_("Brocade NOS driver: failed to associate mac %s")
% interface_mac)
raise Exception(
_("Brocade switch exception: create_port_postcommit failed"))
LOG.info(
_("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"))
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(_("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"))
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)
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(
_("Brocade NOS driver: failed to dissociate MAC %s") %
interface_mac)
raise Exception(
_("Brocade switch exception, delete_port_postcommit failed"))
LOG.info(
_("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

View File

@ -0,0 +1,197 @@
# 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.
#
# Authors:
# Varma Bhupatiraju (vbhupati@#brocade.com)
# Shiv Haris (sharis@brocade.com)
"""NOS NETCONF XML Configuration Command Templates.
Interface Configuration Commands
"""
# Create VLAN (vlan_id)
CREATE_VLAN_INTERFACE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface-vlan xmlns="urn:brocade.com:mgmt:brocade-interface">
<interface>
<vlan>
<name>{vlan_id}</name>
</vlan>
</interface>
</interface-vlan>
</config>
"""
# Delete VLAN (vlan_id)
DELETE_VLAN_INTERFACE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface-vlan xmlns="urn:brocade.com:mgmt:brocade-interface">
<interface>
<vlan operation="delete">
<name>{vlan_id}</name>
</vlan>
</interface>
</interface-vlan>
</config>
"""
#
# AMPP Life-cycle Management Configuration Commands
#
# Create AMPP port-profile (port_profile_name)
CREATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
</port-profile>
</config>
"""
# Create VLAN sub-profile for port-profile (port_profile_name)
CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile/>
</port-profile>
</config>
"""
# Configure L2 mode for VLAN sub-profile (port_profile_name)
CONFIGURE_L2_MODE_FOR_VLAN_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile>
<switchport/>
</vlan-profile>
</port-profile>
</config>
"""
# Configure trunk mode for VLAN sub-profile (port_profile_name)
CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile>
<switchport>
<mode>
<vlan-mode>trunk</vlan-mode>
</mode>
</switchport>
</vlan-profile>
</port-profile>
</config>
"""
# Configure allowed VLANs for VLAN sub-profile
# (port_profile_name, allowed_vlan, native_vlan)
CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile>
<switchport>
<trunk>
<allowed>
<vlan>
<add>{vlan_id}</add>
</vlan>
</allowed>
</trunk>
</switchport>
</vlan-profile>
</port-profile>
</config>
"""
# Delete port-profile (port_profile_name)
DELETE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile
xmlns="urn:brocade.com:mgmt:brocade-port-profile" operation="delete">
<name>{name}</name>
</port-profile>
</config>
"""
# Activate port-profile (port_profile_name)
ACTIVATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<activate/>
</port-profile>
</port-profile-global>
</config>
"""
# Deactivate port-profile (port_profile_name)
DEACTIVATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<activate
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete" />
</port-profile>
</port-profile-global>
</config>
"""
# Associate MAC address to port-profile (port_profile_name, mac_address)
ASSOCIATE_MAC_TO_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<static>
<mac-address>{mac_address}</mac-address>
</static>
</port-profile>
</port-profile-global>
</config>
"""
# Dissociate MAC address from port-profile (port_profile_name, mac_address)
DISSOCIATE_MAC_FROM_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<static
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
<mac-address>{mac_address}</mac-address>
</static>
</port-profile>
</port-profile-global>
</config>
"""
#
# Constants
#
# Port profile naming convention for Neutron networks
OS_PORT_PROFILE_NAME = "openstack-profile-{id}"
# Port profile filter expressions
PORT_PROFILE_XPATH_FILTER = "/port-profile"
PORT_PROFILE_NAME_XPATH_FILTER = "/port-profile[name='{name}']"

View File

@ -0,0 +1,236 @@
# 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.
#
# 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 neutron.openstack.common import excutils
from neutron.openstack.common import log as logging
from neutron.plugins.brocade.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():
"""NOS NETCONF interface driver for Neutron network.
Handles life-cycle management of Neutron network (leverages AMPP on NOS)
"""
def __init__(self):
self.mgr = None
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:
LOG.exception(_("Connect failed to switch"))
raise
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 create_network(self, host, username, password, net_id):
"""Creates a new virtual network."""
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)
self.create_vlan_profile_for_port_profile(mgr, name)
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(_("NETCONF error"))
self.close_session()
def delete_network(self, host, username, password, net_id):
"""Deletes a virtual network."""
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
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(_("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(_("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(_("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)

View File

@ -0,0 +1,71 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2013 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 neutron.openstack.common import importutils
from neutron.openstack.common import log as logging
from neutron.plugins.ml2 import config as ml2_config
from neutron.plugins.ml2.drivers.brocade import (mechanism_brocade
as brocademechanism)
from neutron.tests.unit import test_db_plugin
LOG = logging.getLogger(__name__)
MECHANISM_NAME = ('neutron.plugins.ml2.'
'drivers.brocade.mechanism_brocade.BrocadeMechanism')
class TestBrocadeMechDriverV2(test_db_plugin.NeutronDbPluginV2TestCase):
"""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')
self.addCleanup(ml2_config.cfg.CONF.reset)
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_db_plugin.TestNetworksV2,
TestBrocadeMechDriverV2):
pass
class TestBrocadeMechDriverPortsV2(test_db_plugin.TestPortsV2,
TestBrocadeMechDriverV2):
pass
class TestBrocadeMechDriverSubnetsV2(test_db_plugin.TestSubnetsV2,
TestBrocadeMechDriverV2):
pass

View File

@ -58,6 +58,7 @@ data_files =
etc/neutron/plugins/ml2 =
etc/neutron/plugins/ml2/ml2_conf.ini
etc/neutron/plugins/ml2/ml2_conf_arista.ini
etc/neutron/plugins/ml2/ml2_conf_brocade.ini
etc/neutron/plugins/ml2/ml2_conf_cisco.ini
etc/neutron/plugins/bigswitch/restproxy.ini
etc/neutron/plugins/ml2/ml2_conf_ofa.ini
@ -171,6 +172,7 @@ neutron.ml2.mechanism_drivers =
bigswitch = neutron.plugins.ml2.drivers.mech_bigswitch.driver:BigSwitchMechanismDriver
ofagent = neutron.plugins.ml2.drivers.mech_ofagent:OfagentMechanismDriver
mlnx = neutron.plugins.ml2.drivers.mlnx.mech_mlnx:MlnxMechanismDriver
brocade = neutron.plugins.ml2.drivers.brocade.mechanism_brocade:BrocadeMechanism
neutron.openstack.common.cache.backends =
memory = neutron.openstack.common.cache._backends.memory:MemoryBackend