Merged from trunk

This commit is contained in:
Santhosh Kumar 2011-08-01 11:52:39 +05:30
commit aa60fb2b5c
16 changed files with 143 additions and 79 deletions

View File

@ -53,6 +53,8 @@ class APIRouterV01(wsgi.Router):
uri_prefix = '/tenants/{tenant_id}/'
mapper.resource('network', 'networks',
controller=networks.Controller(plugin),
collection={'detail': 'GET'},
member={'detail': 'GET'},
path_prefix=uri_prefix)
mapper.resource('port', 'ports',
controller=ports.Controller(plugin),

View File

@ -57,8 +57,7 @@ class QuantumController(wsgi.Controller):
pass
if not param_value and param['required']:
msg = ("Failed to parse request. " +
"Parameter: %(param_name)s " +
"not specified" % locals())
"Parameter: " + param_name + " not specified")
for line in msg.split('\n'):
LOG.error(line)
raise exc.HTTPBadRequest(msg)

View File

@ -52,7 +52,7 @@ class Fault(webob.exc.HTTPException):
fault_name: {
'code': code,
'message': self.wrapped_exc.explanation,
'detail': self.wrapped_exc.detail}}
'detail': str(self.wrapped_exc.detail)}}
# 'code' is an attribute on the fault tag itself
metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
default_xmlns = common.XML_NS_V10

View File

@ -36,7 +36,9 @@ class Controller(common.QuantumController):
"application/xml": {
"attributes": {
"network": ["id", "name"],
"port": ["id", "state"],
},
"plurals": {"networks": "network"}
},
}
@ -47,19 +49,46 @@ class Controller(common.QuantumController):
def index(self, request, tenant_id):
""" Returns a list of network ids """
#TODO: this should be for a given tenant!!!
return self._items(request, tenant_id, is_detail=False)
return self._items(request, tenant_id)
def _items(self, request, tenant_id, is_detail):
def _item(self, req, tenant_id, network_id,
net_details=True, port_details=False):
# We expect get_network_details to return information
# concerning logical ports as well.
network = self._plugin.get_network_details(
tenant_id, network_id)
builder = networks_view.get_view_builder(req)
result = builder.build(network, net_details, port_details)['network']
return dict(network=result)
def _items(self, req, tenant_id, net_details=False, port_details=False):
""" Returns a list of networks. """
networks = self._plugin.get_all_networks(tenant_id)
builder = networks_view.get_view_builder(request)
result = [builder.build(network, is_detail)['network']
builder = networks_view.get_view_builder(req)
result = [builder.build(network, net_details, port_details)['network']
for network in networks]
return dict(networks=result)
def show(self, request, tenant_id, id):
""" Returns network details for the given network id """
try:
return self._item(request, tenant_id, id,
net_details=True, port_details=False)
except exception.NetworkNotFound as e:
return faults.Fault(faults.NetworkNotFound(e))
def detail(self, request, **kwargs):
tenant_id = kwargs.get('tenant_id')
network_id = kwargs.get('id')
try:
if network_id:
# show details for a given network
return self._item(request, tenant_id, network_id,
net_details=True, port_details=True)
else:
# show details for all networks
return self._items(request, tenant_id,
net_details=True, port_details=False)
network = self._plugin.get_network_details(
tenant_id, id)
builder = networks_view.get_view_builder(request)

View File

@ -40,7 +40,10 @@ class Controller(common.QuantumController):
_serialization_metadata = {
"application/xml": {
"attributes": {
"port": ["id", "state"], }, }, }
"port": ["id", "state"], },
"plurals": {"ports": "port"}
},
}
def __init__(self, plugin):
self._resource_name = 'port'
@ -68,8 +71,8 @@ class Controller(common.QuantumController):
tenant_id, network_id, id)
builder = ports_view.get_view_builder(request)
#build response with details
result = builder.build(port, True)
return dict(ports=result)
result = builder.build(port, True)['port']
return dict(port=result)
except exception.NetworkNotFound as e:
return faults.Fault(faults.NetworkNotFound(e))
except exception.PortNotFound as e:
@ -105,7 +108,7 @@ class Controller(common.QuantumController):
return faults.Fault(e)
try:
port = self._plugin.update_port(tenant_id, network_id, id,
request_params['port-state'])
request_params['port-state'])
builder = ports_view.get_view_builder(request)
result = builder.build(port, True)
return dict(ports=result)
@ -122,7 +125,7 @@ class Controller(common.QuantumController):
try:
self._plugin.delete_port(tenant_id, network_id, id)
return exc.HTTPAccepted()
#TODO(salvatore-orlando): Handle portInUse error
# TODO(salvatore-orlando): Handle portInUse error
except exception.NetworkNotFound as e:
return faults.Fault(faults.NetworkNotFound(e))
except exception.PortNotFound as e:

