Merging from lp:quantum

This commit is contained in:
Sumit Naiksatam 2011-08-30 14:40:42 -07:00
commit 65f9260346
17 changed files with 252 additions and 63 deletions

View File

@ -29,4 +29,4 @@ paste.filter_factory = quantum.common.extensions:plugin_aware_extension_middlewa
paste.app_factory = quantum.api.versions:Versions.factory
[app:quantumapiapp]
paste.app_factory = quantum.api:APIRouterV01.factory
paste.app_factory = quantum.api:APIRouterV1.factory

View File

@ -118,7 +118,6 @@ class NovatenantsController(common.QuantumController):
#added for cisco's extension
def schedule_host(self, request, tenant_id, id):
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
@ -139,7 +138,6 @@ class NovatenantsController(common.QuantumController):
def associate_port(self, request, tenant_id, id):
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
self._parse_request_params(request,

View File

@ -175,7 +175,6 @@ class PortprofilesController(common.QuantumController):
def associate_portprofile(self, request, tenant_id, id):
""" associate a portprofile to the port """
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
@ -198,7 +197,6 @@ class PortprofilesController(common.QuantumController):
def disassociate_portprofile(self, request, tenant_id, id):
""" Disassociate a portprofile from a port """
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
self._parse_request_params(request,

View File

@ -37,7 +37,7 @@ LOG = logging.getLogger('quantum.api')
FLAGS = flags.FLAGS
class APIRouterV01(wsgi.Router):
class APIRouterV1(wsgi.Router):
"""
Routes requests on the Quantum API to the appropriate controller
"""
@ -45,7 +45,7 @@ class APIRouterV01(wsgi.Router):
def __init__(self, options=None):
mapper = routes.Mapper()
self._setup_routes(mapper, options)
super(APIRouterV01, self).__init__(mapper)
super(APIRouterV1, self).__init__(mapper)
def _setup_routes(self, mapper, options):
# Loads the quantum plugin
@ -64,9 +64,7 @@ class APIRouterV01(wsgi.Router):
parent_resource=dict(member_name='network',
collection_name=uri_prefix +\
'networks'))
attachments_ctrl = attachments.Controller(plugin)
mapper.connect("get_resource",
uri_prefix + 'networks/{network_id}/' \
'ports/{id}/attachment{.format}',

View File

@ -32,8 +32,8 @@ EXCEPTIONS = {
421: exceptions.NetworkInUse,
430: exceptions.PortNotFound,
431: exceptions.StateInvalid,
432: exceptions.PortInUse,
440: exceptions.AlreadyAttached}
432: exceptions.PortInUseClient,
440: exceptions.AlreadyAttachedClient}
class ApiCall(object):
@ -131,7 +131,7 @@ class Client(object):
return conn.getresponse()
def do_request(self, method, action, body=None,
headers=None, params=None):
headers=None, params=None, exception_args={}):
"""
Connects to the server and issues a request.
Returns the result data, or raises an appropriate exception if
@ -190,7 +190,7 @@ class Client(object):
LOG.debug("Error message: %s", error_message)
# Create exception with HTTP status code and message
if res.status in EXCEPTIONS:
raise EXCEPTIONS[res.status]()
raise EXCEPTIONS[res.status](**exception_args)
# Add error code and message to exception arguments
ex = Exception("Server returned error: %s" % status_code)
ex.args = ([dict(status_code=status_code,
@ -254,7 +254,8 @@ class Client(object):
"""
Fetches the details of a certain network
"""
return self.do_request("GET", self.network_path % (network))
return self.do_request("GET", self.network_path % (network),
exception_args={"net_id": network})
@ApiCall
def create_network(self, body=None):
@ -268,14 +269,16 @@ class Client(object):
"""
Updates a network
"""
return self.do_request("PUT", self.network_path % (network), body=body)
return self.do_request("PUT", self.network_path % (network), body=body,
exception_args={"net_id": network})
@ApiCall
def delete_network(self, network):
"""
Deletes the specified network
"""
return self.do_request("DELETE", self.network_path % (network))
return self.do_request("DELETE", self.network_path % (network),
exception_args={"net_id": network})
@ApiCall
def list_ports(self, network):
@ -289,7 +292,8 @@ class Client(object):
"""
Fetches the details of a certain port
"""
return self.do_request("GET", self.port_path % (network, port))
return self.do_request("GET", self.port_path % (network, port),
exception_args={"net_id": network, "port_id": port})
@ApiCall
def create_port(self, network, body=None):
@ -297,14 +301,16 @@ class Client(object):
Creates a new port on a given network
"""
body = self.serialize(body)
return self.do_request("POST", self.ports_path % (network), body=body)
return self.do_request("POST", self.ports_path % (network), body=body,
exception_args={"net_id": network})
@ApiCall
def delete_port(self, network, port):
"""
Deletes the specified port from a network
"""
return self.do_request("DELETE", self.port_path % (network, port))
return self.do_request("DELETE", self.port_path % (network, port),
exception_args={"net_id": network, "port_id": port})
@ApiCall
def set_port_state(self, network, port, body=None):
@ -312,14 +318,18 @@ class Client(object):
Sets the state of the specified port
"""
return self.do_request("PUT",
self.port_path % (network, port), body=body)
self.port_path % (network, port), body=body,
exception_args={"net_id": network,
"port_id": port,
"port_state": str(body)})
@ApiCall
def show_port_attachment(self, network, port):
"""
Fetches the attachment-id associated with the specified port
"""
return self.do_request("GET", self.attachment_path % (network, port))
return self.do_request("GET", self.attachment_path % (network, port),
exception_args={"net_id": network, "port_id": port})
@ApiCall
def attach_resource(self, network, port, body=None):
@ -327,7 +337,10 @@ class Client(object):
Sets the attachment-id of the specified port
"""
return self.do_request("PUT",
self.attachment_path % (network, port), body=body)
self.attachment_path % (network, port), body=body,
exception_args={"net_id": network,
"port_id": port,
"attach_id": str(body)})
@ApiCall
def detach_resource(self, network, port):
@ -335,4 +348,5 @@ class Client(object):
Removes the attachment-id of the specified port
"""
return self.do_request("DELETE",
self.attachment_path % (network, port))
self.attachment_path % (network, port),
exception_args={"net_id": network, "port_id": port})

View File

@ -111,6 +111,21 @@ class AlreadyAttached(QuantumException):
"already plugged into port %(att_port_id)s")
# NOTE: on the client side, we often do not know all of the information
# that is known on the server, thus, we create separate exception for
# those scenarios
class PortInUseClient(QuantumException):
message = _("Unable to complete operation on port %(port_id)s " \
"for network %(net_id)s. An attachment " \
"is plugged into the logical port.")
class AlreadyAttachedClient(QuantumException):
message = _("Unable to plug the attachment %(att_id)s into port " \
"%(port_id)s for network %(net_id)s. The attachment is " \
"already plugged into another port.")
class Duplicate(Error):
pass

View File

@ -338,7 +338,7 @@ def plugin_aware_extension_middleware_factory(global_config, **local_config):
def _factory(app):
extensions_path = global_config.get('api_extensions_path', '')
ext_mgr = PluginAwareExtensionManager(extensions_path,
QuantumManager().get_plugin())
QuantumManager.get_plugin())
return ExtensionMiddleware(app, global_config, ext_mgr=ext_mgr)
return _factory

View File

@ -142,6 +142,13 @@ def network_destroy(net_id):
net = session.query(models.Network).\
filter_by(uuid=net_id).\
one()
ports = session.query(models.Port).\
filter_by(network_id=net_id).\
all()
for p in ports:
session.delete(p)
session.delete(net)
session.flush()
return net

View File

@ -26,7 +26,7 @@ The caller should make sure that QuantumManager is a singleton.
import gettext
import logging
import os
import logging
gettext.install('quantum', unicode=1)
from common import utils

View File

@ -252,3 +252,46 @@ def port_destroy(net_id, port_id):
return port
except exc.NoResultFound:
raise q_exc.PortNotFound(port_id=port_id)
#methods using just port_id
def port_get_by_id(port_id):
session = get_session()
try:
return session.query(models.Port).\
filter_by(uuid=port_id).one()
except exc.NoResultFound:
raise q_exc.PortNotFound(port_id=port_id)
def port_set_attachment_by_id(port_id, new_interface_id):
session = get_session()
port = port_get_by_id(port_id)
if new_interface_id != "":
if port['interface_id']:
raise q_exc.PortInUse(port_id=port_id,
att_id=port['interface_id'])
try:
port = session.query(models.Port).\
filter_by(interface_id=new_interface_id).\
one()
raise q_exc.AlreadyAttached(port_id=port_id,
att_id=new_interface_id,
att_port_id=port['uuid'])
except exc.NoResultFound:
pass
port.interface_id = new_interface_id
session.merge(port)
session.flush()
return port
def port_unset_attachment_by_id(port_id):
session = get_session()
port = port_get_by_id(port_id)
port.interface_id = None
session.merge(port)
session.flush()
return port

View File

@ -35,7 +35,7 @@ flags.DEFINE_integer('quantum_port', 9696,
HOST = FLAGS.quantum_host
PORT = FLAGS.quantum_port
USE_SSL = False
ACTION_PREFIX_EXT = '/v0.1'
ACTION_PREFIX_EXT = '/v1.0'
ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \
'/extensions/csco/tenants/{tenant_id}'
TENANT_ID = 'nova'

View File

@ -40,7 +40,7 @@ HOST = FLAGS.quantum_host
PORT = FLAGS.quantum_port
USE_SSL = False
TENANT_ID = 'nova'
ACTION_PREFIX_EXT = '/v0.1'
ACTION_PREFIX_EXT = '/v1.0'
ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \
'/extensions/csco/tenants/{tenant_id}'
TENANT_ID = 'nova'

View File

@ -87,7 +87,7 @@ class PortprofileExtensionTest(unittest.TestCase):
options = {}
options['plugin_provider'] = 'quantum.plugins.cisco.l2network_plugin'\
'.L2Network'
self.api = server.APIRouterV01(options)
self.api = server.APIRouterV1(options)
self._l2network_plugin = l2network_plugin.L2Network()
def test_list_portprofile(self):
@ -353,6 +353,25 @@ class PortprofileExtensionTest(unittest.TestCase):
LOG.debug("Creating port for network - END")
return port_data['port']['id']
def _delete_port(self, network_id, port_id):
""" Delete port """
LOG.debug("Deleting port for network %s - START", network_id)
port_path = "/tenants/tt/networks/%(network_id)s/ports/"\
"%(port_id)s" % locals()
port_req = self.create_request(port_path, None,
self.contenttype, 'DELETE')
port_req.get_response(self.api)
LOG.debug("Deleting port for network - END")
def _delete_network(self, network_id):
""" Delete network """
LOG.debug("Deleting network %s - START", network_id)
network_path = "/tenants/tt/networks/%s" % network_id
network_req = self.create_request(network_path, None,
self.contenttype, 'DELETE')
network_req.get_response(self.api)
LOG.debug("Deleting network - END")
def test_associate_portprofile(self):
""" Test associate portprofile"""
@ -388,6 +407,7 @@ class PortprofileExtensionTest(unittest.TestCase):
delete_path = str(delete_path_temp)
self.tear_down_associate_profile(delete_path, disassociate_path,
req_assign_body)
self.tear_down_port_network(net_id, port_id)
LOG.debug("test_associate_portprofile - END")
def test_associate_portprofileDNE(self, portprofile_id='100'):
@ -445,8 +465,15 @@ class PortprofileExtensionTest(unittest.TestCase):
resp_body['portprofiles']['portprofile']['id']
delete_path = str(delete_path_temp)
self.tear_down_profile(delete_path)
self.tear_down_port_network(net_id, port_id)
LOG.debug("test_disassociate_portprofile - END")
def tear_down_port_network(self, net_id, port_id):
""" Tear down port and network """
self._delete_port(net_id, port_id)
self._delete_network(net_id)
def tear_down_profile(self, delete_profile_path):
""" Tear down profile"""

View File

@ -28,6 +28,7 @@ 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
from quantum.plugins.cisco.db import api as db
from quantum.plugins.cisco.db import ucs_db as udb
from quantum.plugins.cisco.ucs \
import cisco_ucs_inventory_configuration as conf
@ -268,7 +269,68 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
tenant_id)
return None
def _get_instance_port(self, tenant_id, instance_id, vif_id=None):
def _get_instance_port(self, tenant_id, instance_id, vif_id):
"""
Return the device name for a reserved interface
"""
found_blade_intf_data = None
for ucsm_ip in self._inventory_state.keys():
ucsm = self._inventory_state[ucsm_ip]
for chassis_id in ucsm.keys():
for blade_id in ucsm[chassis_id]:
blade_data = ucsm[chassis_id][blade_id]
blade_intf_data = blade_data[const.BLADE_INTF_DATA]
for blade_intf in blade_intf_data.keys():
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
blade_intf_data[blade_intf]\
[const.INSTANCE_ID] == instance_id:
found_blade_intf_data = blade_intf_data
LOG.debug("Found blade %s associated with this" \
" instance: %s" % \
(blade_id,
instance_id))
break
if found_blade_intf_data:
blade_intf_data = found_blade_intf_data
for blade_intf in blade_intf_data.keys():
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
(not blade_intf_data[blade_intf][const.VIF_ID]):
blade_intf_data[blade_intf][const.VIF_ID] = \
vif_id
blade_intf_data[blade_intf]\
[const.INSTANCE_ID] = instance_id
port_binding = udb.get_portbinding_dn(blade_intf)
port_id = port_binding[const.PORTID]
udb.update_portbinding(port_id, instance_id=instance_id,
vif_id=vif_id)
db.port_set_attachment_by_id(port_id, vif_id)
device_name = blade_intf_data[blade_intf]\
[const.BLADE_INTF_RHEL_DEVICE_NAME]
profile_name = port_binding[const.PORTPROFILENAME]
dynamicnic_details = \
{const.DEVICENAME: device_name,
const.UCSPROFILE: profile_name}
LOG.debug("Found reserved dynamic nic: %s" \
"associated with port %s" %
(blade_intf_data[blade_intf], port_id))
LOG.debug("Returning dynamic nic details: %s" %
dynamicnic_details)
return dynamicnic_details
LOG.warn("Could not find a reserved dynamic nic for tenant: %s" %
tenant_id)
return None
def _disassociate_vifid_from_port(self, tenant_id, port_id):
"""
Return the device name for a reserved interface
"""
@ -284,28 +346,24 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
blade_intf_data[blade_intf]\
[const.INSTANCE_ID] == instance_id:
blade_intf_data[blade_intf][const.PORTID] == \
port_id:
vif_id = blade_intf_data[blade_intf][const.VIF_ID]
blade_intf_data[blade_intf][const.VIF_ID] = \
vif_id
port_binding = udb.get_portbinding_dn(blade_intf)
port_id = port_binding[const.PORTID]
udb.update_portbinding(port_id,
vif_id=vif_id)
device_name = blade_intf_data[blade_intf]\
[const.BLADE_INTF_RHEL_DEVICE_NAME]
profile_name = port_binding[const.PORTPROFILENAME]
dynamicnic_details = \
{const.DEVICENAME: device_name,
const.UCSPROFILE: profile_name}
LOG.debug("Found reserved dynamic nic: %s" \
"associated with port %s" %
(blade_intf_data[blade_intf], port_id))
LOG.debug("Returning dynamic nic details: %s" %
dynamicnic_details)
return dynamicnic_details
LOG.warn("Could not find a reserved dynamic nic for tenant: %s" %
tenant_id)
None
blade_intf_data[blade_intf][const.INSTANCE_ID] = \
None
udb.update_portbinding(port_id, instance_id=None,
vif_id=None)
LOG.debug("Disassociated VIF-ID: %s " \
"from port: %s" \
"in UCS inventory state for blade: %s" %
(vif_id, port_id,
blade_intf_data[blade_intf]))
return
LOG.warn("Disassociating VIF-ID %s in UCS inventory failed. " \
"Could not find a reserved dynamic nic for tenant: %s" %
(vif_id, tenant_id))
return None
def reload_inventory(self):
@ -595,6 +653,9 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
on which a dynamic vnic was reserved for this port
"""
LOG.debug("unplug_interface() called\n")
tenant_id = args[0]
port_id = args[2]
self._disassociate_vifid_from_port(tenant_id, port_id)
return self._get_blade_for_port(args)
def schedule_host(self, args):

