Merge "Adding multi switch support to the Cisco Nexus plugin"

This commit is contained in:
Jenkins 2013-01-15 17:59:45 +00:00 committed by Gerrit Code Review
commit 94208514cf
12 changed files with 320 additions and 233 deletions

View File

@ -4,7 +4,13 @@ username=<put_user_name_here>
password=<put_password_here> password=<put_password_here>
#Provide the Nexus credentials, if you are using Nexus #Provide the Nexus credentials, if you are using Nexus
[1.1.1.1] [<put_nexus_switch_ip_address_here>]
username=abc username=<put_user_name_here>
password=def password=<put_password_here>
# Provide credentials and endpoint
# for keystone here
[keystone]
auth_url=<put_keystone_endpoint_here>
username=<put_user_name_here>
password=<put_password_here>

View File

@ -18,3 +18,7 @@ model_class=quantum.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchMod
[SEGMENTATION] [SEGMENTATION]
manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr
# IMPORTANT: Comment the following lines for production deployments
[TEST]
host=testhost

View File

@ -1,10 +1,13 @@
[SWITCH] [SWITCH]
# Change the following to reflect the Nexus switch details # Ip address of the switch
nexus_ip_address=<put_nexus_switch_ip_address_here> [[<put_nexus_switch_ip_address_here>]]
#Interfaces connected from the Nexus Switch to the compute hosts ports, e.g.: 1/10 and 1/11 # Hostname of the node
ports=<put_interfaces_names_here_separated_by_commas> [[[<put_hostname_here>]]]
#Port number where the SSH will be running at the Nexus Switch, e.g.: 22 (Default) # Port this node is connected to on the nexus switch
nexus_ssh_port=22 ports=<put_port_id_here>
# Port number where the SSH will be running at the Nexus Switch, e.g.: 22 (Default)
[[[ssh_port]]]
ssh_port=<put_port_number_here>
[DRIVER] [DRIVER]
#name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver_v2.CiscoNEXUSDriver #name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver_v2.CiscoNEXUSDriver

View File

@ -13,18 +13,22 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
#
# @author: Rohit Agarwalla, Cisco Systems, Inc. # @author: Rohit Agarwalla, Cisco Systems, Inc.
# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com)
import logging as LOG #
from sqlalchemy.orm import exc from sqlalchemy.orm import exc
import quantum.db.api as db import quantum.db.api as db
from quantum.openstack.common import log as logging
from quantum.plugins.cisco.common import cisco_exceptions as c_exc from quantum.plugins.cisco.common import cisco_exceptions as c_exc
from quantum.plugins.cisco.db import nexus_models_v2 from quantum.plugins.cisco.db import nexus_models_v2
LOG = logging.getLogger(__name__)
def get_all_nexusport_bindings(): def get_all_nexusport_bindings():
"""Lists all the nexusport bindings""" """Lists all the nexusport bindings"""
LOG.debug("get_all_nexusport_bindings() called") LOG.debug("get_all_nexusport_bindings() called")
@ -36,35 +40,54 @@ def get_all_nexusport_bindings():
return [] return []
def get_nexusport_binding(vlan_id): def get_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
"""Lists a nexusport binding""" """Lists a nexusport binding"""
LOG.debug("get_nexusport_binding() called") LOG.debug("get_nexusport_binding() called")
session = db.get_session() session = db.get_session()
try: try:
binding = (session.query(nexus_models_v2.NexusPortBinding). binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).all()) filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
filter_by(port_id=port_id).
filter_by(instance_id=instance_id).all())
return binding return binding
except exc.NoResultFound: except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id) raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
def add_nexusport_binding(port_id, vlan_id): def get_nexusvlan_binding(vlan_id, switch_ip):
"""Lists a vlan and switch binding"""
LOG.debug("get_nexusvlan_binding() called")
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
all())
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
def add_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
"""Adds a nexusport binding""" """Adds a nexusport binding"""
LOG.debug("add_nexusport_binding() called") LOG.debug("add_nexusport_binding() called")
session = db.get_session() session = db.get_session()
binding = nexus_models_v2.NexusPortBinding(port_id, vlan_id) binding = nexus_models_v2.NexusPortBinding(
port_id, vlan_id, switch_ip, instance_id)
session.add(binding) session.add(binding)
session.flush() session.flush()
return binding return binding
def remove_nexusport_binding(vlan_id): def remove_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
"""Removes a nexusport binding""" """Removes a nexusport binding"""
LOG.debug("remove_nexusport_binding() called") LOG.debug("remove_nexusport_binding() called")
session = db.get_session() session = db.get_session()
try: try:
binding = (session.query(nexus_models_v2.NexusPortBinding). binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).all()) filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
filter_by(port_id=port_id).
filter_by(instance_id=instance_id).all())
for bind in binding: for bind in binding:
session.delete(bind) session.delete(bind)
session.flush() session.flush()
@ -87,3 +110,29 @@ def update_nexusport_binding(port_id, new_vlan_id):
return binding return binding
except exc.NoResultFound: except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound() raise c_exc.NexusPortBindingNotFound()
def get_nexusvm_binding(vlan_id, instance_id):
"""Lists nexusvm bindings"""
LOG.debug("get_nexusvm_binding() called")
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(instance_id=instance_id).
filter_by(vlan_id=vlan_id).first())
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
"""Lists nexusvm bindings"""
LOG.debug("get_port_vlan_switch_binding() called")
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(port_id=port_id).filter_by(switch_ip=switch_ip).
filter_by(vlan_id=vlan_id).all())
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)