View File

@ -13,4 +13,3 @@
# 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: Somik Behera, Nicira Networks, Inc.

View File

@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from quantum.api.views import ports as ports_view
def get_view_builder(req):
@ -31,19 +31,34 @@ class ViewBuilder(object):
"""
self.base_url = base_url
def build(self, network_data, is_detail=False):
def build(self, network_data, net_detail=False, port_detail=False):
"""Generic method used to generate a network entity."""
if is_detail:
if net_detail:
network = self._build_detail(network_data)
else:
network = self._build_simple(network_data)
if port_detail:
builder = ports_view.ViewBuilder(self.base_url)
ports = [builder.build(port_data, port_detail)['port']
for port_data in network_data['net-ports'].values()]
network['ports'] = ports
return network
def _build_simple(self, network_data):
"""Return a simple model of a server."""
"""Return a simple model of a network."""
return dict(network=dict(id=network_data['net-id']))
def _build_detail(self, network_data):
"""Return a simple model of a server."""
"""Return a detailed model of a network."""
# net-ports might not be present in response from plugin
ports = network_data.get('net-ports', None)
portcount = ports and len(ports) or 0
return dict(network=dict(id=network_data['net-id'],
name=network_data['net-name']))
name=network_data['net-name'],
PortCount=portcount))
def _build_port(self, port_data):
"""Return details about a specific logical port."""
return dict(port=dict(id=port_data['port-id'],
state=port_data['port-state'],
attachment=port_data['attachment']))

View File

@ -38,10 +38,10 @@ class ViewBuilder(object):
return port
def _build_simple(self, port_data):
"""Return a simple model of a server."""
"""Return a simple model of a port."""
return dict(port=dict(id=port_data['port-id']))
def _build_detail(self, port_data):
"""Return a simple model of a server."""
"""Return a simple model of a port (with its state)."""
return dict(port=dict(id=port_data['port-id'],
state=port_data['port-state']))
state=port_data['port-state']))

View File

@ -114,7 +114,7 @@ def create_net(manager, *args):
def api_create_net(client, *args):
tid, name = args
data = {'network': {'network-name': '%s' % name}}
data = {'network': {'net-name': '%s' % name}}
body = Serializer().serialize(data, CONTENT_TYPE)
res = client.do_request(tid, 'POST', "/networks." + FORMAT, body=body)
rd = json.loads(res.read())
@ -185,7 +185,7 @@ def rename_net(manager, *args):
def api_rename_net(client, *args):
tid, nid, name = args
data = {'network': {'network-name': '%s' % name}}
data = {'network': {'net-name': '%s' % name}}
body = Serializer().serialize(data, CONTENT_TYPE)
res = client.do_request(tid, 'PUT', "/networks/%s.%s" % (nid, FORMAT),
body=body)

View File

@ -20,17 +20,13 @@
Utility methods for working with WSGI servers
"""
import json
import logging
import sys
import datetime
from xml.dom import minidom
import eventlet
import eventlet.wsgi
eventlet.patcher.monkey_patch(all=False, socket=True)
import routes
import routes.middleware
import webob.dec
import webob.exc
@ -135,7 +131,6 @@ class Request(webob.Request):
ctypes = ['application/json', 'application/xml']
bm = self.accept.best_match(ctypes)
LOG.debug("BM:%s", bm)
return bm or 'application/json'
def get_content_type(self):
@ -332,7 +327,6 @@ class Controller(object):
"""
Call the method specified in req.environ by RoutesMiddleware.
"""
LOG.debug("HERE - wsgi.Controller.__call__")
arg_dict = req.environ['wsgiorg.routing_args'][1]
action = arg_dict['action']
method = getattr(self, action)
@ -345,8 +339,6 @@ class Controller(object):
if type(result) is dict:
content_type = req.best_match_content_type()
LOG.debug("Content type:%s", content_type)
LOG.debug("Result:%s", result)
default_xmlns = self.get_default_xmlns(req)
body = self._serialize(result, content_type, default_xmlns)
@ -425,7 +417,10 @@ class Serializer(object):
The string must be in the format of a supported MIME type.
"""
return self.get_deserialize_handler(content_type)(datastring)
try:
return self.get_deserialize_handler(content_type)(datastring)
except Exception:
raise webob.exc.HTTPBadRequest("Could not deserialize data")
def get_deserialize_handler(self, content_type):
handlers = {
@ -457,7 +452,8 @@ class Serializer(object):
if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3:
return node.childNodes[0].nodeValue
elif node.nodeName in listnames:
return [self._from_xml_node(n, listnames) for n in node.childNodes]
return [self._from_xml_node(n, listnames)
for n in node.childNodes if n.nodeType != node.TEXT_NODE]
else:
result = dict()
for attr in node.attributes.keys():
@ -482,7 +478,7 @@ class Serializer(object):
if not xmlns and self.default_xmlns:
node.setAttribute('xmlns', self.default_xmlns)
return node.toprettyxml(indent=' ')
return node.toprettyxml(indent='', newl='')
def _to_xml_node(self, doc, metadata, nodename, data):
"""Recursive method to convert data members to XML nodes."""
@ -493,9 +489,7 @@ class Serializer(object):
xmlns = metadata.get('xmlns', None)
if xmlns:
result.setAttribute('xmlns', xmlns)
LOG.debug("DATA:%s", data)
if type(data) is list:
LOG.debug("TYPE IS LIST")
collections = metadata.get('list_collections', {})
if nodename in collections:
metadata = collections[nodename]
@ -514,7 +508,6 @@ class Serializer(object):
node = self._to_xml_node(doc, metadata, singular, item)
result.appendChild(node)
elif type(data) is dict:
LOG.debug("TYPE IS DICT")
collections = metadata.get('dict_collections', {})
if nodename in collections:
metadata = collections[nodename]
@ -533,8 +526,7 @@ class Serializer(object):
node = self._to_xml_node(doc, metadata, k, v)
result.appendChild(node)
else:
# Type is atom
LOG.debug("TYPE IS ATOM:%s", data)
# Type is atom.
node = doc.createTextNode(str(data))
result.appendChild(node)
return result

View File

@ -21,7 +21,6 @@
Quantum's Manager class is responsible for parsing a config file and
instantiating the correct plugin that concretely implement quantum_plugin_base
class.
The caller should make sure that QuantumManager is a singleton.
"""
import gettext

