Adds Brocade Plugin implementation
blueprint brocade-quantum-plugin This plugin is meant to orchestrate Brocade VCS switches running NOS, examples of these are: 1. VDX 67xx series of switches 2. VDX 87xx series of switches Change-Id: Ia8c91ba0e70d3dc7fa79e7f0e09f869542846954
This commit is contained in:
parent
40bc2a2304
commit
cf133047c8
48
etc/quantum/plugins/brocade/brocade.ini
Normal file
48
etc/quantum/plugins/brocade/brocade.ini
Normal file
@ -0,0 +1,48 @@
|
||||
[SWITCH]
|
||||
# username = <mgmt admin username>
|
||||
# password = <mgmt admin password>
|
||||
# address = <switch mgmt ip address>
|
||||
# ostype = NOS
|
||||
#
|
||||
# Example:
|
||||
# username = admin
|
||||
# password = password
|
||||
# address = 10.24.84.38
|
||||
# ostype = NOS
|
||||
|
||||
[DATABASE]
|
||||
# sql_connection = sqlite://
|
||||
# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size,
|
||||
# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled.
|
||||
# sql_dbpool_enable = False
|
||||
# Minimum number of SQL connections to keep open in a pool
|
||||
# sql_min_pool_size = 1
|
||||
# Maximum number of SQL connections to keep open in a pool
|
||||
# sql_max_pool_size = 5
|
||||
# Timeout in seconds before idle sql connections are reaped
|
||||
# sql_idle_timeout = 3600
|
||||
#
|
||||
# Example:
|
||||
# sql_connection = mysql://root:pass@localhost/brcd_quantum?charset=utf8
|
||||
|
||||
[PHYSICAL_INTERFACE]
|
||||
# physical_interface = <physical network name>
|
||||
#
|
||||
# Example:
|
||||
# physical_interface = physnet1
|
||||
|
||||
[VLANS]
|
||||
# network_vlan_ranges = <physical network name>:nnnn:mmmm
|
||||
#
|
||||
# Example:
|
||||
# network_vlan_ranges = physnet1:1000:2999
|
||||
|
||||
[AGENT]
|
||||
# Example:
|
||||
# root_helper = sudo /usr/local/bin/quantum-rootwrap /etc/quantum/rootwrap.conf
|
||||
|
||||
[LINUX_BRIDGE]
|
||||
# physical_interface_mappings = <physical network name>:<local interface>
|
||||
#
|
||||
# Example:
|
||||
# physical_interface_mappings = physnet1:em1
|
453
quantum/plugins/brocade/QuantumPlugin.py
Normal file
453
quantum/plugins/brocade/QuantumPlugin.py
Normal file
@ -0,0 +1,453 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 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)
|
||||
#
|
||||
# (Some parts adapted from LinuxBridge Plugin)
|
||||
# TODO (shiv) need support for security groups
|
||||
|
||||
|
||||
"""
|
||||
Implentation of Brocade Quantum Plugin.
|
||||
"""
|
||||
from quantum.agent import securitygroups_rpc as sg_rpc
|
||||
from quantum.common import rpc as q_rpc
|
||||
from quantum.common import topics
|
||||
from quantum.common import utils
|
||||
from quantum.db import api as db
|
||||
from quantum.db import db_base_plugin_v2
|
||||
from quantum.db import dhcp_rpc_base
|
||||
from quantum.db import l3_db
|
||||
from quantum.db import l3_rpc_base
|
||||
from quantum.db import securitygroups_rpc_base as sg_db_rpc
|
||||
from quantum.extensions import portbindings
|
||||
from quantum.extensions import securitygroup as ext_sg
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.openstack.common import context
|
||||
from quantum.openstack.common import importutils
|
||||
from quantum.openstack.common import log as logging
|
||||
from quantum.openstack.common import rpc
|
||||
from quantum.openstack.common.rpc import proxy
|
||||
from quantum.plugins.brocade.db import models as brocade_db
|
||||
from quantum.plugins.brocade import vlanbm as vbm
|
||||
from quantum import policy
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
PLUGIN_VERSION = 0.88
|
||||
AGENT_OWNER_PREFIX = "network:"
|
||||
NOS_DRIVER = 'quantum.plugins.brocade.nos.nosdriver.NOSdriver'
|
||||
|
||||
SWITCH_OPTS = [cfg.StrOpt('address', default=''),
|
||||
cfg.StrOpt('username', default=''),
|
||||
cfg.StrOpt('password', default=''),
|
||||
cfg.StrOpt('ostype', default='NOS')
|
||||
]
|
||||
|
||||
PHYSICAL_INTERFACE_OPTS = [cfg.StrOpt('physical_interface', default='eth0')
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(SWITCH_OPTS, "SWITCH")
|
||||
cfg.CONF.register_opts(PHYSICAL_INTERFACE_OPTS, "PHYSICAL_INTERFACE")
|
||||
|
||||
|
||||
class BridgeRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
|
||||
l3_rpc_base.L3RpcCallbackMixin,
|
||||
sg_db_rpc.SecurityGroupServerRpcCallbackMixin):
|
||||
"""Agent callback."""
|
||||
|
||||
RPC_API_VERSION = '1.1'
|
||||
# Device names start with "tap"
|
||||
# history
|
||||
# 1.1 Support Security Group RPC
|
||||
TAP_PREFIX_LEN = 3
|
||||
|
||||
def create_rpc_dispatcher(self):
|
||||
'''Get the rpc dispatcher for this manager.
|
||||
|
||||
If a manager would like to set an rpc API version, or support more than
|
||||
one class as the target of rpc messages, override this method.
|
||||
'''
|
||||
return q_rpc.PluginRpcDispatcher([self])
|
||||
|
||||
@classmethod
|
||||
def get_port_from_device(cls, device):
|
||||
"""Get port from the brocade specific db."""
|
||||
|
||||
# TODO(shh) context is not being passed as
|
||||
# an argument to this function;
|
||||
#
|
||||
# need to be fixed in:
|
||||
# file: quantum/db/securtygroups_rpc_base.py
|
||||
# function: securitygroup_rules_for_devices()
|
||||
# which needs to pass context to us
|
||||
|
||||
# Doing what other plugins are doing
|
||||
session = db.get_session()
|
||||
port = brocade_db.get_port_from_device(
|
||||
session, device[cls.TAP_PREFIX_LEN:])
|
||||
|
||||
# TODO(shiv): need to extend the db model to include device owners
|
||||
# make it appears that the device owner is of type network
|
||||
if port:
|
||||
port['device'] = device
|
||||
port['device_owner'] = AGENT_OWNER_PREFIX
|
||||
port['binding:vif_type'] = 'bridge'
|
||||
return port
|
||||
|
||||
def get_device_details(self, rpc_context, **kwargs):
|
||||
"""Agent requests device details."""
|
||||
|
||||
agent_id = kwargs.get('agent_id')
|
||||
device = kwargs.get('device')
|
||||
LOG.debug(_("Device %(device)s details requested from %(agent_id)s"),
|
||||
locals())
|
||||
port = brocade_db.get_port(rpc_context, device[self.TAP_PREFIX_LEN:])
|
||||
if port:
|
||||
entry = {'device': device,
|
||||
'vlan_id': port.vlan_id,
|
||||
'network_id': port.network_id,
|
||||
'port_id': port.port_id,
|
||||
'physical_network': port.physical_interface,
|
||||
'admin_state_up': port.admin_state_up
|
||||
}
|
||||
|
||||
else:
|
||||
entry = {'device': device}
|
||||
LOG.debug(_("%s can not be found in database"), device)
|
||||
return entry
|
||||
|
||||
def update_device_down(self, rpc_context, **kwargs):
|
||||
"""Device no longer exists on agent."""
|
||||
|
||||
device = kwargs.get('device')
|
||||
port = self.get_port_from_device(device)
|
||||
if port:
|
||||
entry = {'device': device,
|
||||
'exists': True}
|
||||
# Set port status to DOWN
|
||||
port_id = port['port_id']
|
||||
brocade_db.update_port_state(rpc_context, port_id, False)
|
||||
else:
|
||||
entry = {'device': device,
|
||||
'exists': False}
|
||||
LOG.debug(_("%s can not be found in database"), device)
|
||||
return entry
|
||||
|
||||
|
||||
class AgentNotifierApi(proxy.RpcProxy,
|
||||
sg_rpc.SecurityGroupAgentRpcApiMixin):
|
||||
'''Agent side of the linux bridge rpc API.
|
||||
|
||||
API version history:
|
||||
1.0 - Initial version.
|
||||
|
||||
'''
|
||||
|
||||
BASE_RPC_API_VERSION = '1.0'
|
||||
|
||||
def __init__(self, topic):
|
||||
super(AgentNotifierApi, self).__init__(
|
||||
topic=topic, default_version=self.BASE_RPC_API_VERSION)
|
||||
self.topic = topic
|
||||
self.topic_network_delete = topics.get_topic_name(topic,
|
||||
topics.NETWORK,
|
||||
topics.DELETE)
|
||||
self.topic_port_update = topics.get_topic_name(topic,
|
||||
topics.PORT,
|
||||
topics.UPDATE)
|
||||
|
||||
def network_delete(self, context, network_id):
|
||||
self.fanout_cast(context,
|
||||
self.make_msg('network_delete',
|
||||
network_id=network_id),
|
||||
topic=self.topic_network_delete)
|
||||
|
||||
def port_update(self, context, port, physical_network, vlan_id):
|
||||
self.fanout_cast(context,
|
||||
self.make_msg('port_update',
|
||||
port=port,
|
||||
physical_network=physical_network,
|
||||
vlan_id=vlan_id),
|
||||
topic=self.topic_port_update)
|
||||
|
||||
|
||||
class BrocadePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
||||
l3_db.L3_NAT_db_mixin,
|
||||
sg_db_rpc.SecurityGroupServerRpcMixin):
|
||||
"""BrocadePluginV2 is a Quantum plugin.
|
||||
|
||||
Provides L2 Virtual Network functionality using VDX. Upper
|
||||
layer driver class that interfaces to NETCONF layer below.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize Brocade Plugin, specify switch address
|
||||
and db configuration.
|
||||
"""
|
||||
|
||||
self.supported_extension_aliases = ["binding", "security-group"]
|
||||
self.binding_view = "extension:port_binding:view"
|
||||
self.binding_set = "extension:port_binding:set"
|
||||
|
||||
self.physical_interface = (cfg.CONF.PHYSICAL_INTERFACE.
|
||||
physical_interface)
|
||||
db.configure_db()
|
||||
self.ctxt = context.get_admin_context()
|
||||
self.ctxt.session = db.get_session()
|
||||
self._vlan_bitmap = vbm.VlanBitmap(self.ctxt)
|
||||
self._setup_rpc()
|
||||
self.brocade_init()
|
||||
|
||||
def brocade_init(self):
|
||||
"""Brocade specific initialization."""
|
||||
|
||||
self._switch = {'address': cfg.CONF.SWITCH.address,
|
||||
'username': cfg.CONF.SWITCH.username,
|
||||
'password': cfg.CONF.SWITCH.password
|
||||
}
|
||||
self._driver = importutils.import_object(NOS_DRIVER)
|
||||
|
||||
def _setup_rpc(self):
|
||||
# RPC support
|
||||
self.topic = topics.PLUGIN
|
||||
self.rpc_context = context.RequestContext('quantum', 'quantum',
|
||||
is_admin=False)
|
||||
self.conn = rpc.create_connection(new=True)
|
||||
self.callbacks = BridgeRpcCallbacks()
|
||||
self.dispatcher = self.callbacks.create_rpc_dispatcher()
|
||||
self.conn.create_consumer(self.topic, self.dispatcher,
|
||||
fanout=False)
|
||||
# Consume from all consumers in a thread
|
||||
self.conn.consume_in_thread()
|
||||
self.notifier = AgentNotifierApi(topics.AGENT)
|
||||
|
||||
def create_network(self, context, network):
|
||||
"""This call to create network translates to creation of
|
||||
port-profile on the physical switch.
|
||||
"""
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
net = super(BrocadePluginV2, self).create_network(context, network)
|
||||
net_uuid = net['id']
|
||||
vlan_id = self._vlan_bitmap.get_next_vlan(None)
|
||||
switch = self._switch
|
||||
try:
|
||||
self._driver.create_network(switch['address'],
|
||||
switch['username'],
|
||||
switch['password'],
|
||||
vlan_id)
|
||||
except Exception as e:
|
||||
# Proper formatting
|
||||
LOG.warning(_("Brocade NOS driver:"))
|
||||
LOG.warning(_("%s"), e)
|
||||
LOG.debug(_("Returning the allocated vlan (%d) to the pool"),
|
||||
vlan_id)
|
||||
self._vlan_bitmap.release_vlan(int(vlan_id))
|
||||
raise Exception("Brocade plugin raised exception, check logs")
|
||||
|
||||
brocade_db.create_network(context, net_uuid, vlan_id)
|
||||
|
||||
LOG.info(_("Allocated vlan (%d) from the pool"), vlan_id)
|
||||
return net
|
||||
|
||||
def delete_network(self, context, net_id):
|
||||
"""This call to delete the network translates to removing
|
||||
the port-profile on the physical switch.
|
||||
"""
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
result = super(BrocadePluginV2, self).delete_network(context,
|
||||
net_id)
|
||||
# we must delete all ports in db first (foreign key constraint)
|
||||
# there is no need to delete port in the driver (its a no-op)
|
||||
# (actually: note there is no such call to the driver)
|
||||
bports = brocade_db.get_ports(context, net_id)
|
||||
for bport in bports:
|
||||
brocade_db.delete_port(context, bport['port_id'])
|
||||
|
||||
# find the vlan for this network
|
||||
net = brocade_db.get_network(context, net_id)
|
||||
vlan_id = net['vlan']
|
||||
|
||||
# Tell hw to do remove PP
|
||||
switch = self._switch
|
||||
try:
|
||||
self._driver.delete_network(switch['address'],
|
||||
switch['username'],
|
||||
switch['password'],
|
||||
net_id)
|
||||
except Exception as e:
|
||||
# Proper formatting
|
||||
LOG.warning(_("Brocade NOS driver:"))
|
||||
LOG.warning(_("%s"), e)
|
||||
raise Exception("Brocade plugin raised exception, check logs")
|
||||
|
||||
# now ok to delete the network
|
||||
brocade_db.delete_network(context, net_id)
|
||||
|
||||
# relinquish vlan in bitmap
|
||||
self._vlan_bitmap.release_vlan(int(vlan_id))
|
||||
return result
|
||||
|
||||
def create_port(self, context, port):
|
||||
"""Create logical port on the switch."""
|
||||
|
||||
tenant_id = port['port']['tenant_id']
|
||||
network_id = port['port']['network_id']
|
||||
admin_state_up = port['port']['admin_state_up']
|
||||
|
||||
physical_interface = self.physical_interface
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
bnet = brocade_db.get_network(context, network_id)
|
||||
vlan_id = bnet['vlan']
|
||||
|
||||
quantum_port = super(BrocadePluginV2, self).create_port(context,
|
||||
port)
|
||||
interface_mac = quantum_port['mac_address']
|
||||
port_id = quantum_port['id']
|
||||
|
||||
switch = self._switch
|
||||
|
||||
# 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(switch['address'],
|
||||
switch['username'],
|
||||
switch['password'],
|
||||
vlan_id,
|
||||
mac)
|
||||
except Exception as e:
|
||||
# Proper formatting
|
||||
LOG.warning(_("Brocade NOS driver:"))
|
||||
LOG.warning(_("%s"), e)
|
||||
raise Exception("Brocade plugin raised exception, check logs")
|
||||
|
||||
# save to brocade persistent db
|
||||
brocade_db.create_port(context, port_id, network_id,
|
||||
physical_interface,
|
||||
vlan_id, tenant_id, admin_state_up)
|
||||
|
||||
# apply any extensions
|
||||
return self._extend_port_dict_binding(context, quantum_port)
|
||||
|
||||
def delete_port(self, context, port_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
super(BrocadePluginV2, self).delete_port(context, port_id)
|
||||
brocade_db.delete_port(context, port_id)
|
||||
|
||||
def update_port(self, context, port_id, port):
|
||||
original_port = self.get_port(context, port_id)
|
||||
session = context.session
|
||||
port_updated = False
|
||||
with session.begin(subtransactions=True):
|
||||
# delete the port binding and read it with the new rules
|
||||
if ext_sg.SECURITYGROUPS in port['port']:
|
||||
port['port'][ext_sg.SECURITYGROUPS] = (
|
||||
self._get_security_groups_on_port(context, port))
|
||||
self._delete_port_security_group_bindings(context, port_id)
|
||||
self._process_port_create_security_group(
|
||||
context,
|
||||
port_id,
|
||||
port['port'][ext_sg.SECURITYGROUPS])
|
||||
port_updated = True
|
||||
|
||||
port = super(BrocadePluginV2, self).update_port(
|
||||
context, port_id, port)
|
||||
self._extend_port_dict_security_group(context, port)
|
||||
|
||||
if original_port['admin_state_up'] != port['admin_state_up']:
|
||||
port_updated = True
|
||||
|
||||
if (original_port['fixed_ips'] != port['fixed_ips'] or
|
||||
not utils.compare_elements(
|
||||
original_port.get(ext_sg.SECURITYGROUPS),
|
||||
port.get(ext_sg.SECURITYGROUPS))):
|
||||
self.notifier.security_groups_member_updated(
|
||||
context, port.get(ext_sg.SECURITYGROUPS))
|
||||
|
||||
if port_updated:
|
||||
self._notify_port_updated(context, port)
|
||||
|
||||
return self._extend_port_dict_binding(context, port)
|
||||
|
||||
def get_port(self, context, port_id, fields=None):
|
||||
with context.session.begin(subtransactions=True):
|
||||
port = super(BrocadePluginV2, self).get_port(
|
||||
context, port_id, fields)
|
||||
self._extend_port_dict_security_group(context, port)
|
||||
self._extend_port_dict_binding(context, port)
|
||||
|
||||
return self._fields(port, fields)
|
||||
|
||||
def get_ports(self, context, filters=None, fields=None):
|
||||
res_ports = []
|
||||
with context.session.begin(subtransactions=True):
|
||||
ports = super(BrocadePluginV2, self).get_ports(context,
|
||||
filters,
|
||||
fields)
|
||||
for port in ports:
|
||||
self._extend_port_dict_security_group(context, port)
|
||||
self._extend_port_dict_binding(context, port)
|
||||
res_ports.append(self._fields(port, fields))
|
||||
|
||||
return res_ports
|
||||
|
||||
def _notify_port_updated(self, context, port):
|
||||
port_id = port['id']
|
||||
bport = brocade_db.get_port(context, port_id)
|
||||
self.notifier.port_update(context, port,
|
||||
bport.physical_interface,
|
||||
bport.vlan_id)
|
||||
|
||||
def _extend_port_dict_binding(self, context, port):
|
||||
if self._check_view_auth(context, port, self.binding_view):
|
||||
port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_BRIDGE
|
||||
port['binding:vif_type'] = portbindings.VIF_TYPE_BRIDGE
|
||||
port[portbindings.CAPABILITIES] = {
|
||||
portbindings.CAP_PORT_FILTER:
|
||||
'security-group' in self.supported_extension_aliases}
|
||||
return port
|
||||
|
||||
def _check_view_auth(self, context, resource, action):
|
||||
return policy.check(context, action, resource)
|
||||
|
||||
def get_plugin_version(self):
|
||||
"""Get version number of the plugin."""
|
||||
return PLUGIN_VERSION
|
||||
|
||||
@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
|
112
quantum/plugins/brocade/README.md
Normal file
112
quantum/plugins/brocade/README.md
Normal file
@ -0,0 +1,112 @@
|
||||
Brocade Openstack Quantum Plugin
|
||||
================================
|
||||
|
||||
* up-to-date version of these instructions are located at:
|
||||
http://wiki.openstack.org/brocade-quantum-plugin
|
||||
|
||||
* N.B.: Please see Prerequisites section regarding ncclient (netconf client library)
|
||||
|
||||
* Supports VCS (Virtual Cluster of Switches)
|
||||
|
||||
|
||||
Openstack Brocade Quantum Plugin implements the Quantum v2.0 API.
|
||||
|
||||
This plugin is meant to orchestrate Brocade VCS switches running NOS, examples of these are:
|
||||
|
||||
1. VDX 67xx series of switches
|
||||
2. VDX 87xx series of switches
|
||||
|
||||
Brocade Quantum plugin implements the Quantum v2.0 API. It uses NETCONF at the backend
|
||||
to configure the Brocade switch.
|
||||
|
||||
+------------+ +------------+ +-------------+
|
||||
| | | | | |
|
||||
| | | | | Brocade |
|
||||
| Openstack | v2.0 | Brocade | NETCONF | VCS Switch |
|
||||
| Quantum +--------+ Quantum +----------+ |
|
||||
| | | Plugin | | VDX 67xx |
|
||||
| | | | | VDX 87xx |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
+------------+ +------------+ +-------------+
|
||||
|
||||
|
||||
Directory Structure
|
||||
===================
|
||||
|
||||
Normally you will have your Openstack directory structure as follows:
|
||||
|
||||
/opt/stack/nova/
|
||||
/opt/stack/horizon/
|
||||
...
|
||||
/opt/stack/quantum/quantum/plugins/
|
||||
|
||||
Within this structure, Brocade plugin resides at:
|
||||
|
||||
/opt/stack/quantum/quantum/plugins/brocade
|
||||
|
||||
|
||||
Prerequsites
|
||||
============
|
||||
|
||||
This plugin requires installation of the python netconf client (ncclient) library:
|
||||
|
||||
ncclient v0.3.1 - Python library for NETCONF clients available at http://github.com/brocade/ncclient
|
||||
|
||||
% git clone https://www.github.com/brocade/ncclient
|
||||
% cd ncclient; sudo python ./setup.py install
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
1. Specify to Quantum that you will be using the Brocade Plugin - this is done
|
||||
by setting the parameter core_plugin in Quantum:
|
||||
|
||||
core_plugin = quantum.plugins.brocade.QuantumPlugin.BrocadePluginV2
|
||||
|
||||
2. Physical switch configuration parameters and Brocade specific database configuration is specified in
|
||||
the configuration file specified in the brocade.ini files:
|
||||
|
||||
% cat /etc/quantum/plugins/brocade/brocade.ini
|
||||
[SWITCH]
|
||||
username = admin
|
||||
password = password
|
||||
address = <switch mgmt ip address>
|
||||
ostype = NOS
|
||||
|
||||
[DATABASE]
|
||||
sql_connection = mysql://root:pass@localhost/brocade_quantum?charset=utf8
|
||||
|
||||
(please see list of more configuration parameters in the brocade.ini file)
|
||||
|
||||
Running Setup.py
|
||||
================
|
||||
|
||||
Running setup.py with appropriate permissions will copy the default configuration
|
||||
file to /etc/quantum/plugins/brocade/brocade.ini. This file MUST be edited to
|
||||
suit your setup/environment.
|
||||
|
||||
% cd /opt/stack/quantum/quantum/plugins/brocade
|
||||
% python setup.py
|
||||
|
||||
|
||||
Devstack
|
||||
========
|
||||
|
||||
Please see special notes for devstack at:
|
||||
http://wiki.openstack.org/brocade-quantum-plugin
|
||||
|
||||
In order to use Brocade Quantum Plugin, add the following lines in localrc, if localrc file doe
|
||||
not exist create one:
|
||||
|
||||
ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,cinder,c-sch,c-api,c-vol,n-sch,n-novnc,n-xvnc,n-cauth,horizon,rabbit,quantum,q-svc,q-agt
|
||||
Q_PLUGIN=brocade
|
||||
|
||||
As part of running devstack/stack.sh, the configuration files is copied as:
|
||||
|
||||
% cp /opt/stack/quantum/etc/quantum/plugins/brocade/brocade.ini /etc/quantum/plugins/brocade/brocade.ini
|
||||
|
||||
(hence it is important to make any changes to the configuration in:
|
||||
/opt/stack/quantum/etc/quantum/plugins/brocade/brocade.ini)
|
||||
|
16
quantum/plugins/brocade/__init__.py
Normal file
16
quantum/plugins/brocade/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 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.
|
16
quantum/plugins/brocade/db/__init__.py
Normal file
16
quantum/plugins/brocade/db/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 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.
|
152
quantum/plugins/brocade/db/models.py
Normal file
152
quantum/plugins/brocade/db/models.py
Normal file
@ -0,0 +1,152 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 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 quantum.db import model_base
|
||||
from quantum.db import models_v2
|
||||
|
||||
|
||||
class BrocadeNetwork(model_base.BASEV2, models_v2.HasId):
|
||||
"""Schema for brocade network."""
|
||||
vlan = sa.Column(sa.String(10))
|
||||
|
||||
|
||||
class BrocadePort(model_base.BASEV2):
|
||||
"""Schema for brocade port."""
|
||||
|
||||
port_id = sa.Column(sa.String(36), primary_key=True, default="")
|
||||
network_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey("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))
|
||||
tenant_id = sa.Column(sa.String(36))
|
||||
|
||||
|
||||
def create_network(context, net_id, vlan):
|
||||
"""Create a brocade specific network/port-profiles."""
|
||||
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
net = BrocadeNetwork(id=net_id, vlan=vlan)
|
||||
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 = (session.query(BrocadeNetwork).filter_by(id=net_id).first())
|
||||
if net is not None:
|
||||
session.delete(net)
|
||||
|
||||
|
||||
def get_network(context, net_id, fields=None):
|
||||
"""Get brocade specific network, with vlan extension."""
|
||||
|
||||
session = context.session
|
||||
return (session.query(BrocadeNetwork).filter_by(id=net_id).first())
|
||||
|
||||
|
||||
def get_networks(context, filters=None, fields=None):
|
||||
"""Get all brocade specific networks."""
|
||||
|
||||
session = context.session
|
||||
try:
|
||||
nets = session.query(BrocadeNetwork).all()
|
||||
return nets
|
||||
except sa.exc.SQLAlchemyError:
|
||||
return None
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
# port_id is truncated: since the linux-bridge tap device names are
|
||||
# based on truncated port id, this enables port lookups using
|
||||
# tap devices
|
||||
port_id = port_id[0:11]
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
port = BrocadePort(port_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."""
|
||||
|
||||
port_id = port_id[0:11]
|
||||
session = context.session
|
||||
port = (session.query(BrocadePort).filter_by(port_id=port_id).first())
|
||||
return port
|
||||
|
||||
|
||||
def get_ports(context, network_id=None):
|
||||
"""get a brocade specific port."""
|
||||
|
||||
session = context.session
|
||||
ports = (session.query(BrocadePort).filter_by(network_id=network_id).all())
|
||||
return ports
|
||||
|
||||
|
||||
def delete_port(context, port_id):
|
||||
"""delete brocade specific port."""
|
||||
|
||||
port_id = port_id[0:11]
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
port = (session.query(BrocadePort).filter_by(port_id=port_id).first())
|
||||
if port is not None:
|
||||
session.delete(port)
|
||||
|
||||
|
||||
def get_port_from_device(session, port_id):
|
||||
"""get port from the tap device."""
|
||||
|
||||
# device is same as truncated port_id
|
||||
port = (session.query(BrocadePort).filter_by(port_id=port_id).first())
|
||||
return port
|
||||
|
||||
|
||||
def update_port_state(context, port_id, admin_state_up):
|
||||
"""Update port attributes."""
|
||||
|
||||
port_id = port_id[0:11]
|
||||
session = context.session
|
||||
session.query(BrocadePort).filter_by(
|
||||
port_id=port_id).update({'admin_state_up': admin_state_up})
|
16
quantum/plugins/brocade/nos/__init__.py
Normal file
16
quantum/plugins/brocade/nos/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (c) 2013 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.
|
118
quantum/plugins/brocade/nos/fake_nosdriver.py
Normal file
118
quantum/plugins/brocade/nos/fake_nosdriver.py
Normal file
@ -0,0 +1,118 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 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 (sharis@brocade.com)
|
||||
|
||||
|
||||
"""
|
||||
FAKE DRIVER, for unit tests purposes
|
||||
Brocade NOS Driver implements NETCONF over SSHv2 for
|
||||
Quantum network life-cycle management
|
||||
"""
|
||||
|
||||
|
||||
class NOSdriver():
|
||||
"""NOS NETCONF interface driver for Quantum network.
|
||||
|
||||
Fake: Handles life-cycle management of Quantum network,
|
||||
leverages AMPP on NOS
|
||||
(for use by unit tests, avoids touching any hardware)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def connect(self, host, username, password):
|
||||
"""Connect via SSH and initialize the NETCONF session."""
|
||||
pass
|
||||
|
||||
def create_network(self, host, username, password, net_id):
|
||||
"""Creates a new virtual network."""
|
||||
pass
|
||||
|
||||
def delete_network(self, host, username, password, net_id):
|
||||
"""Deletes a virtual network."""
|
||||
pass
|
||||
|
||||
def associate_mac_to_network(self, host, username, password,
|
||||
net_id, mac):
|
||||
"""Associates a MAC address to virtual network."""
|
||||
pass
|
||||
|
||||
def dissociate_mac_from_network(self, host, username, password,
|
||||
net_id, mac):
|
||||
"""Dissociates a MAC address from virtual network."""
|
||||
pass
|
||||
|
||||
def create_vlan_interface(self, mgr, vlan_id):
|
||||
"""Configures a VLAN interface."""
|
||||
pass
|
||||
|
||||
def delete_vlan_interface(self, mgr, vlan_id):
|
||||
"""Deletes a VLAN interface."""
|
||||
pass
|
||||
|
||||
def get_port_profiles(self, mgr):
|
||||
"""Retrieves all port profiles."""
|
||||
pass
|
||||
|
||||
def get_port_profile(self, mgr, name):
|
||||
"""Retrieves a port profile."""
|
||||
pass
|
||||
|
||||
def create_port_profile(self, mgr, name):
|
||||
"""Creates a port profile."""
|
||||
pass
|
||||
|
||||
def delete_port_profile(self, mgr, name):
|
||||
"""Deletes a port profile."""
|
||||
pass
|
||||
|
||||
def activate_port_profile(self, mgr, name):
|
||||
"""Activates a port profile."""
|
||||
pass
|
||||
|
||||
def deactivate_port_profile(self, mgr, name):
|
||||
"""Deactivates a port profile."""
|
||||
pass
|
||||
|
||||
def associate_mac_to_port_profile(self, mgr, name, mac_address):
|
||||
"""Associates a MAC address to a port profile."""
|
||||
pass
|
||||
|
||||
def dissociate_mac_from_port_profile(self, mgr, name, mac_address):
|
||||
"""Dissociates a MAC address from a port profile."""
|
||||
pass
|
||||
|
||||
def create_vlan_profile_for_port_profile(self, mgr, name):
|
||||
"""Creates VLAN sub-profile for port profile."""
|
||||
pass
|
||||
|
||||
def configure_l2_mode_for_vlan_profile(self, mgr, name):
|
||||
"""Configures L2 mode for VLAN sub-profile."""
|
||||
pass
|
||||
|
||||
def configure_trunk_mode_for_vlan_profile(self, mgr, name):
|
||||
"""Configures trunk mode for VLAN sub-profile."""
|
||||
pass
|
||||
|
||||
def configure_allowed_vlans_for_vlan_profile(self, mgr, name, vlan_id):
|
||||
"""Configures allowed VLANs for VLAN sub-profile."""
|
||||
pass
|
204
quantum/plugins/brocade/nos/nctemplates.py
Normal file
204
quantum/plugins/brocade/nos/nctemplates.py
Normal file
@ -0,0 +1,204 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (c) 2013 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>
|
||||
<native-vlan>{vlan_id}</native-vlan>
|
||||
</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>
|
||||
"""
|
||||
|
||||
#
|
||||
# Custom RPC Commands
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
|
||||
# Port profile naming convention for Quantum 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}']"
|
202
quantum/plugins/brocade/nos/nosdriver.py
Normal file
202
quantum/plugins/brocade/nos/nosdriver.py
Normal file
@ -0,0 +1,202 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 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 (sharis@brocade.com)
|
||||
|
||||
|
||||
"""
|
||||
Brocade NOS Driver implements NETCONF over SSHv2 for
|
||||
Quantum network life-cycle management
|
||||
"""
|
||||
from ncclient import manager
|
||||
|
||||
from quantum.openstack.common import log as logging
|
||||
from quantum.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 Quantum network.
|
||||
|
||||
Handles life-cycle management of Quantum network (leverages AMPP on NOS)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def connect(self, host, username, password):
|
||||
"""Connect via SSH and initialize the NETCONF session."""
|
||||
try:
|
||||
mgr = manager.connect(host=host, port=SSH_PORT,
|
||||
username=username, password=password,
|
||||
unknown_host_cb=nos_unknown_host_cb)
|
||||
except Exception as e:
|
||||
LOG.debug(_("Connect failed to switch: %s"), e)
|
||||
raise
|
||||
|
||||
LOG.debug(_("Connect success to host %s:%d"), host, SSH_PORT)
|
||||
return mgr
|
||||
|
||||
def create_network(self, host, username, password, net_id):
|
||||
"""Creates a new virtual network."""
|
||||
|
||||
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
|
||||
with self.connect(host, username, password) as mgr:
|
||||
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)
|
||||
|
||||
def delete_network(self, host, username, password, net_id):
|
||||
"""Deletes a virtual network."""
|
||||
|
||||
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
|
||||
with self.connect(host, username, password) as mgr:
|
||||
self.deactivate_port_profile(mgr, name)
|
||||
self.delete_port_profile(mgr, name)
|
||||
self.delete_vlan_interface(mgr, net_id)
|
||||
|
||||
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)
|
||||
with self.connect(host, username, password) as mgr:
|
||||
self.associate_mac_to_port_profile(mgr, name, mac)
|
||||
|
||||
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)
|
||||
with self.connect(host, username, password) as mgr:
|
||||
self.dissociate_mac_from_port_profile(mgr, name, mac)
|
||||
|
||||
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)
|
24
quantum/plugins/brocade/tests/README
Normal file
24
quantum/plugins/brocade/tests/README
Normal file
@ -0,0 +1,24 @@
|
||||
Start the quantum-server with IP address of switch configured in brocade.ini:
|
||||
(for configuration instruction please see README.md in the above directory)
|
||||
|
||||
nostest.py:
|
||||
This tests two things:
|
||||
1. Creates port-profile on the physical switch when a quantum 'network' is created
|
||||
2. Associates the MAC address with the created port-profile
|
||||
|
||||
noscli.py:
|
||||
CLI interface to create/delete/associate MAC/dissociate MAC
|
||||
Commands:
|
||||
% noscli.py create <network>
|
||||
(after running check that PP is created on the switch)
|
||||
|
||||
% noscli.py delete <network>
|
||||
(after running check that PP is deleted from the switch)
|
||||
|
||||
% noscli.py associate <network> <mac>
|
||||
(after running check that MAC is associated with PP)
|
||||
|
||||
% noscli.py dissociate <network> <mac>
|
||||
(after running check that MAC is dissociated from the PP)
|
||||
|
||||
|
93
quantum/plugins/brocade/tests/noscli.py
Normal file
93
quantum/plugins/brocade/tests/noscli.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2013 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)
|
||||
|
||||
|
||||
"""
|
||||
Brocade NOS Driver CLI
|
||||
"""
|
||||
import argparse
|
||||
|
||||
from quantum.openstack.common import log as logging
|
||||
from quantum.plugins.brocade.nos import nosdriver as nos
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NOSCli(object):
|
||||
|
||||
def __init__(self, host, username, password):
|
||||
self.host = host
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.driver = nos.NOSdriver()
|
||||
|
||||
def execute(self, cmd):
|
||||
numargs = len(args.otherargs)
|
||||
|
||||
if args.cmd == 'create' and numargs == 1:
|
||||
self._create(args.otherargs[0])
|
||||
elif args.cmd == 'delete' and numargs == 1:
|
||||
self._delete(args.otherargs[0])
|
||||
elif args.cmd == 'associate' and numargs == 2:
|
||||
self._associate(args.otherargs[0], args.otherargs[1])
|
||||
elif args.cmd == 'dissociate' and numargs == 2:
|
||||
self._dissociate(args.otherargs[0], args.otherargs[1])
|
||||
else:
|
||||
print usage_desc
|
||||
exit(0)
|
||||
|
||||
def _create(self, net_id):
|
||||
self.driver.create_network(self.host, self.username, self.password,
|
||||
net_id)
|
||||
|
||||
def _delete(self, net_id):
|
||||
self.driver.delete_network(self.host, self.username, self.password,
|
||||
net_id)
|
||||
|
||||
def _associate(self, net_id, mac):
|
||||
self.driver.associate_mac_to_network(
|
||||
self.host, self.username, self.password, net_id, mac)
|
||||
|
||||
def _dissociate(self, net_id, mac):
|
||||
self.driver.dissociate_mac_from_network(
|
||||
self.host, self.username, self.password, net_id, mac)
|
||||
|
||||
|
||||
usage_desc = """
|
||||
Command descriptions:
|
||||
|
||||
create <id>
|
||||
delete <id>
|
||||
associate <id> <mac>
|
||||
dissociate <id> <mac>
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description='process args',
|
||||
usage=usage_desc, epilog='foo bar help')
|
||||
parser.add_argument('--ip', default='localhost')
|
||||
parser.add_argument('--username', default='admin')
|
||||
parser.add_argument('--password', default='password')
|
||||
parser.add_argument('cmd')
|
||||
parser.add_argument('otherargs', nargs='*')
|
||||
args = parser.parse_args()
|
||||
|
||||
noscli = NOSCli(args.ip, args.username, args.password)
|
||||
noscli.execute(args.cmd)
|
48
quantum/plugins/brocade/tests/nostest.py
Normal file
48
quantum/plugins/brocade/tests/nostest.py
Normal file
@ -0,0 +1,48 @@
|
||||
# Copyright (c) 2013 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)
|
||||
|
||||
|
||||
"""
|
||||
Brocade NOS Driver Test
|
||||
"""
|
||||
import sys
|
||||
|
||||
from quantum.plugins.brocade.nos import nosdriver as nos
|
||||
|
||||
|
||||
def nostest(host, username, password):
|
||||
# Driver
|
||||
driver = nos.NOSdriver()
|
||||
|
||||
# Quantum operations
|
||||
vlan = 1001
|
||||
mac = '0050.56bf.0001'
|
||||
driver.create_network(host, username, password, vlan)
|
||||
driver.associate_mac_to_network(host, username, password, vlan, mac)
|
||||
driver.dissociate_mac_from_network(host, username, password, vlan, mac)
|
||||
driver.delete_network(host, username, password, vlan)
|
||||
|
||||
# AMPP enumeration
|
||||
with driver.connect(host, username, password) as mgr:
|
||||
print driver.get_port_profiles(mgr)
|
||||
print driver.get_port_profile(mgr, 'default')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
nostest(sys.argv[1], sys.argv[2], sys.argv[3])
|
61
quantum/plugins/brocade/vlanbm.py
Normal file
61
quantum/plugins/brocade/vlanbm.py
Normal file
@ -0,0 +1,61 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 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)
|
||||
|
||||
|
||||
"""
|
||||
A Vlan Bitmap class to handle allocation/de-allocation of vlan ids.
|
||||
"""
|
||||
from quantum.plugins.brocade.db import models as brocade_db
|
||||
|
||||
|
||||
MIN_VLAN = 2
|
||||
MAX_VLAN = 4094
|
||||
|
||||
|
||||
class VlanBitmap(object):
|
||||
"""Setup a vlan bitmap for allocation/de-allocation."""
|
||||
|
||||
# Keep track of the vlans that have been allocated/de-allocated
|
||||
# uses a bitmap to do this
|
||||
|
||||
def __init__(self, ctxt):
|
||||
"""initialize the vlan as a set."""
|
||||
self.vlans = set(int(net['vlan'])
|
||||
for net in brocade_db.get_networks(ctxt)
|
||||
if net['vlan']
|
||||
)
|
||||
|
||||
def get_next_vlan(self, vlan_id=None):
|
||||
"""try to get a specific vlan if requested
|
||||
or get the next vlan.
|
||||
"""
|
||||
min_vlan_search = vlan_id or MIN_VLAN
|
||||
max_vlan_search = (vlan_id and vlan_id + 1) or MAX_VLAN
|
||||
|
||||
for vlan in xrange(min_vlan_search, max_vlan_search):
|
||||
if vlan not in self.vlans:
|
||||
self.vlans.add(vlan)
|
||||
return vlan
|
||||
|
||||
def release_vlan(self, vlan_id):
|
||||
"""return the vlan to the pool."""
|
||||
if vlan_id in self.vlans:
|
||||
self.vlans.remove(vlan_id)
|
17
quantum/tests/unit/brocade/__init__.py
Normal file
17
quantum/tests/unit/brocade/__init__.py
Normal file
@ -0,0 +1,17 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 OpenStack LLC.
|
||||
#
|
||||
# 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.
|
98
quantum/tests/unit/brocade/test_brocade_db.py
Normal file
98
quantum/tests/unit/brocade/test_brocade_db.py
Normal file
@ -0,0 +1,98 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (c) 2013 OpenStack, LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
"""
|
||||
Unit test brocade db.
|
||||
"""
|
||||
import uuid
|
||||
|
||||
from quantum import context
|
||||
from quantum.plugins.brocade.db import models as brocade_db
|
||||
from quantum.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
TEST_VLAN = 1000
|
||||
|
||||
|
||||
class TestBrocadeDb(test_plugin.QuantumDbPluginV2TestCase):
|
||||
"""Test brocade db functionality"""
|
||||
|
||||
def test_create_network(self):
|
||||
"""Test brocade specific network db."""
|
||||
|
||||
net_id = str(uuid.uuid4())
|
||||
|
||||
# Create a network
|
||||
self.context = context.get_admin_context()
|
||||
brocade_db.create_network(self.context, net_id, TEST_VLAN)
|
||||
|
||||
# Get the network and verify
|
||||
net = brocade_db.get_network(self.context, net_id)
|
||||
self.assertEqual(net['id'], net_id)
|
||||
self.assertEqual(int(net['vlan']), TEST_VLAN)
|
||||
|
||||
# Delete the network
|
||||
brocade_db.delete_network(self.context, net['id'])
|
||||
|
||||
def test_create_port(self):
|
||||
"""Test brocade specific port db."""
|
||||
|
||||
net_id = str(uuid.uuid4())
|
||||
port_id = str(uuid.uuid4())
|
||||
# port_id is truncated: since the linux-bridge tap device names are
|
||||
# based on truncated port id, this enables port lookups using
|
||||
# tap devices
|
||||
port_id = port_id[0:11]
|
||||
tenant_id = str(uuid.uuid4())
|
||||
admin_state_up = True
|
||||
|
||||
# Create Port
|
||||
|
||||
# To create a port a network must exists, Create a network
|
||||
self.context = context.get_admin_context()
|
||||
brocade_db.create_network(self.context, net_id, TEST_VLAN)
|
||||
|
||||
physical_interface = "em1"
|
||||
brocade_db.create_port(self.context, port_id, net_id,
|
||||
physical_interface,
|
||||
TEST_VLAN, tenant_id, admin_state_up)
|
||||
|
||||
port = brocade_db.get_port(self.context, port_id)
|
||||
self.assertEqual(port['port_id'], port_id)
|
||||
self.assertEqual(port['network_id'], net_id)
|
||||
self.assertEqual(port['physical_interface'], physical_interface)
|
||||
self.assertEqual(int(port['vlan_id']), TEST_VLAN)
|
||||
self.assertEqual(port['tenant_id'], tenant_id)
|
||||
self.assertEqual(port['admin_state_up'], admin_state_up)
|
||||
|
||||
admin_state_up = True
|
||||
brocade_db.update_port_state(self.context, port_id, admin_state_up)
|
||||
port = brocade_db.get_port(self.context, port_id)
|
||||
self.assertEqual(port['admin_state_up'], admin_state_up)
|
||||
|
||||
admin_state_up = False
|
||||
brocade_db.update_port_state(self.context, port_id, admin_state_up)
|
||||
port = brocade_db.get_port(self.context, port_id)
|
||||
self.assertEqual(port['admin_state_up'], admin_state_up)
|
||||
|
||||
admin_state_up = True
|
||||
brocade_db.update_port_state(self.context, port_id, admin_state_up)
|
||||
port = brocade_db.get_port(self.context, port_id)
|
||||
self.assertEqual(port['admin_state_up'], admin_state_up)
|
||||
|
||||
# Delete Port
|
||||
brocade_db.delete_port(self.context, port_id)
|
74
quantum/tests/unit/brocade/test_brocade_plugin.py
Normal file
74
quantum/tests/unit/brocade/test_brocade_plugin.py
Normal file
@ -0,0 +1,74 @@
|
||||
# Copyright (c) 2012 OpenStack, LLC.
|
||||
#
|
||||
# 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 quantum.extensions import portbindings
|
||||
from quantum.openstack.common import importutils
|
||||
from quantum.plugins.brocade import QuantumPlugin as brocade_plugin
|
||||
from quantum.tests.unit import _test_extension_portbindings as test_bindings
|
||||
from quantum.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
|
||||
PLUGIN_NAME = ('quantum.plugins.brocade.'
|
||||
'QuantumPlugin.BrocadePluginV2')
|
||||
NOS_DRIVER = ('quantum.plugins.brocade.'
|
||||
'nos.fake_nosdriver.NOSdriver')
|
||||
FAKE_IPADDRESS = '2.2.2.2'
|
||||
FAKE_USERNAME = 'user'
|
||||
FAKE_PASSWORD = 'password'
|
||||
FAKE_PHYSICAL_INTERFACE = 'em1'
|
||||
|
||||
|
||||
class BrocadePluginV2TestCase(test_plugin.QuantumDbPluginV2TestCase):
|
||||
_plugin_name = PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def mocked_brocade_init(self):
|
||||
|
||||
self._switch = {'address': FAKE_IPADDRESS,
|
||||
'username': FAKE_USERNAME,
|
||||
'password': FAKE_PASSWORD
|
||||
}
|
||||
self._driver = importutils.import_object(NOS_DRIVER)
|
||||
|
||||
with mock.patch.object(brocade_plugin.BrocadePluginV2,
|
||||
'brocade_init', new=mocked_brocade_init):
|
||||
super(BrocadePluginV2TestCase, self).setUp(self._plugin_name)
|
||||
|
||||
|
||||
class TestBrocadeBasicGet(test_plugin.TestBasicGet,
|
||||
BrocadePluginV2TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestBrocadeV2HTTPResponse(test_plugin.TestV2HTTPResponse,
|
||||
BrocadePluginV2TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestBrocadePortsV2(test_plugin.TestPortsV2,
|
||||
BrocadePluginV2TestCase,
|
||||
test_bindings.PortBindingsTestCase):
|
||||
|
||||
VIF_TYPE = portbindings.VIF_TYPE_BRIDGE
|
||||
HAS_PORT_FILTER = True
|
||||
|
||||
|
||||
class TestBrocadeNetworksV2(test_plugin.TestNetworksV2,
|
||||
BrocadePluginV2TestCase):
|
||||
pass
|
74
quantum/tests/unit/brocade/test_brocade_vlan.py
Normal file
74
quantum/tests/unit/brocade/test_brocade_vlan.py
Normal file
@ -0,0 +1,74 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (c) 2013 OpenStack, LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
"""
|
||||
Test vlans alloc/dealloc.
|
||||
"""
|
||||
import unittest2 as unittest
|
||||
|
||||
from quantum.db import api as db
|
||||
from quantum.openstack.common import context
|
||||
from quantum.plugins.brocade import vlanbm as vlan_bitmap
|
||||
|
||||
|
||||
class TestVlanBitmap(unittest.TestCase):
|
||||
"""exercise Vlan bitmap ."""
|
||||
|
||||
def setUp(self):
|
||||
db.configure_db()
|
||||
self.context = context.get_admin_context()
|
||||
self.context.session = db.get_session()
|
||||
|
||||
def test_vlan(self):
|
||||
"""test vlan allocation/de-alloc."""
|
||||
|
||||
self.vbm_ = vlan_bitmap.VlanBitmap(self.context)
|
||||
vlan_id = self.vbm_.get_next_vlan(None)
|
||||
|
||||
# First vlan is always 2
|
||||
self.assertEqual(vlan_id, 2)
|
||||
|
||||
# next vlan is always 3
|
||||
vlan_id = self.vbm_.get_next_vlan(None)
|
||||
self.assertEqual(vlan_id, 3)
|
||||
|
||||
# get a specific vlan i.e. 4
|
||||
vlan_id = self.vbm_.get_next_vlan(4)
|
||||
self.assertEqual(vlan_id, 4)
|
||||
|
||||
# get a specific vlan i.e. 5
|
||||
vlan_id = self.vbm_.get_next_vlan(5)
|
||||
self.assertEqual(vlan_id, 5)
|
||||
|
||||
# Skip 6
|
||||
|
||||
# get a specific vlan i.e. 7
|
||||
vlan_id = self.vbm_.get_next_vlan(7)
|
||||
self.assertEqual(vlan_id, 7)
|
||||
|
||||
# get a specific vlan i.e. 1900
|
||||
vlan_id = self.vbm_.get_next_vlan(1900)
|
||||
self.assertEqual(vlan_id, 1900)
|
||||
|
||||
# Release 4 and get next again
|
||||
self.vbm_.release_vlan(4)
|
||||
vlan_id = self.vbm_.get_next_vlan(None)
|
||||
self.assertEqual(vlan_id, 4)
|
||||
|
||||
def tearDown(self):
|
||||
db.clear_db()
|
3
setup.py
3
setup.py
@ -45,6 +45,7 @@ init_path = 'etc/init.d'
|
||||
rootwrap_path = 'etc/quantum/rootwrap.d'
|
||||
ovs_plugin_config_path = 'etc/quantum/plugins/openvswitch'
|
||||
bigswitch_plugin_config_path = 'etc/quantum/plugins/bigswitch'
|
||||
brocade_plugin_config_path = 'etc/quantum/plugins/brocade'
|
||||
cisco_plugin_config_path = 'etc/quantum/plugins/cisco'
|
||||
linuxbridge_plugin_config_path = 'etc/quantum/plugins/linuxbridge'
|
||||
nvp_plugin_config_path = 'etc/quantum/plugins/nicira'
|
||||
@ -94,6 +95,8 @@ else:
|
||||
'etc/quantum/plugins/cisco/db_conn.ini']),
|
||||
(bigswitch_plugin_config_path,
|
||||
['etc/quantum/plugins/bigswitch/restproxy.ini']),
|
||||
(brocade_plugin_config_path,
|
||||
['etc/quantum/plugins/brocade/brocade.ini']),
|
||||
(linuxbridge_plugin_config_path,
|
||||
['etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini']),
|
||||
(nvp_plugin_config_path,
|
||||
|
Loading…
Reference in New Issue
Block a user