View File

@ -22,16 +22,21 @@ from quantum.plugins.cisco.db.l2network_models import L2NetworkBase
class NexusPortBinding(model_base.BASEV2, L2NetworkBase): class NexusPortBinding(model_base.BASEV2, L2NetworkBase):
"""Represents a binding of nexus port to vlan_id""" """Represents a binding of VM's to nexus ports"""
__tablename__ = 'nexusport_bindings' __tablename__ = "nexusport_bindings"
id = Column(Integer, primary_key=True, autoincrement=True) id = Column(Integer, primary_key=True, autoincrement=True)
port_id = Column(String(255)) port_id = Column(String(255))
vlan_id = Column(Integer, nullable=False) vlan_id = Column(Integer, nullable=False)
switch_ip = Column(String(255))
instance_id = Column(String(255))
def __init__(self, port_id, vlan_id): def __init__(self, port_id, vlan_id, switch_ip, instance_id):
self.port_id = port_id self.port_id = port_id
self.vlan_id = vlan_id self.vlan_id = vlan_id
self.switch_ip = switch_ip
self.instance_id = instance_id
def __repr__(self): def __repr__(self):
return "<NexusPortBinding (%s,%d)>" % (self.port_id, self.vlan_id) return "<NexusPortBinding (%s,%d, %s, %s)>" % \
(self.port_id, self.vlan_id, self.switch_ip, self.instance_id)

View File

@ -43,6 +43,9 @@ MAX_NETWORKS = SECTION_CONF['max_networks']
SECTION_CONF = CONF_PARSER_OBJ['MODEL'] SECTION_CONF = CONF_PARSER_OBJ['MODEL']
MODEL_CLASS = SECTION_CONF['model_class'] MODEL_CLASS = SECTION_CONF['model_class']
if 'TEST' in CONF_PARSER_OBJ.keys():
TEST = CONF_PARSER_OBJ['TEST']
CONF_FILE = find_config_file({'plugin': 'cisco'}, "cisco_plugins.ini") CONF_FILE = find_config_file({'plugin': 'cisco'}, "cisco_plugins.ini")
SECTION_CONF = CONF_PARSER_OBJ['SEGMENTATION'] SECTION_CONF = CONF_PARSER_OBJ['SEGMENTATION']
@ -51,7 +54,6 @@ MANAGER_CLASS = SECTION_CONF['manager_class']
CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE) CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE)
# Read the config for the device plugins # Read the config for the device plugins
PLUGINS = CONF_PARSER_OBJ.walk(CONF_PARSER_OBJ.dummy) PLUGINS = CONF_PARSER_OBJ.walk(CONF_PARSER_OBJ.dummy)

View File