View File

@ -1,4 +1,3 @@
[PLUGIN]
# Quantum plugin provider module
#provider = quantum.plugins.SamplePlugin.FakePlugin
provider = quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPlugin
provider = quantum.plugins.SamplePlugin.FakePlugin

View File

@ -164,7 +164,6 @@ class DummyDataPlugin(object):
retrieved a list of all the remote vifs that
are attached to the network
"""
print("get_network_details() called\n")
vifs_on_net = ["/tenant1/networks/net_id/portid/vif2.0"]
return vifs_on_net
@ -192,12 +191,6 @@ class DummyDataPlugin(object):
#return the port id
return 201
def update_port(self, tenant_id, net_id, port_id, port_state):
"""
Updates the state of a port on the specified Virtual Network.
"""
print("update_port() called\n")
def delete_port(self, tenant_id, net_id, port_id):
"""
Deletes a port on a specified Virtual Network,
@ -295,8 +288,11 @@ class FakePlugin(object):
"""
LOG.debug("FakePlugin.get_network_details() called")
net = self._get_network(tenant_id, net_id)
# Retrieves ports for network
ports = self.get_all_ports(tenant_id, net_id)
return {'net-id': str(net.uuid),
'net-name': net.name}
'net-name': net.name,
'net-ports': ports}
def create_network(self, tenant_id, net_name):
"""

