""" # vim: tabstop=4 shiftwidth=4 softtabstop=4 # # Copyright 2011 Cisco 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. # # @author: Sumit Naiksatam, Cisco Systems, Inc. # """ import inspect import logging as LOG from quantum.common import exceptions as exc from quantum.common import utils from quantum.quantum_plugin_base import QuantumPluginBase from quantum.plugins.cisco import l2network_plugin_configuration as conf from quantum.plugins.cisco.common import cisco_constants as const from quantum.plugins.cisco.common import cisco_exceptions as cexc from quantum.plugins.cisco.db import api as db from quantum.plugins.cisco.db import l2network_db as cdb LOG.basicConfig(level=LOG.WARN) LOG.getLogger(const.LOGGER_COMPONENT_NAME) class L2Network(QuantumPluginBase): """ L2 Network Framework Plugin """ def __init__(self): self._vlan_counter = int(conf.VLAN_START) - 1 self._model = utils.import_object(conf.MODEL_CLASS) cdb.initialize() # TODO (Sumit): The following should move to the segmentation module cdb.create_vlanids() """ Core API implementation """ def get_all_networks(self, tenant_id): """ Returns a dictionary containing all for the specified tenant. """ LOG.debug("get_all_networks() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id]) networks_list = db.network_list(tenant_id) new_networks_list = [] for network in networks_list: new_network_dict = self._make_net_dict(network[const.UUID], network[const.NETWORKNAME], []) new_networks_list.append(new_network_dict) return new_networks_list def create_network(self, tenant_id, net_name): """ Creates a new Virtual Network, and assigns it a symbolic name. """ LOG.debug("create_network() called\n") new_network = db.network_create(tenant_id, net_name) new_net_id = new_network[const.UUID] vlan_id = self._get_vlan_for_tenant(tenant_id, net_name) vlan_name = self._get_vlan_name(new_net_id, str(vlan_id)) self._invoke_device_plugins(self._func_name(), [tenant_id, net_name, new_net_id, vlan_name, vlan_id]) cdb.add_vlan_binding(vlan_id, vlan_name, new_net_id) new_net_dict = {const.NET_ID: new_net_id, const.NET_NAME: net_name, const.NET_PORTS: []} return new_net_dict def delete_network(self, tenant_id, net_id): """ Deletes the network with the specified network identifier belonging to the specified tenant. """ LOG.debug("delete_network() called\n") net = db.network_get(net_id) if net: if len(net[const.NETWORKPORTS]) > 0: ports_on_net = db.port_list(net_id) for port in ports_on_net: if port[const.INTERFACEID]: raise exc.NetworkInUse(net_id=net_id) for port in ports_on_net: self.delete_port(tenant_id, net_id, port[const.PORTID]) self._invoke_device_plugins(self._func_name(), [tenant_id, net_id]) net_dict = self._make_net_dict(net[const.UUID], net[const.NETWORKNAME], []) self._release_vlan_for_tenant(tenant_id, net_id) cdb.remove_vlan_binding(net_id) db.network_destroy(net_id) return net_dict # Network not found raise exc.NetworkNotFound(net_id=net_id) def get_network_details(self, tenant_id, net_id): """ Gets the details of a particular network """ LOG.debug("get_network_details() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id]) network = db.network_get(net_id) ports_list = network[const.NETWORKPORTS] ports_on_net = [] for port in ports_list: new_port = self._make_port_dict(port[const.UUID], port[const.PORTSTATE], port[const.NETWORKID], port[const.INTERFACEID]) ports_on_net.append(new_port) new_network = self._make_net_dict(network[const.UUID], network[const.NETWORKNAME], ports_on_net) return new_network def rename_network(self, tenant_id, net_id, new_name): """ Updates the symbolic name belonging to a particular Virtual Network. """ LOG.debug("rename_network() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id, new_name]) network = db.network_rename(tenant_id, net_id, new_name) net_dict = self._make_net_dict(network[const.UUID], network[const.NETWORKNAME], []) return net_dict def get_all_ports(self, tenant_id, net_id): """ Retrieves all port identifiers belonging to the specified Virtual Network. """ LOG.debug("get_all_ports() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id]) network = db.network_get(net_id) ports_list = network[const.NETWORKPORTS] ports_on_net = [] for port in ports_list: new_port = self._make_port_dict(port[const.UUID], port[const.PORTSTATE], port[const.NETWORKID], port[const.INTERFACEID]) ports_on_net.append(new_port) return ports_on_net def create_port(self, tenant_id, net_id, port_state=None): """ Creates a port on the specified Virtual Network. """ LOG.debug("create_port() called\n") port = db.port_create(net_id, port_state) unique_port_id_string = port[const.UUID] self._invoke_device_plugins(self._func_name(), [tenant_id, net_id, port_state, unique_port_id_string]) new_port_dict = self._make_port_dict(port[const.UUID], port[const.PORTSTATE], port[const.NETWORKID], port[const.INTERFACEID]) return new_port_dict def delete_port(self, tenant_id, net_id, port_id): """ Deletes a port on a specified Virtual Network, if the port contains a remote interface attachment, the remote interface should first be un-plugged and then the port can be deleted. """ LOG.debug("delete_port() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id, port_id]) db.port_destroy(net_id, port_id) new_port_dict = self._make_port_dict(port_id, None, None, None) return new_port_dict def update_port(self, tenant_id, net_id, port_id, port_state): """ Updates the state of a port on the specified Virtual Network. """ LOG.debug("update_port() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id, port_id, port_state]) self._validate_port_state(port_state) db.port_set_state(net_id, port_id, port_state) new_port_dict = self._make_port_dict(port_id, port_state, net_id, None) return new_port_dict def get_port_details(self, tenant_id, net_id, port_id): """ This method allows the user to retrieve a remote interface that is attached to this particular port. """ LOG.debug("get_port_details() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id, port_id]) port = db.port_get(net_id, port_id) new_port_dict = self._make_port_dict(port[const.UUID], port[const.PORTSTATE], port[const.NETWORKID], port[const.INTERFACEID]) return new_port_dict def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id): """ Attaches a remote interface to the specified port on the specified Virtual Network. """ LOG.debug("plug_interface() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id, port_id, remote_interface_id]) db.port_set_attachment(net_id, port_id, remote_interface_id) def unplug_interface(self, tenant_id, net_id, port_id): """ Detaches a remote interface from the specified port on the specified Virtual Network. """ LOG.debug("unplug_interface() called\n") self._invoke_device_plugins(self._func_name(), [tenant_id, net_id, port_id]) db.port_unset_attachment(net_id, port_id) """ Extension API implementation """ def get_all_portprofiles(self, tenant_id): """Get all port profiles""" pplist = cdb.get_all_portprofiles() new_pplist = [] for portprofile in pplist: new_pp = self._make_portprofile_dict(tenant_id, portprofile[const.UUID], portprofile[const.PPNAME], portprofile[const.PPQOS]) new_pplist.append(new_pp) return new_pplist def get_portprofile_details(self, tenant_id, profile_id): """Get port profile details""" portprofile = cdb.get_portprofile(tenant_id, profile_id) new_pp = self._make_portprofile_dict(tenant_id, portprofile[const.UUID], portprofile[const.PPNAME], portprofile[const.PPQOS]) return new_pp def create_portprofile(self, tenant_id, profile_name, qos): """Create port profile""" portprofile = cdb.add_portprofile(tenant_id, profile_name, const.NO_VLAN_ID, qos) new_pp = self._make_portprofile_dict(tenant_id, portprofile[const.UUID], portprofile[const.PPNAME], portprofile[const.PPQOS]) return new_pp def delete_portprofile(self, tenant_id, profile_id): """Delete portprofile""" try: portprofile = cdb.get_portprofile(tenant_id, profile_id) except Exception, exc: raise cexc.PortProfileNotFound(tenant_id=tenant_id, portprofile_id=profile_id) plist = cdb.get_pp_binding(tenant_id, profile_id) if plist: raise cexc.PortProfileInvalidDelete(tenant_id=tenant_id, profile_id=profile_id) else: cdb.remove_portprofile(tenant_id, profile_id) def rename_portprofile(self, tenant_id, profile_id, new_name): """Rename port profile""" try: portprofile = cdb.get_portprofile(tenant_id, profile_id) except Exception, exc: raise cexc.PortProfileNotFound(tenant_id=tenant_id, portprofile_id=profile_id) portprofile = cdb.update_portprofile(tenant_id, profile_id, new_name) new_pp = self._make_portprofile_dict(tenant_id, portprofile[const.UUID], portprofile[const.PPNAME], portprofile[const.PPQOS]) return new_pp def associate_portprofile(self, tenant_id, net_id, port_id, portprofile_id): """Associate port profile""" try: portprofile = cdb.get_portprofile(tenant_id, portprofile_id) except Exception, exc: raise cexc.PortProfileNotFound(tenant_id=tenant_id, portprofile_id=portprofile_id) cdb.add_pp_binding(tenant_id, port_id, portprofile_id, False) def disassociate_portprofile(self, tenant_id, net_id, port_id, portprofile_id): """Disassociate port profile""" try: portprofile = cdb.get_portprofile(tenant_id, portprofile_id) except Exception, exc: raise cexc.PortProfileNotFound(tenant_id=tenant_id, portprofile_id=portprofile_id) cdb.remove_pp_binding(tenant_id, port_id, portprofile_id) def create_default_port_profile(self, tenant_id, network_id, profile_name, qos): "Create default port profile""" portprofile = cdb.add_portprofile(tenant_id, profile_name, const.NO_VLAN_ID, qos) new_pp = self._make_portprofile_dict(tenant_id, portprofile[const.UUID], portprofile[const.PPNAME], portprofile[const.PPQOS]) # TODO (Sumit): Need to check the following port_id = None cdb.add_pp_binding(tenant_id, port_id, portprofile[const.UUID], True) return new_pp """ Private functions """ def _invoke_device_plugins(self, function_name, args): """ All device-specific calls are delegate to the model """ getattr(self._model, function_name)(args) def _get_vlan_for_tenant(self, tenant_id, net_name): """Get vlan ID""" # TODO (Sumit): # The VLAN ID for a tenant might need to be obtained from # somewhere (from Donabe/Melange?) # Also need to make sure that the VLAN ID is not being used already # Currently, just a wrap-around counter ranging from VLAN_START to # VLAN_END return cdb.reserve_vlanid() def _release_vlan_for_tenant(self, tenant_id, net_id): """Relase VLAN""" vlan_binding = cdb.get_vlan_binding(net_id) return cdb.release_vlanid(vlan_binding[const.VLANID]) def _get_vlan_name(self, net_id, vlan): """Getting the vlan name from the tenant and vlan""" vlan_name = conf.VLAN_NAME_PREFIX + vlan return vlan_name def _validate_port_state(self, port_state): """Checking the port state""" if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN): raise exc.StateInvalid(port_state=port_state) return True def _func_name(self, offset=0): """Getting the name of the calling funciton""" return inspect.stack()[1 + offset][3] def _make_net_dict(self, net_id, net_name, ports): """Helper funciton""" res = {const.NET_ID: net_id, const.NET_NAME: net_name} res[const.NET_PORTS] = ports return res def _make_port_dict(self, port_id, port_state, net_id, attachment): """Helper funciton""" res = {const.PORT_ID: port_id, const.PORT_STATE: port_state} res[const.NET_ID] = net_id res[const.ATTACHMENT] = attachment return res def _make_portprofile_dict(self, tenant_id, profile_id, profile_name, qos): """Helper funciton""" profile_associations = self._make_portprofile_assc_list(tenant_id, profile_id) res = {const.PROFILE_ID: str(profile_id), const.PROFILE_NAME: profile_name, const.PROFILE_ASSOCIATIONS: profile_associations, const.PROFILE_VLAN_ID: None, const.PROFILE_QOS: qos} return res def _make_portprofile_assc_list(self, tenant_id, profile_id): """Helper function to create port profile association list""" plist = cdb.get_pp_binding(tenant_id, profile_id) assc_list = [] for port in plist: assc_list.append(port[const.PORTID]) return assc_list