@ -21,6 +21,10 @@ from copy import deepcopy
import inspect import inspect
import logging import logging
from keystoneclient.v2_0 import client as keystone_client
from novaclient.v1_1 import client as nova_client
from quantum.db import l3_db
from quantum.manager import QuantumManager from quantum.manager import QuantumManager
from quantum.openstack.common import importutils from quantum.openstack.common import importutils
from quantum.plugins.cisco.common import cisco_constants as const from quantum.plugins.cisco.common import cisco_constants as const
@ -30,7 +34,6 @@ from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.openvswitch import ovs_db_v2 as odb from quantum.plugins.openvswitch import ovs_db_v2 as odb
from quantum import quantum_plugin_base_v2 from quantum import quantum_plugin_base_v2
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -46,11 +49,11 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
_plugins = {} _plugins = {}
_inventory = {} _inventory = {}
_methods_to_delegate = ['get_network', 'get_networks', _methods_to_delegate = ['get_network', 'get_networks',
'create_port', 'create_port_bulk', 'delete_port', 'create_port_bulk', 'update_port',
'update_port', 'get_port', 'get_ports', 'get_port', 'get_ports',
'create_subnet', 'create_subnet_bulk', 'create_subnet', 'create_subnet_bulk',
'delete_subnet', 'update_subnet', 'get_subnet', 'delete_subnet', 'update_subnet',
'get_subnets', ] 'get_subnet', 'get_subnets', ]
def __init__(self): def __init__(self):
""" """
@ -181,6 +184,25 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
else: else:
return False return False
def _get_instance_host(self, tenant_id, instance_id):
keystone = cred._creds_dictionary['keystone']
kc = keystone_client.Client(username=keystone['username'],
password=keystone['password'],
tenant_id=tenant_id,
auth_url=keystone['auth_url'])
tenant = kc.tenants.get(tenant_id)
tenant_name = tenant.name
nc = nova_client.Client(keystone['username'],
keystone['password'],
tenant_name,
keystone['auth_url'],
no_cache=True)
serv = nc.servers.get(instance_id)
host = serv.__getattr__('OS-EXT-SRV-ATTR:host')
return host
def create_network(self, context, network): def create_network(self, context, network):
""" """
Perform this operation in the context of the configured device Perform this operation in the context of the configured device
@ -200,9 +222,6 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
args = [ovs_output[0]['tenant_id'], ovs_output[0]['name'], args = [ovs_output[0]['tenant_id'], ovs_output[0]['name'],
ovs_output[0]['id'], vlan_name, vlan_id, ovs_output[0]['id'], vlan_name, vlan_id,
{'vlan_ids': vlanids}] {'vlan_ids': vlanids}]
nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(),
args)
return ovs_output[0] return ovs_output[0]
except: except:
# TODO (Sumit): Check if we need to perform any rollback here # TODO (Sumit): Check if we need to perform any rollback here
@ -221,14 +240,6 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
LOG.debug("ovs_output: %s\n " % ovs_output) LOG.debug("ovs_output: %s\n " % ovs_output)
vlanids = self._get_all_segmentation_ids() vlanids = self._get_all_segmentation_ids()
ovs_networks = ovs_output ovs_networks = ovs_output
for ovs_network in ovs_networks:
vlan_id = self._get_segmentation_id(ovs_network['id'])
vlan_name = conf.VLAN_NAME_PREFIX + str(vlan_id)
args = [ovs_network['tenant_id'], ovs_network['name'],
ovs_network['id'], vlan_name, vlan_id,
{'vlan_ids': vlanids}]
nexus_output = self._invoke_plugin_per_device(
const.NEXUS_PLUGIN, "create_network", args)
return ovs_output return ovs_output
except: except:
# TODO (Sumit): Check if we need to perform any rollback here # TODO (Sumit): Check if we need to perform any rollback here
@ -289,8 +300,41 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
pass pass
def create_port(self, context, port): def create_port(self, context, port):
"""For this model this method will be delegated to vswitch plugin""" """
pass Perform this operation in the context of the configured device
plugins.
"""
LOG.debug("create_port() called\n")
try:
args = [context, port]
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
self._func_name(),
args)
net_id = port['port']['network_id']
instance_id = port['port']['device_id']
tenant_id = port['port']['tenant_id']
net_dict = self.get_network(context, net_id)
net_name = net_dict['name']
vlan_id = self._get_segmentation_id(net_id)
host = ''
if hasattr(conf, 'TEST'):
host = conf.TEST['host']
else:
host = self._get_instance_host(tenant_id, instance_id)
# Trunk segmentation id for only this host
vlan_name = conf.VLAN_NAME_PREFIX + str(vlan_id)
n_args = [tenant_id, net_name, net_id,
vlan_name, vlan_id, host, instance_id]
nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
'create_network',
n_args)
return ovs_output[0]
except:
# TODO (asomya): Check if we need to perform any rollback here
raise
def get_port(self, context, id, fields=None): def get_port(self, context, id, fields=None):
"""For this model this method will be delegated to vswitch plugin""" """For this model this method will be delegated to vswitch plugin"""
@ -304,9 +348,27 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
"""For this model this method will be delegated to vswitch plugin""" """For this model this method will be delegated to vswitch plugin"""
pass pass
def delete_port(self, context, id, kwargs): def delete_port(self, context, id):
"""For this model this method will be delegated to vswitch plugin""" """
pass Perform this operation in the context of the configured device
plugins.
"""
LOG.debug("delete_port() called\n")
try:
args = [context, id]
port = self.get_port(context, id)
vlan_id = self._get_segmentation_id(port['network_id'])
n_args = [port['device_id'], vlan_id]
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
self._func_name(),
args)
nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(),
n_args)
return ovs_output[0]
except:
# TODO (asomya): Check if we need to perform any rollback here
raise
def create_subnet(self, context, subnet): def create_subnet(self, context, subnet):
"""For this model this method will be delegated to vswitch plugin""" """For this model this method will be delegated to vswitch plugin"""