View File

@ -84,10 +84,10 @@ $ python ovs_quantum_agent.py ovs_quantum_plugin.ini
# -- Getting quantum up and running
- Start quantum [on the quantum service host]:
~/src/quantum-framework$ PYTHONPATH=.:$PYTHONPATH python bin/quantum etc/quantum.conf
~/src/quantum $ PYTHONPATH=.:$PYTHONPATH python bin/quantum etc/quantum.conf
- Run ovs_quantum_plugin.py via the quantum plugin framework cli [on the
quantum service host]
~/src/quantum-framework$ PYTHONPATH=.:$PYTHONPATH python quantum/cli.py
~/src/quantum$ PYTHONPATH=.:$PYTHONPATH python quantum/cli.py
This will show help all of the available commands.

View File

@ -75,7 +75,7 @@ class APITest(unittest.TestCase):
network_data = Serializer().deserialize(show_network_res.body,
content_type)
self.assertEqual(network_id,
network_data['networks']['network']['id'])
network_data['network']['id'])
LOG.debug("_test_create_network - format:%s - END", format)
def _test_create_network_badrequest(self, format):
@ -96,8 +96,8 @@ class APITest(unittest.TestCase):
format)
list_network_res = list_network_req.get_response(self.api)
self.assertEqual(list_network_res.status_int, 200)
network_data = Serializer().deserialize(list_network_res.body,
content_type)
network_data = self._net_serializer.deserialize(
list_network_res.body, content_type)
# Check network count: should return 2
self.assertEqual(len(network_data['networks']), 2)
LOG.debug("_test_list_networks - format:%s - END", format)
@ -111,10 +111,12 @@ class APITest(unittest.TestCase):
format)
show_network_res = show_network_req.get_response(self.api)
self.assertEqual(show_network_res.status_int, 200)
network_data = Serializer().deserialize(show_network_res.body,
content_type)
self.assertEqual({'id': network_id, 'name': self.network_name},
network_data['networks']['network'])
network_data = self._net_serializer.deserialize(
show_network_res.body, content_type)
self.assertEqual({'id': network_id,
'name': self.network_name,
'PortCount': 0},
network_data['network'])
LOG.debug("_test_show_network - format:%s - END", format)
def _test_show_network_not_found(self, format):
@ -142,10 +144,12 @@ class APITest(unittest.TestCase):
format)
show_network_res = show_network_req.get_response(self.api)
self.assertEqual(show_network_res.status_int, 200)
network_data = Serializer().deserialize(show_network_res.body,
content_type)
self.assertEqual({'id': network_id, 'name': new_name},
network_data['networks']['network'])
network_data = self._net_serializer.deserialize(
show_network_res.body, content_type)
self.assertEqual({'id': network_id,
'name': new_name,
'PortCount': 0},
network_data['network'])
LOG.debug("_test_rename_network - format:%s - END", format)
def _test_rename_network_badrequest(self, format):
@ -189,8 +193,8 @@ class APITest(unittest.TestCase):
list_network_req = testlib.network_list_request(self.tenant_id,
format)
list_network_res = list_network_req.get_response(self.api)
network_list_data = Serializer().deserialize(list_network_res.body,
content_type)
network_list_data = self._net_serializer.deserialize(
list_network_res.body, content_type)
network_count = len(network_list_data['networks'])
self.assertEqual(network_count, 0)
LOG.debug("_test_delete_network - format:%s - END", format)
@ -233,8 +237,8 @@ class APITest(unittest.TestCase):
network_id, format)
list_port_res = list_port_req.get_response(self.api)
self.assertEqual(list_port_res.status_int, 200)
port_data = Serializer().deserialize(list_port_res.body,
content_type)
port_data = self._port_serializer.deserialize(
list_port_res.body, content_type)
# Check port count: should return 2
self.assertEqual(len(port_data['ports']), 2)
LOG.debug("_test_list_ports - format:%s - END", format)
@ -250,10 +254,10 @@ class APITest(unittest.TestCase):
format)
show_port_res = show_port_req.get_response(self.api)
self.assertEqual(show_port_res.status_int, 200)
port_data = Serializer().deserialize(show_port_res.body,
content_type)
port_data = self._port_serializer.deserialize(
show_port_res.body, content_type)
self.assertEqual({'id': port_id, 'state': port_state},
port_data['ports']['port'])
port_data['port'])
LOG.debug("_test_show_port - format:%s - END", format)
def _test_show_port_networknotfound(self, format):
@ -291,8 +295,9 @@ class APITest(unittest.TestCase):
network_id, port_id, format)
show_port_res = show_port_req.get_response(self.api)
self.assertEqual(show_port_res.status_int, 200)
port_data = Serializer().deserialize(show_port_res.body, content_type)
self.assertEqual(port_id, port_data['ports']['port']['id'])
port_data = self._port_serializer.deserialize(
show_port_res.body, content_type)
self.assertEqual(port_id, port_data['port']['id'])
LOG.debug("_test_create_port - format:%s - END", format)
def _test_create_port_networknotfound(self, format):
@ -329,8 +334,8 @@ class APITest(unittest.TestCase):
list_port_req = testlib.port_list_request(self.tenant_id, network_id,
format)
list_port_res = list_port_req.get_response(self.api)
port_list_data = Serializer().deserialize(list_port_res.body,
content_type)
port_list_data = self._port_serializer.deserialize(
list_port_res.body, content_type)
port_count = len(port_list_data['ports'])
self.assertEqual(port_count, 0)
LOG.debug("_test_delete_port - format:%s - END", format)
@ -405,10 +410,10 @@ class APITest(unittest.TestCase):
format)
show_port_res = show_port_req.get_response(self.api)
self.assertEqual(show_port_res.status_int, 200)
network_data = Serializer().deserialize(show_port_res.body,
content_type)
port_data = self._port_serializer.deserialize(
show_port_res.body, content_type)
self.assertEqual({'id': port_id, 'state': new_port_state},
network_data['ports']['port'])
port_data['port'])
LOG.debug("_test_set_port_state - format:%s - END", format)
def _test_set_port_state_networknotfound(self, format):
@ -614,12 +619,32 @@ class APITest(unittest.TestCase):
LOG.debug("_test_delete_attachment_portnotfound - " \
"format:%s - END", format)
def _test_unparsable_data(self, format):
LOG.debug("_test_unparsable_data - " \
" format:%s - START", format)
data = "this is not json or xml"
method = 'POST'
content_type = "application/%s" % format
tenant_id = self.tenant_id
path = "/tenants/%(tenant_id)s/networks.%(format)s" % locals()
network_req = testlib.create_request(path, data, content_type, method)
network_res = network_req.get_response(self.api)
self.assertEqual(network_res.status_int, 400)
LOG.debug("_test_unparsable_data - " \
"format:%s - END", format)
def setUp(self):
options = {}
options['plugin_provider'] = 'quantum.plugins.SamplePlugin.FakePlugin'
self.api = server.APIRouterV01(options)
self.tenant_id = "test_tenant"
self.network_name = "test_network"
self._net_serializer = \
Serializer(server.networks.Controller._serialization_metadata)
self._port_serializer = \
Serializer(server.ports.Controller._serialization_metadata)
def tearDown(self):
"""Clear the test environment"""
@ -829,3 +854,9 @@ class APITest(unittest.TestCase):
def test_delete_attachment_portnotfound_json(self):
self._test_delete_attachment_portnotfound('json')
def test_unparsable_data_xml(self):
self._test_unparsable_data('xml')
def test_unparsable_data_json(self):
self._test_unparsable_data('json')

View File

@ -84,7 +84,7 @@ def delete_all_nets(client, tenant_id):
def create_net_with_attachments(net_name, iface_ids):
data = {'network': {'network-name': '%s' % net_name}}
data = {'network': {'net-name': '%s' % net_name}}
body = Serializer().serialize(data, CONTENT_TYPE)
res = client.do_request(tenant_id, 'POST',
"/networks." + FORMAT, body=body)