Sumit Naiksatam fa9e303fe4 Loading of device-specific plugins and drivers is done dynamically by setting configuration.
All configuration is driven through configuration files place in the conf directory.
Each .ini conf file contains info on the configuration.
README file updated to reflect all the changes.
Fixed issue with delete_network deleting the network even when attachments were present.
Fixed issue with port id generation.
2011-08-05 02:59:54 -07:00

383 lines
16 KiB
Python

# 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 logging as LOG
from quantum.common import exceptions as exc
from quantum.common import utils
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_credentials as cred
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.common import cisco_utils as cutil
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2Network(object):
_networks = {}
_tenants = {}
_portprofiles = {}
_plugins = {}
def __init__(self):
self._net_counter = 0
self._portprofile_counter = 0
self._port_counter = 0
self._vlan_counter = int(conf.VLAN_START) - 1
for key in conf.plugins[const.PLUGINS].keys():
self._plugins[key] = utils.import_object(
conf.plugins[const.PLUGINS][key])
LOG.debug("Loaded device plugin %s\n" % \
conf.plugins[const.PLUGINS][key])
"""
Core API implementation
"""
def get_all_networks(self, tenant_id):
"""
Returns a dictionary containing all
<network_uuid, network_name> for
the specified tenant.
"""
LOG.debug("get_all_networks() called\n")
return self._networks.values()
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_net_id = self._get_unique_net_id(tenant_id)
vlan_id = self._get_vlan_for_tenant(tenant_id, net_name)
vlan_name = self._get_vlan_name(new_net_id, str(vlan_id))
for pluginClass in self._plugins.values():
pluginClass.create_network(tenant_id, net_name,
new_net_id, vlan_name, vlan_id)
new_net_dict = {const.NET_ID: new_net_id,
const.NET_NAME: net_name,
const.NET_PORTS: {},
const.NET_VLAN_NAME: vlan_name,
const.NET_VLAN_ID: vlan_id,
const.NET_TENANTS: [tenant_id]}
self._networks[new_net_id] = new_net_dict
tenant = self._get_tenant(tenant_id)
tenant_networks = tenant[const.TENANT_NETWORKS]
tenant_networks[new_net_id] = new_net_dict
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 = self._networks.get(net_id)
# TODO (Sumit) : Verify that no attachments are plugged into the
# network
if net:
if len(net[const.NET_PORTS].values()) > 0:
ports_on_net = net[const.NET_PORTS].values()
for port in ports_on_net:
if port[const.ATTACHMENT]:
raise exc.NetworkInUse(net_id=net_id)
for port in ports_on_net:
self.delete_port(tenant_id, net_id, port[const.PORT_ID])
# TODO (Sumit) : Before deleting the network, make sure all the
# ports associated with this network are also deleted
for pluginClass in self._plugins.values():
pluginClass.delete_network(tenant_id, net_id)
self._networks.pop(net_id)
tenant = self._get_tenant(tenant_id)
tenant_networks = tenant[const.TENANT_NETWORKS]
tenant_networks.pop(net_id)
return net
# Network not found
raise exc.NetworkNotFound(net_id=net_id)
def get_network_details(self, tenant_id, net_id):
"""
Deletes the Virtual Network belonging to a the
spec
"""
LOG.debug("get_network_details() called\n")
network = self._get_network(tenant_id, net_id)
return 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")
for pluginClass in self._plugins.values():
pluginClas.rename_network(tenant_id, net_id)
network = self._get_network(tenant_id, net_id)
network[const.NET_NAME] = new_name
return network
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")
network = self._get_network(tenant_id, net_id)
ports_on_net = network[const.NET_PORTS].values()
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")
net = self._get_network(tenant_id, net_id)
ports = net[const.NET_PORTS]
unique_port_id_string = self._get_unique_port_id(tenant_id, net_id)
self._plugins[const.UCS_PLUGIN].create_port(tenant_id, net_id,
port_state,
unique_port_id_string)
new_port_dict = {const.PORT_ID: unique_port_id_string,
const.PORT_STATE: const.PORT_UP,
const.ATTACHMENT: None}
ports[unique_port_id_string] = new_port_dict
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")
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
try:
#TODO (Sumit): Before deleting port profile make sure that there
# is no VM using this port profile
self._plugins[const.UCS_PLUGIN].delete_port(tenant_id, net_id,
port_id)
net = self._get_network(tenant_id, net_id)
net[const.NET_PORTS].pop(port_id)
except KeyError:
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
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")
port = self._get_port(tenant_id, net_id, port_id)
self._validate_port_state(port_state)
port[const.PORT_STATE] = port_state
return port
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")
return self._get_port(tenant_id, net_id, port_id)
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._validate_attachment(tenant_id, net_id, port_id,
remote_interface_id)
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
self._plugins[const.UCS_PLUGIN].plug_interface(tenant_id,
net_id, port_id,
remote_interface_id)
port[const.ATTACHMENT] = 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")
port = self._get_port(tenant_id, net_id, port_id)
self._plugins[const.UCS_PLUGIN].unplug_interface(tenant_id, net_id,
port_id)
port[const.ATTACHMENT] = None
"""
Extension API implementation
"""
def get_all_portprofiles(self, tenant_id):
return self._portprofiles.values()
def get_portprofile_details(self, tenant_id, profile_id):
return self._get_portprofile(tenant_id, profile_id)
def create_portprofile(self, tenant_id, profile_name, vlan_id):
profile_id = self._get_unique_profile_id(tenant_id)
new_port_profile_dict = {const.PROFILE_ID: profile_id,
const.PROFILE_NAME: profile_name,
const.PROFILE_ASSOCIATIONS: [],
const.PROFILE_VLAN_ID: vlan_id,
const.PROFILE_QOS: None}
self._portprofiles[profile_id] = new_port_profile_dict
tenant = self._get_tenant(tenant_id)
portprofiles = tenant[const.TENANT_PORTPROFILES]
portprofiles[profile_id] = new_port_profile_dict
return new_port_profile_dict
def delete_portprofile(self, tenant_id, profile_id):
portprofile = self._get_portprofile(tenant_id, profile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
if len(associations) > 0:
raise cexc.PortProfileInvalidDelete(tenant_id=tenant_id,
profile_id=profile_id)
else:
self._portprofiles.pop(profile_id)
tenant = self._get_tenant(tenant_id)
tenant[const.TENANT_PORTPROFILES].pop(profile_id)
def rename_portprofile(self, tenant_id, profile_id, new_name):
portprofile = self._get_portprofile(tenant_id, profile_id)
portprofile[const.PROFILE_NAME] = new_name
return portprofile
def associate_portprofile(self, tenant_id, net_id,
port_id, portprofile_id):
portprofile = self._get_portprofile(tenant_id, portprofile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
associations.append(port_id)
def disassociate_portprofile(self, tenant_id, net_id,
port_id, portprofile_id):
portprofile = self._get_portprofile(tenant_id, portprofile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
associations.remove(port_id)
def create_defaultPProfile(self, tenant_id, network_id, profile_name,
vlan_id):
pass
"""
Private functions
"""
def _get_vlan_for_tenant(self, tenant_id, net_name):
# 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
self._vlan_counter += 1
self._vlan_counter %= int(conf.VLAN_END)
if self._vlan_counter < int(conf.VLAN_START):
self._vlan_counter = int(conf.VLAN_START)
return self._vlan_counter
def _get_vlan_name(self, net_id, vlan):
vlan_name = conf.VLAN_NAME_PREFIX + net_id + "-" + vlan
return vlan_name
def _validate_port_state(self, port_state):
if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN):
raise exc.StateInvalid(port_state=port_state)
return True
def _validate_attachment(self, tenant_id, network_id, port_id,
remote_interface_id):
network = self._get_network(tenant_id, network_id)
for port in network[const.NET_PORTS].values():
if port[const.ATTACHMENT] == remote_interface_id:
raise exc.AlreadyAttached(net_id=network_id,
port_id=port_id,
att_id=port[const.ATTACHMENT],
att_port_id=port[const.PORT_ID])
def _get_network(self, tenant_id, network_id):
network = self._networks.get(network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)
return network
def _get_tenant(self, tenant_id):
tenant = self._tenants.get(tenant_id)
if not tenant:
LOG.debug("Creating new tenant record with tenant id %s\n" %
tenant_id)
tenant = {const.TENANT_ID: tenant_id,
const.TENANT_NAME: tenant_id,
const.TENANT_NETWORKS: {},
const.TENANT_PORTPROFILES: {}}
self._tenants[tenant_id] = tenant
return tenant
def _get_port(self, tenant_id, network_id, port_id):
net = self._get_network(tenant_id, network_id)
port = net[const.NET_PORTS].get(port_id)
if not port:
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
return port
def _get_portprofile(self, tenant_id, portprofile_id):
portprofile = self._portprofiles.get(portprofile_id)
if not portprofile:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
return portprofile
def _get_unique_net_id(self, tenant_id):
self._net_counter += 1
self._net_counter %= int(conf.MAX_NETWORKS)
id = tenant_id[:3] + \
"-n-" + ("0" * (6 - len(str(self._net_counter)))) + \
str(self._net_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
# ID will be generated by DB
return id
def _get_unique_port_id(self, tenant_id, net_id):
self._port_counter += 1
self._port_counter %= int(conf.MAX_PORTS)
id = net_id + "-p-" + str(self._port_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
# ID will be generated by DB
return id
def _get_unique_profile_id(self, tenant_id):
self._portprofile_counter += 1
self._portprofile_counter %= int(conf.MAX_PORT_PROFILES)
id = tenant_id[:3] + "-pp-" + \
("0" * (6 - len(str(self._net_counter)))) + str(self._net_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
# ID will be generated by DB
return id
# TODO (Sumit):
# (1) Persistent storage