View File

@ -42,7 +42,7 @@ class FakeHTTPConnection:
self._req = None
options = \
dict(plugin_provider='quantum.plugins.SamplePlugin.FakePlugin')
self._api = server.APIRouterV01(options)
self._api = server.APIRouterV1(options)
def request(self, method, action, body, headers):
# TODO: remove version prefix from action!

View File

@ -218,8 +218,8 @@ class APITest(unittest.TestCase):
LOG.debug("_test_delete_network - format:%s - START", format)
content_type = "application/%s" % format
network_id = self._create_network(format)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
delete_network_req = testlib.network_delete_request(self.tenant_id,
network_id,
format)
@ -240,8 +240,8 @@ class APITest(unittest.TestCase):
port_state = "ACTIVE"
attachment_id = "test_attachment"
network_id = self._create_network(format)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
port_id = self._create_port(network_id, port_state, format)
#plug an attachment into the port
LOG.debug("Putting attachment into port %s", port_id)
@ -252,8 +252,8 @@ class APITest(unittest.TestCase):
attachment_res = attachment_req.get_response(self.api)
self.assertEquals(attachment_res.status_int, 204)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
delete_network_req = testlib.network_delete_request(self.tenant_id,
network_id,
format)
@ -261,6 +261,26 @@ class APITest(unittest.TestCase):
self.assertEqual(delete_network_res.status_int, 421)
LOG.debug("_test_delete_network_in_use - format:%s - END", format)
def _test_delete_network_with_unattached_port(self, format):
LOG.debug("_test_delete_network_with_unattached_port "\
"- format:%s - START", format)
content_type = "application/%s" % format
port_state = "ACTIVE"
network_id = self._create_network(format)
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
port_id = self._create_port(network_id, port_state, format)
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
delete_network_req = testlib.network_delete_request(self.tenant_id,
network_id,
format)
delete_network_res = delete_network_req.get_response(self.api)
self.assertEqual(delete_network_res.status_int, 204)
LOG.debug("_test_delete_network_with_unattached_port "\
"- format:%s - END", format)
def _test_list_ports(self, format):
LOG.debug("_test_list_ports - format:%s - START", format)
content_type = "application/%s" % format
@ -433,8 +453,9 @@ class APITest(unittest.TestCase):
port_state = "ACTIVE"
network_id = self._create_network(format)
port_id = self._create_port(network_id, port_state, format)
LOG.debug("Deleting port %(port_id)s for network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting port %s for network %s"\
" of tenant %s" % (port_id, network_id,
self.tenant_id))
delete_port_req = testlib.port_delete_request(self.tenant_id,
network_id, port_id,
format)
@ -464,8 +485,9 @@ class APITest(unittest.TestCase):
attachment_id)
attachment_res = attachment_req.get_response(self.api)
self.assertEquals(attachment_res.status_int, 204)
LOG.debug("Deleting port %(port_id)s for network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting port %s for network %s"\
" of tenant %s" % (port_id, network_id,
self.tenant_id))
delete_port_req = testlib.port_delete_request(self.tenant_id,
network_id, port_id,
format)
@ -763,7 +785,7 @@ class APITest(unittest.TestCase):
def setUp(self):
options = {}
options['plugin_provider'] = test_config['plugin_name']
self.api = server.APIRouterV01(options)
self.api = server.APIRouterV1(options)
self.tenant_id = "test_tenant"
self.network_name = "test_network"
self._net_serializer = \
@ -848,6 +870,12 @@ class APITest(unittest.TestCase):
def test_delete_network_in_use_xml(self):
self._test_delete_network_in_use('xml')
def test_delete_network_with_unattached_port_xml(self):
self._test_delete_network_with_unattached_port('xml')
def test_delete_network_with_unattached_port_json(self):
self._test_delete_network_with_unattached_port('json')
def test_list_ports_json(self):
self._test_list_ports('json')

View File

@ -42,7 +42,7 @@ class CLITest(unittest.TestCase):
"""Prepare the test environment"""
options = {}
options['plugin_provider'] = 'quantum.plugins.SamplePlugin.FakePlugin'
self.api = server.APIRouterV01(options)
self.api = server.APIRouterV1(options)
self.tenant_id = "test_tenant"
self.network_name_1 = "test_network_1"