View File

@ -30,10 +30,7 @@ from quantum.plugins.cisco.common import cisco_configparser as confp
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'}, CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'},
"nexus.ini")) "nexus.ini"))
SECTION = CP['SWITCH'] NEXUS_DETAILS = CP['SWITCH']
NEXUS_IP_ADDRESS = SECTION['nexus_ip_address']
NEXUS_PORTS = SECTION['ports']
NEXUS_SSH_PORT = SECTION['nexus_ssh_port']
SECTION = CP['DRIVER'] SECTION = CP['DRIVER']
NEXUS_DRIVER = SECTION['name'] NEXUS_DRIVER = SECTION['name']

View File

@ -21,6 +21,7 @@
Implements a Nexus-OS NETCONF over SSHv2 API Client Implements a Nexus-OS NETCONF over SSHv2 API Client
""" """
import eventlet
import logging import logging
from ncclient import manager from ncclient import manager
@ -116,14 +117,14 @@ class CiscoNEXUSDriver():
Creates a VLAN and Enable on trunk mode an interface on Nexus Switch Creates a VLAN and Enable on trunk mode an interface on Nexus Switch
given the VLAN ID and Name and Interface Number given the VLAN ID and Name and Interface Number
""" """
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user, man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_password) as man: nexus_user, nexus_password)
self.enable_vlan(man, vlan_id, vlan_name) self.enable_vlan(man, vlan_id, vlan_name)
if vlan_ids is '': if vlan_ids is '':
vlan_ids = self.build_vlans_cmd() vlan_ids = self.build_vlans_cmd()
LOG.debug("NexusDriver VLAN IDs: %s" % vlan_ids) LOG.debug("NexusDriver VLAN IDs: %s" % vlan_ids)
for ports in nexus_ports: for ports in nexus_ports:
self.enable_vlan_on_trunk_int(man, ports, vlan_ids) self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password, def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ports, nexus_ssh_port): nexus_ports, nexus_ssh_port):
@ -131,11 +132,11 @@ class CiscoNEXUSDriver():
Delete a VLAN and Disables trunk mode an interface on Nexus Switch Delete a VLAN and Disables trunk mode an interface on Nexus Switch
given the VLAN ID and Interface Number given the VLAN ID and Interface Number
""" """
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user, man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_password) as man: nexus_user, nexus_password)
self.disable_vlan(man, vlan_id) self.disable_vlan(man, vlan_id)
for ports in nexus_ports: for ports in nexus_ports:
self.disable_vlan_on_trunk_int(man, ports, vlan_id) self.disable_vlan_on_trunk_int(man, ports, vlan_id)
def build_vlans_cmd(self): def build_vlans_cmd(self):
""" """
@ -154,19 +155,19 @@ class CiscoNEXUSDriver():
""" """
Adds a vlan from interfaces on the Nexus switch given the VLAN ID Adds a vlan from interfaces on the Nexus switch given the VLAN ID
""" """
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user, man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_password) as man: nexus_user, nexus_password)
if not vlan_ids: if not vlan_ids:
vlan_ids = self.build_vlans_cmd() vlan_ids = self.build_vlans_cmd()
for ports in nexus_ports: for ports in nexus_ports:
self.enable_vlan_on_trunk_int(man, ports, vlan_ids) self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password, def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ports, nexus_ssh_port): nexus_ports, nexus_ssh_port):
""" """
Removes a vlan from interfaces on the Nexus switch given the VLAN ID Removes a vlan from interfaces on the Nexus switch given the VLAN ID
""" """
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user, man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_password) as man: nexus_user, nexus_password)
for ports in nexus_ports: for ports in nexus_ports:
self.disable_vlan_on_trunk_int(man, ports, vlan_id) self.disable_vlan_on_trunk_int(man, ports, vlan_id)

