diff --git a/etc/quantum/plugins/cisco/credentials.ini b/etc/quantum/plugins/cisco/credentials.ini index 4eef1d9923..a33e16c35b 100644 --- a/etc/quantum/plugins/cisco/credentials.ini +++ b/etc/quantum/plugins/cisco/credentials.ini @@ -4,7 +4,13 @@ username= password= #Provide the Nexus credentials, if you are using Nexus -[1.1.1.1] -username=abc -password=def +[] +username= +password= +# Provide credentials and endpoint +# for keystone here +[keystone] +auth_url= +username= +password= diff --git a/etc/quantum/plugins/cisco/l2network_plugin.ini b/etc/quantum/plugins/cisco/l2network_plugin.ini index 848ab21836..501ff3814e 100644 --- a/etc/quantum/plugins/cisco/l2network_plugin.ini +++ b/etc/quantum/plugins/cisco/l2network_plugin.ini @@ -18,3 +18,7 @@ model_class=quantum.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchMod [SEGMENTATION] manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr + +# IMPORTANT: Comment the following lines for production deployments +[TEST] +host=testhost diff --git a/etc/quantum/plugins/cisco/nexus.ini b/etc/quantum/plugins/cisco/nexus.ini index 54bb967e01..b01cd0aa81 100644 --- a/etc/quantum/plugins/cisco/nexus.ini +++ b/etc/quantum/plugins/cisco/nexus.ini @@ -1,10 +1,13 @@ [SWITCH] -# Change the following to reflect the Nexus switch details -nexus_ip_address= -#Interfaces connected from the Nexus Switch to the compute hosts ports, e.g.: 1/10 and 1/11 -ports= -#Port number where the SSH will be running at the Nexus Switch, e.g.: 22 (Default) -nexus_ssh_port=22 +# Ip address of the switch +[[]] +# Hostname of the node +[[[]]] +# Port this node is connected to on the nexus switch +ports= +# Port number where the SSH will be running at the Nexus Switch, e.g.: 22 (Default) +[[[ssh_port]]] +ssh_port= [DRIVER] #name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver_v2.CiscoNEXUSDriver diff --git a/quantum/plugins/cisco/db/nexus_db_v2.py b/quantum/plugins/cisco/db/nexus_db_v2.py index a61ffc4ae5..b5f35dd8fa 100644 --- a/quantum/plugins/cisco/db/nexus_db_v2.py +++ b/quantum/plugins/cisco/db/nexus_db_v2.py @@ -13,18 +13,22 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +# # @author: Rohit Agarwalla, Cisco Systems, Inc. - -import logging as LOG +# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com) +# from sqlalchemy.orm import exc 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.db import nexus_models_v2 +LOG = logging.getLogger(__name__) + + def get_all_nexusport_bindings(): """Lists all the nexusport bindings""" LOG.debug("get_all_nexusport_bindings() called") @@ -36,35 +40,54 @@ def get_all_nexusport_bindings(): return [] -def get_nexusport_binding(vlan_id): +def get_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): """Lists a nexusport binding""" LOG.debug("get_nexusport_binding() called") session = db.get_session() try: 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 except exc.NoResultFound: 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""" LOG.debug("add_nexusport_binding() called") 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.flush() return binding -def remove_nexusport_binding(vlan_id): +def remove_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): """Removes a nexusport binding""" LOG.debug("remove_nexusport_binding() called") session = db.get_session() try: 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: session.delete(bind) session.flush() @@ -87,3 +110,29 @@ def update_nexusport_binding(port_id, new_vlan_id): return binding except exc.NoResultFound: 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) diff --git a/quantum/plugins/cisco/db/nexus_models_v2.py b/quantum/plugins/cisco/db/nexus_models_v2.py index 975d270cd9..e1cfd4b3c9 100644 --- a/quantum/plugins/cisco/db/nexus_models_v2.py +++ b/quantum/plugins/cisco/db/nexus_models_v2.py @@ -22,16 +22,21 @@ from quantum.plugins.cisco.db.l2network_models import L2NetworkBase class NexusPortBinding(model_base.BASEV2, L2NetworkBase): - """Represents a binding of nexus port to vlan_id""" - __tablename__ = 'nexusport_bindings' + """Represents a binding of VM's to nexus ports""" + __tablename__ = "nexusport_bindings" id = Column(Integer, primary_key=True, autoincrement=True) port_id = Column(String(255)) 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.vlan_id = vlan_id + self.switch_ip = switch_ip + self.instance_id = instance_id def __repr__(self): - return "" % (self.port_id, self.vlan_id) + return "" % \ + (self.port_id, self.vlan_id, self.switch_ip, self.instance_id) diff --git a/quantum/plugins/cisco/l2network_plugin_configuration.py b/quantum/plugins/cisco/l2network_plugin_configuration.py index fdd4191bc5..a1c35f059a 100644 --- a/quantum/plugins/cisco/l2network_plugin_configuration.py +++ b/quantum/plugins/cisco/l2network_plugin_configuration.py @@ -43,6 +43,9 @@ MAX_NETWORKS = SECTION_CONF['max_networks'] SECTION_CONF = CONF_PARSER_OBJ['MODEL'] 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") SECTION_CONF = CONF_PARSER_OBJ['SEGMENTATION'] @@ -51,7 +54,6 @@ MANAGER_CLASS = SECTION_CONF['manager_class'] CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE) - # Read the config for the device plugins PLUGINS = CONF_PARSER_OBJ.walk(CONF_PARSER_OBJ.dummy) diff --git a/quantum/plugins/cisco/models/virt_phy_sw_v2.py b/quantum/plugins/cisco/models/virt_phy_sw_v2.py index b46aa4bc17..49c26b5a33 100644 --- a/quantum/plugins/cisco/models/virt_phy_sw_v2.py +++ b/quantum/plugins/cisco/models/virt_phy_sw_v2.py @@ -21,6 +21,10 @@ from copy import deepcopy import inspect 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.openstack.common import importutils 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 import quantum_plugin_base_v2 - LOG = logging.getLogger(__name__) @@ -46,11 +49,11 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): _plugins = {} _inventory = {} _methods_to_delegate = ['get_network', 'get_networks', - 'create_port', 'create_port_bulk', 'delete_port', - 'update_port', 'get_port', 'get_ports', + 'create_port_bulk', 'update_port', + 'get_port', 'get_ports', 'create_subnet', 'create_subnet_bulk', - 'delete_subnet', 'update_subnet', 'get_subnet', - 'get_subnets', ] + 'delete_subnet', 'update_subnet', + 'get_subnet', 'get_subnets', ] def __init__(self): """ @@ -181,6 +184,25 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): else: 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): """ 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'], ovs_output[0]['id'], vlan_name, vlan_id, {'vlan_ids': vlanids}] - nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - args) return ovs_output[0] except: # 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) vlanids = self._get_all_segmentation_ids() 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 except: # TODO (Sumit): Check if we need to perform any rollback here @@ -289,8 +300,41 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): pass 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): """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""" pass - def delete_port(self, context, id, kwargs): - """For this model this method will be delegated to vswitch plugin""" - pass + def delete_port(self, context, id): + """ + 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): """For this model this method will be delegated to vswitch plugin""" diff --git a/quantum/plugins/cisco/nexus/cisco_nexus_configuration.py b/quantum/plugins/cisco/nexus/cisco_nexus_configuration.py index 4d648d93dd..d862a39a14 100644 --- a/quantum/plugins/cisco/nexus/cisco_nexus_configuration.py +++ b/quantum/plugins/cisco/nexus/cisco_nexus_configuration.py @@ -30,10 +30,7 @@ from quantum.plugins.cisco.common import cisco_configparser as confp CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'}, "nexus.ini")) -SECTION = CP['SWITCH'] -NEXUS_IP_ADDRESS = SECTION['nexus_ip_address'] -NEXUS_PORTS = SECTION['ports'] -NEXUS_SSH_PORT = SECTION['nexus_ssh_port'] +NEXUS_DETAILS = CP['SWITCH'] SECTION = CP['DRIVER'] NEXUS_DRIVER = SECTION['name'] diff --git a/quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py b/quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py index 6f30c80bf6..0e10f025bd 100644 --- a/quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py +++ b/quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py @@ -21,6 +21,7 @@ Implements a Nexus-OS NETCONF over SSHv2 API Client """ +import eventlet import logging from ncclient import manager @@ -116,14 +117,14 @@ class CiscoNEXUSDriver(): Creates a VLAN and Enable on trunk mode an interface on Nexus Switch given the VLAN ID and Name and Interface Number """ - with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user, - nexus_password) as man: - self.enable_vlan(man, vlan_id, vlan_name) - if vlan_ids is '': - vlan_ids = self.build_vlans_cmd() - LOG.debug("NexusDriver VLAN IDs: %s" % vlan_ids) - for ports in nexus_ports: - self.enable_vlan_on_trunk_int(man, ports, vlan_ids) + man = self.nxos_connect(nexus_host, int(nexus_ssh_port), + nexus_user, nexus_password) + self.enable_vlan(man, vlan_id, vlan_name) + if vlan_ids is '': + vlan_ids = self.build_vlans_cmd() + LOG.debug("NexusDriver VLAN IDs: %s" % vlan_ids) + for ports in nexus_ports: + self.enable_vlan_on_trunk_int(man, ports, vlan_ids) def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password, nexus_ports, nexus_ssh_port): @@ -131,11 +132,11 @@ class CiscoNEXUSDriver(): Delete a VLAN and Disables trunk mode an interface on Nexus Switch given the VLAN ID and Interface Number """ - with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user, - nexus_password) as man: - self.disable_vlan(man, vlan_id) - for ports in nexus_ports: - self.disable_vlan_on_trunk_int(man, ports, vlan_id) + man = self.nxos_connect(nexus_host, int(nexus_ssh_port), + nexus_user, nexus_password) + self.disable_vlan(man, vlan_id) + for ports in nexus_ports: + self.disable_vlan_on_trunk_int(man, ports, vlan_id) def build_vlans_cmd(self): """ @@ -154,19 +155,19 @@ class CiscoNEXUSDriver(): """ 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, - nexus_password) as man: - if not vlan_ids: - vlan_ids = self.build_vlans_cmd() - for ports in nexus_ports: - self.enable_vlan_on_trunk_int(man, ports, vlan_ids) + man = self.nxos_connect(nexus_host, int(nexus_ssh_port), + nexus_user, nexus_password) + if not vlan_ids: + vlan_ids = self.build_vlans_cmd() + for ports in nexus_ports: + self.enable_vlan_on_trunk_int(man, ports, vlan_ids) def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password, nexus_ports, nexus_ssh_port): """ 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, - nexus_password) as man: - for ports in nexus_ports: - self.disable_vlan_on_trunk_int(man, ports, vlan_id) + man = self.nxos_connect(nexus_host, int(nexus_ssh_port), + nexus_user, nexus_password) + for ports in nexus_ports: + self.disable_vlan_on_trunk_int(man, ports, vlan_id) diff --git a/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py b/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py index 447faf466b..57bc3c3a22 100644 --- a/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py +++ b/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py @@ -16,6 +16,7 @@ # # @author: Sumit Naiksatam, Cisco Systems, Inc. # @author: Edgar Magana, Cisco Systems, Inc. +# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com) # """ PlugIn for Nexus OS driver @@ -48,11 +49,18 @@ class NexusPlugin(L2DevicePluginBase): """ self._client = importutils.import_object(conf.NEXUS_DRIVER) LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER) - self._nexus_ip = conf.NEXUS_IP_ADDRESS - self._nexus_username = cred.Store.get_username(conf.NEXUS_IP_ADDRESS) - self._nexus_password = cred.Store.get_password(conf.NEXUS_IP_ADDRESS) - self._nexus_ports = conf.NEXUS_PORTS - self._nexus_ssh_port = conf.NEXUS_SSH_PORT + self._nexus_switches = conf.NEXUS_DETAILS + self.credentials = {} + + def get_credential(self, nexus_ip): + 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): """ @@ -64,26 +72,52 @@ class NexusPlugin(L2DevicePluginBase): return self._networks.values() 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 """ LOG.debug("NexusPlugin:create_network() called\n") - vlan_ids = '' - for key in kwargs: - if key == 'vlan_ids': - vlan_ids = kwargs['vlan_ids'] - self._client.create_vlan( - vlan_name, str(vlan_id), self._nexus_ip, - self._nexus_username, self._nexus_password, - self._nexus_ports, self._nexus_ssh_port, vlan_ids) - for ports in self._nexus_ports: - try: - nxos_db.add_nexusport_binding(ports, str(vlan_id)) - except: - raise excep.NexusPortBindingAlreadyExists(port_id=ports) + # Grab the switch IP and port for this host + switch_ip = '' + port_id = '' + for switch in self._nexus_switches.keys(): + for hostname in self._nexus_switches[switch].keys(): + if str(hostname) == str(host): + switch_ip = switch + port_id = self._nexus_switches[switch][hostname]['ports'] + # Check if this network is already in the DB + binding = nxos_db.get_port_vlan_switch_binding( + port_id, vlan_id, switch_ip) + if not binding: + _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, const.NET_NAME: net_name, const.NET_PORTS: {}, @@ -94,32 +128,10 @@ class NexusPlugin(L2DevicePluginBase): 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 """ 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): """ @@ -135,22 +147,6 @@ class NexusPlugin(L2DevicePluginBase): Virtual Network. """ 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): """ @@ -166,12 +162,38 @@ class NexusPlugin(L2DevicePluginBase): """ 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 if not required. + Delete port bindings from the database and scan + whether the network is still required on + the interfaces trunked """ 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): """ diff --git a/quantum/tests/unit/cisco/test_nexus_plugin.py b/quantum/tests/unit/cisco/test_nexus_plugin.py index 74ab64fb93..f4d9d206d8 100644 --- a/quantum/tests/unit/cisco/test_nexus_plugin.py +++ b/quantum/tests/unit/cisco/test_nexus_plugin.py @@ -27,7 +27,9 @@ from quantum.plugins.cisco.nexus import cisco_nexus_plugin_v2 NEXUS_IP_ADDRESS = '1.1.1.1' NEXUS_USERNAME = 'username' NEXUS_PASSWORD = 'password' -NEXUS_PORTS = ['1/10'] +HOSTNAME = 'testhost' +INSTANCE = 'testvm' +NEXUS_PORTS = '1/10' NEXUS_SSH_PORT = '22' NEXUS_DRIVER = ('quantum.plugins.cisco.tests.unit.v2.nexus.' 'fake_nexus_driver.CiscoNEXUSFakeDriver') @@ -48,6 +50,17 @@ class TestCiscoNexusPlugin(unittest.TestCase): self.second_net_id = 000005 self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan" 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(): db.configure_db() @@ -59,14 +72,21 @@ class TestCiscoNexusPlugin(unittest.TestCase): self._nexus_password = NEXUS_PASSWORD self._nexus_ports = NEXUS_PORTS 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): cdb.initialize() with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin, '__init__', new=new_nexus_init): 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 deletion of one Virtual Network. @@ -90,18 +110,16 @@ class TestCiscoNexusPlugin(unittest.TestCase): 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_name, vlan_id, self._hostname, INSTANCE) 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_VLAN_NAME], self.vlan_name) 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( tenant_id, second_net_name, second_net_id, - second_vlan_name, second_vlan_id, - vlan_ids=vlan_ids) + second_vlan_name, second_vlan_id, self._hostname, + INSTANCE) self.assertEqual(new_net_dict[const.NET_ID], second_net_id) 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.assertEqual(new_net_dict[const.NET_VLAN_ID], self.second_vlan_id) - expected_net_id = self._cisco_nexus_plugin.delete_network( - tenant_id, net_id, vlan_id=str(vlan_id)) - - self.assertEqual(expected_net_id, net_id) - - def test_b_nexus_clear_vlan(self): + def test_b_nexus_delete_port(self): """ Test to clean up second vlan of nexus device created by test_create_delete_network. This test will fail if it is run individually. """ - tenant_id = self.tenant_id - second_net_id = self.second_net_id - second_vlan_id = self.second_vlan_id + expected_instance_id = self._cisco_nexus_plugin.delete_port( + INSTANCE, self.second_vlan_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_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() + self.assertEqual(expected_instance_id, INSTANCE) def tearDown(self): """Clear the test environment""" + pass # Remove database contents - db.clear_db(network_models_v2.model_base.BASEV2) + #db.clear_db(network_models_v2.model_base.BASEV2) diff --git a/tools/pip-requires b/tools/pip-requires index 331b5beb9b..c9c40cbc0d 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -17,3 +17,7 @@ sqlalchemy==0.7.9 webob>=1.0.8 python-keystoneclient>=0.2.0 alembic>=0.4.1 + +# Cisco plugin dependencies +python-novaclient +# End Cisco dependencies