View File

@ -16,6 +16,7 @@
# #
# @author: Sumit Naiksatam, Cisco Systems, Inc. # @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Edgar Magana, Cisco Systems, Inc. # @author: Edgar Magana, Cisco Systems, Inc.
# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com)
# #
""" """
PlugIn for Nexus OS driver PlugIn for Nexus OS driver
@ -48,11 +49,18 @@ class NexusPlugin(L2DevicePluginBase):
""" """
self._client = importutils.import_object(conf.NEXUS_DRIVER) self._client = importutils.import_object(conf.NEXUS_DRIVER)
LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER) LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER)
self._nexus_ip = conf.NEXUS_IP_ADDRESS self._nexus_switches = conf.NEXUS_DETAILS
self._nexus_username = cred.Store.get_username(conf.NEXUS_IP_ADDRESS) self.credentials = {}
self._nexus_password = cred.Store.get_password(conf.NEXUS_IP_ADDRESS)
self._nexus_ports = conf.NEXUS_PORTS def get_credential(self, nexus_ip):
self._nexus_ssh_port = conf.NEXUS_SSH_PORT if not nexus_ip in self.credentials.keys():
_nexus_username = cred.Store.get_username(nexus_ip)
_nexus_password = cred.Store.get_password(nexus_ip)
self.credentials[nexus_ip] = {
'username': _nexus_username,
'password': _nexus_password
}
return self.credentials[nexus_ip]
def get_all_networks(self, tenant_id): def get_all_networks(self, tenant_id):
""" """
@ -64,26 +72,52 @@ class NexusPlugin(L2DevicePluginBase):
return self._networks.values() return self._networks.values()
def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id,
**kwargs): host, instance):
""" """
Create a VLAN in the switch, and configure the appropriate interfaces Create a VLAN in the appropriate switch/port,
and configure the appropriate interfaces
for this VLAN for this VLAN
""" """
LOG.debug("NexusPlugin:create_network() called\n") LOG.debug("NexusPlugin:create_network() called\n")
vlan_ids = '' # Grab the switch IP and port for this host
for key in kwargs: switch_ip = ''
if key == 'vlan_ids': port_id = ''
vlan_ids = kwargs['vlan_ids'] for switch in self._nexus_switches.keys():
self._client.create_vlan( for hostname in self._nexus_switches[switch].keys():
vlan_name, str(vlan_id), self._nexus_ip, if str(hostname) == str(host):
self._nexus_username, self._nexus_password, switch_ip = switch
self._nexus_ports, self._nexus_ssh_port, vlan_ids) port_id = self._nexus_switches[switch][hostname]['ports']
for ports in self._nexus_ports: # Check if this network is already in the DB
try: binding = nxos_db.get_port_vlan_switch_binding(
nxos_db.add_nexusport_binding(ports, str(vlan_id)) port_id, vlan_id, switch_ip)
except: if not binding:
raise excep.NexusPortBindingAlreadyExists(port_id=ports) _nexus_ip = switch_ip
_nexus_ports = (port_id,)
_nexus_ssh_port = \
self._nexus_switches[switch_ip]['ssh_port']['ssh_port']
_nexus_creds = self.get_credential(_nexus_ip)
_nexus_username = _nexus_creds['username']
_nexus_password = _nexus_creds['password']
# Check for vlan/switch binding
vbinding = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
if not vbinding:
# Create vlan and trunk vlan on the port
self._client.create_vlan(
vlan_name, str(vlan_id), _nexus_ip,
_nexus_username, _nexus_password,
_nexus_ports, _nexus_ssh_port, vlan_id)
else:
# Only trunk vlan on the port
man = self._client.nxos_connect(_nexus_ip,
int(_nexus_ssh_port),
_nexus_username,
_nexus_password)
self._client.enable_vlan_on_trunk_int(man,
port_id,
vlan_id)
nxos_db.add_nexusport_binding(port_id, str(vlan_id),
switch_ip, instance)
new_net_dict = {const.NET_ID: net_id, new_net_dict = {const.NET_ID: net_id,
const.NET_NAME: net_name, const.NET_NAME: net_name,
const.NET_PORTS: {}, const.NET_PORTS: {},
@ -94,32 +128,10 @@ class NexusPlugin(L2DevicePluginBase):
def delete_network(self, tenant_id, net_id, **kwargs): def delete_network(self, tenant_id, net_id, **kwargs):
""" """
Deletes a VLAN in the switch, and removes the VLAN configuration Deletes the VLAN in all switches, and removes the VLAN configuration
from the relevant interfaces from the relevant interfaces
""" """
LOG.debug("NexusPlugin:delete_network() called\n") LOG.debug("NexusPlugin:delete_network() called\n")
vlan_id = None
for key in kwargs:
if key == const.CONTEXT:
context = kwargs[const.CONTEXT]
elif key == const.BASE_PLUGIN_REF:
base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
elif key == 'vlan_id':
vlan_id = kwargs['vlan_id']
if vlan_id is None:
vlan_id = self._get_vlan_id_for_network(tenant_id, net_id,
context, base_plugin_ref)
ports_id = nxos_db.get_nexusport_binding(vlan_id)
LOG.debug("NexusPlugin: Interfaces to be disassociated: %s" % ports_id)
nxos_db.remove_nexusport_binding(vlan_id)
if net_id:
self._client.delete_vlan(
str(vlan_id), self._nexus_ip,
self._nexus_username, self._nexus_password,
self._nexus_ports, self._nexus_ssh_port)
return net_id
# Network not found
raise exc.NetworkNotFound(net_id=net_id)
def get_network_details(self, tenant_id, net_id, **kwargs): def get_network_details(self, tenant_id, net_id, **kwargs):
""" """
@ -135,22 +147,6 @@ class NexusPlugin(L2DevicePluginBase):
Virtual Network. Virtual Network.
""" """
LOG.debug("NexusPlugin:update_network() called\n") LOG.debug("NexusPlugin:update_network() called\n")
if 'net_admin_state' in kwargs:
net_admin_state = kwargs['net_admin_state']
vlan_id = kwargs['vlan_id']
vlan_ids = kwargs['vlan_ids']
if not net_admin_state:
self._client.remove_vlan_int(
str(vlan_id), self._nexus_ip,
self._nexus_username, self._nexus_password,
self._nexus_ports, self._nexus_ssh_port)
else:
self._client.add_vlan_int(
str(vlan_id), self._nexus_ip,
self._nexus_username, self._nexus_password,
self._nexus_ports, self._nexus_ssh_port,
vlan_ids)
return net_id
def get_all_ports(self, tenant_id, net_id, **kwargs): def get_all_ports(self, tenant_id, net_id, **kwargs):
""" """
@ -166,12 +162,38 @@ class NexusPlugin(L2DevicePluginBase):
""" """
LOG.debug("NexusPlugin:create_port() called\n") LOG.debug("NexusPlugin:create_port() called\n")
def delete_port(self, tenant_id, net_id, port_id, **kwargs): def delete_port(self, device_id, vlan_id):
""" """
This is probably not applicable to the Nexus plugin. Delete port bindings from the database and scan
Delete if not required. whether the network is still required on
the interfaces trunked
""" """
LOG.debug("NexusPlugin:delete_port() called\n") LOG.debug("NexusPlugin:delete_port() called\n")
# Delete DB row for this port
row = nxos_db.get_nexusvm_binding(vlan_id, device_id)
if row:
nxos_db.remove_nexusport_binding(row['port_id'], row['vlan_id'],
row['switch_ip'],
row['instance_id'])
# Check for any other bindings with the same vlan_id and switch_ip
bindings = nxos_db.get_nexusvlan_binding(
row['vlan_id'], row['switch_ip'])
if not bindings:
# Delete this vlan from this switch
_nexus_ip = row['switch_ip']
_nexus_ports = (row['port_id'],)
_nexus_ssh_port = \
self._nexus_switches[_nexus_ip]['ssh_port']['ssh_port']
_nexus_creds = self.get_credential(_nexus_ip)
_nexus_username = _nexus_creds['username']
_nexus_password = _nexus_creds['password']
self._client.delete_vlan(
str(row['vlan_id']), _nexus_ip,
_nexus_username, _nexus_password,
_nexus_ports, _nexus_ssh_port)
return row['instance_id']
def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs): def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
""" """

View File

@ -27,7 +27,9 @@ from quantum.plugins.cisco.nexus import cisco_nexus_plugin_v2
NEXUS_IP_ADDRESS = '1.1.1.1' NEXUS_IP_ADDRESS = '1.1.1.1'
NEXUS_USERNAME = 'username' NEXUS_USERNAME = 'username'
NEXUS_PASSWORD = 'password' NEXUS_PASSWORD = 'password'
NEXUS_PORTS = ['1/10'] HOSTNAME = 'testhost'
INSTANCE = 'testvm'
NEXUS_PORTS = '1/10'
NEXUS_SSH_PORT = '22' NEXUS_SSH_PORT = '22'
NEXUS_DRIVER = ('quantum.plugins.cisco.tests.unit.v2.nexus.' NEXUS_DRIVER = ('quantum.plugins.cisco.tests.unit.v2.nexus.'
'fake_nexus_driver.CiscoNEXUSFakeDriver') 'fake_nexus_driver.CiscoNEXUSFakeDriver')
@ -48,6 +50,17 @@ class TestCiscoNexusPlugin(unittest.TestCase):
self.second_net_id = 000005 self.second_net_id = 000005
self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan" self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan"
self.second_vlan_id = 265 self.second_vlan_id = 265
self._nexus_switches = {
NEXUS_IP_ADDRESS: {
HOSTNAME: {
'ports': NEXUS_PORTS,
},
'ssh_port': {
'ssh_port': NEXUS_SSH_PORT
}
}
}
self._hostname = HOSTNAME
def new_cdb_init(): def new_cdb_init():
db.configure_db() db.configure_db()
@ -59,14 +72,21 @@ class TestCiscoNexusPlugin(unittest.TestCase):
self._nexus_password = NEXUS_PASSWORD self._nexus_password = NEXUS_PASSWORD
self._nexus_ports = NEXUS_PORTS self._nexus_ports = NEXUS_PORTS
self._nexus_ssh_port = NEXUS_SSH_PORT self._nexus_ssh_port = NEXUS_SSH_PORT
self.credentials = {
self._nexus_ip: {
'username': self._nexus_username,
'password': self._nexus_password
}
}
with mock.patch.object(cdb, 'initialize', new=new_cdb_init): with mock.patch.object(cdb, 'initialize', new=new_cdb_init):
cdb.initialize() cdb.initialize()
with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin, with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin,
'__init__', new=new_nexus_init): '__init__', new=new_nexus_init):
self._cisco_nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin() self._cisco_nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin()
self._cisco_nexus_plugin._nexus_switches = self._nexus_switches
def test_a_create_delete_network(self): def test_a_create_network(self):
""" """
Tests creation of two new Virtual Network. Tests creation of two new Virtual Network.
Tests deletion of one Virtual Network. Tests deletion of one Virtual Network.
@ -90,18 +110,16 @@ class TestCiscoNexusPlugin(unittest.TestCase):
new_net_dict = self._cisco_nexus_plugin.create_network( new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, net_name, net_id, tenant_id, net_name, net_id,
vlan_name, vlan_id, vlan_ids=str(vlan_id)) vlan_name, vlan_id, self._hostname, INSTANCE)
self.assertEqual(new_net_dict[const.NET_ID], net_id) self.assertEqual(new_net_dict[const.NET_ID], net_id)
self.assertEqual(new_net_dict[const.NET_NAME], self.net_name) self.assertEqual(new_net_dict[const.NET_NAME], self.net_name)
self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name) self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name)
self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id) self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id)
vlan_ids = str(vlan_id) + "," + str(second_vlan_id)
new_net_dict = self._cisco_nexus_plugin.create_network( new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, second_net_name, second_net_id, tenant_id, second_net_name, second_net_id,
second_vlan_name, second_vlan_id, second_vlan_name, second_vlan_id, self._hostname,
vlan_ids=vlan_ids) INSTANCE)
self.assertEqual(new_net_dict[const.NET_ID], second_net_id) self.assertEqual(new_net_dict[const.NET_ID], second_net_id)
self.assertEqual(new_net_dict[const.NET_NAME], self.second_net_name) self.assertEqual(new_net_dict[const.NET_NAME], self.second_net_name)
@ -109,106 +127,20 @@ class TestCiscoNexusPlugin(unittest.TestCase):
self.second_vlan_name) self.second_vlan_name)
self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.second_vlan_id) self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.second_vlan_id)
expected_net_id = self._cisco_nexus_plugin.delete_network( def test_b_nexus_delete_port(self):
tenant_id, net_id, vlan_id=str(vlan_id))
self.assertEqual(expected_net_id, net_id)
def test_b_nexus_clear_vlan(self):
""" """
Test to clean up second vlan of nexus device Test to clean up second vlan of nexus device
created by test_create_delete_network. This created by test_create_delete_network. This
test will fail if it is run individually. test will fail if it is run individually.
""" """
tenant_id = self.tenant_id expected_instance_id = self._cisco_nexus_plugin.delete_port(
second_net_id = self.second_net_id INSTANCE, self.second_vlan_id
second_vlan_id = self.second_vlan_id )
expected_second_net_id = self._cisco_nexus_plugin.delete_network( self.assertEqual(expected_instance_id, INSTANCE)
tenant_id, second_net_id,
vlan_id=str(second_vlan_id))
self.assertEqual(expected_second_net_id, second_net_id)
def test_c_update_network_False(self):
"""
Test to update a network state to False
resulting in disabling a vlan corresponding to
that network from the configured nexus interfaces
"""
tenant_id = self.tenant_id
net_name = self.net_name
net_id = self.net_id
vlan_name = self.vlan_name
vlan_id = self.vlan_id
second_net_name = self.second_net_name
second_net_id = self.second_net_id
second_vlan_name = self.second_vlan_name
second_vlan_id = self.second_vlan_id
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, net_name, net_id,
vlan_name, vlan_id, vlan_ids=str(vlan_id))
vlan_ids = str(vlan_id) + "," + str(second_vlan_id)
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, second_net_name, second_net_id,
second_vlan_name, second_vlan_id,
vlan_ids=vlan_ids)
expected_net_id = self._cisco_nexus_plugin.update_network(
tenant_id, net_id, net_admin_state=False,
vlan_id=vlan_id, vlan_ids=str(vlan_id))
self.assertEqual(expected_net_id, net_id)
def test_d_nexus_clean_vlan_update(self):
"""
Cleans up vlans on the nexus for the two
created networks
"""
tenant_id = self.tenant_id
net_id = self.net_id
vlan_id = self.vlan_id
second_net_id = self.second_net_id
second_vlan_id = self.second_vlan_id
netid = self._cisco_nexus_plugin.delete_network(
tenant_id, net_id, vlan_id=str(vlan_id))
self.assertEqual(netid, net_id)
expected_second_net_id = self._cisco_nexus_plugin.delete_network(
tenant_id, second_net_id,
vlan_id=str(second_vlan_id))
self.assertEqual(expected_second_net_id, second_net_id)
def test_e_update_network_True(self):
"""
Test to update a disabled network state to True
resulting in enabling a vlan corresponding to
that network to the configured nexus interfaces
"""
tenant_id = self.tenant_id
net_name = self.net_name
net_id = self.net_id
vlan_name = self.vlan_name
vlan_id = self.vlan_id
second_vlan_id = self.second_vlan_id
self.test_c_update_network_False()
vlan_ids = str(vlan_id) + "," + str(second_vlan_id)
expected_net_id = self._cisco_nexus_plugin.update_network(
tenant_id, net_id, net_admin_state=True,
vlan_id=vlan_id, vlan_ids=str(vlan_ids))
self.assertEqual(expected_net_id, net_id)
self.test_d_nexus_clean_vlan_update()
def tearDown(self): def tearDown(self):
"""Clear the test environment""" """Clear the test environment"""
pass
# Remove database contents # Remove database contents
db.clear_db(network_models_v2.model_base.BASEV2) #db.clear_db(network_models_v2.model_base.BASEV2)

View File

@ -17,3 +17,7 @@ sqlalchemy==0.7.9
webob>=1.0.8 webob>=1.0.8
python-keystoneclient>=0.2.0 python-keystoneclient>=0.2.0
alembic>=0.4.1 alembic>=0.4.1
# Cisco plugin dependencies
python-novaclient
# End Cisco dependencies