First working version of Quantum API
This commit is contained in:
parent
4d0029e879
commit
9f1c248826
@ -26,6 +26,7 @@ import webob.exc
|
|||||||
|
|
||||||
from quantum.api import faults
|
from quantum.api import faults
|
||||||
from quantum.api import networks
|
from quantum.api import networks
|
||||||
|
from quantum.api import ports
|
||||||
from quantum.common import flags
|
from quantum.common import flags
|
||||||
from quantum.common import wsgi
|
from quantum.common import wsgi
|
||||||
|
|
||||||
@ -33,18 +34,6 @@ from quantum.common import wsgi
|
|||||||
LOG = logging.getLogger('quantum.api')
|
LOG = logging.getLogger('quantum.api')
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
class FaultWrapper(wsgi.Middleware):
|
|
||||||
"""Calls down the middleware stack, making exceptions into faults."""
|
|
||||||
|
|
||||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
|
||||||
def __call__(self, req):
|
|
||||||
try:
|
|
||||||
return req.get_response(self.application)
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.exception(_("Caught error: %s"), unicode(ex))
|
|
||||||
exc = webob.exc.HTTPInternalServerError(explanation=unicode(ex))
|
|
||||||
return faults.Fault(exc)
|
|
||||||
|
|
||||||
|
|
||||||
class APIRouterV01(wsgi.Router):
|
class APIRouterV01(wsgi.Router):
|
||||||
"""
|
"""
|
||||||
@ -57,26 +46,33 @@ class APIRouterV01(wsgi.Router):
|
|||||||
super(APIRouterV01, self).__init__(mapper)
|
super(APIRouterV01, self).__init__(mapper)
|
||||||
|
|
||||||
def _setup_routes(self, mapper):
|
def _setup_routes(self, mapper):
|
||||||
#server_members = self.server_members
|
|
||||||
#server_members['action'] = 'POST'
|
|
||||||
|
|
||||||
#server_members['pause'] = 'POST'
|
uri_prefix = '/tenants/{tenant_id}/'
|
||||||
#server_members['unpause'] = 'POST'
|
mapper.resource('network',
|
||||||
#server_members['diagnostics'] = 'GET'
|
'networks',
|
||||||
#server_members['actions'] = 'GET'
|
controller=networks.Controller(),
|
||||||
#server_members['suspend'] = 'POST'
|
path_prefix=uri_prefix)
|
||||||
#server_members['resume'] = 'POST'
|
mapper.resource("port", "ports", controller=ports.Controller(),
|
||||||
#server_members['rescue'] = 'POST'
|
parent_resource=dict(member_name='network',
|
||||||
#server_members['unrescue'] = 'POST'
|
collection_name= uri_prefix + 'networks'))
|
||||||
#server_members['reset_network'] = 'POST'
|
|
||||||
#server_members['inject_network_info'] = 'POST'
|
mapper.connect("get_resource",
|
||||||
mapper.resource("/tenants/{tenant_id}/network", "/tenants/{tenant_id}/networks", controller=networks.Controller())
|
uri_prefix + 'networks/{network_id}/ports/{id}/attachment{.format}',
|
||||||
|
controller=ports.Controller(),
|
||||||
|
action="get_resource",
|
||||||
|
conditions=dict(method=['GET']))
|
||||||
|
mapper.connect("attach_resource",
|
||||||
|
uri_prefix + 'networks/{network_id}/ports/{id}/attachment{.format}',
|
||||||
|
controller=ports.Controller(),
|
||||||
|
action="attach_resource",
|
||||||
|
conditions=dict(method=['PUT']))
|
||||||
|
mapper.connect("detach_resource",
|
||||||
|
uri_prefix + 'networks/{network_id}/ports/{id}/attachment{.format}',
|
||||||
|
controller=ports.Controller(),
|
||||||
|
action="detach_resource",
|
||||||
|
conditions=dict(method=['DELETE']))
|
||||||
|
|
||||||
print "AFTER MAPPING"
|
print "AFTER MAPPING"
|
||||||
print mapper
|
print mapper
|
||||||
for route in mapper.matchlist:
|
for route in mapper.matchlist:
|
||||||
print "Found route:%s %s" %(route.defaults,route.conditions)
|
print "Found route:%s %s" %(route.defaults,route.conditions)
|
||||||
#mapper.resource("port", "ports", controller=ports.Controller(),
|
|
||||||
# collection=dict(public='GET', private='GET'),
|
|
||||||
# parent_resource=dict(member_name='network',
|
|
||||||
# collection_name='networks'))
|
|
||||||
|
|
||||||
|
@ -15,7 +15,54 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from quantum import manager
|
||||||
|
from quantum.common import wsgi
|
||||||
|
|
||||||
XML_NS_V01 = 'http://netstack.org/quantum/api/v0.1'
|
XML_NS_V01 = 'http://netstack.org/quantum/api/v0.1'
|
||||||
XML_NS_V10 = 'http://netstack.org/quantum/api/v1.0'
|
XML_NS_V10 = 'http://netstack.org/quantum/api/v1.0'
|
||||||
|
LOG = logging.getLogger('quantum.api.api_common')
|
||||||
|
|
||||||
|
class QuantumController(wsgi.Controller):
|
||||||
|
""" Base controller class for Quantum API """
|
||||||
|
|
||||||
|
def __init__(self, plugin_conf_file=None):
|
||||||
|
self._setup_network_manager()
|
||||||
|
super(QuantumController, self).__init__()
|
||||||
|
|
||||||
|
def _parse_request_params(self, req, params):
|
||||||
|
results = {}
|
||||||
|
for param in params:
|
||||||
|
param_name = param['param-name']
|
||||||
|
param_value = None
|
||||||
|
# 1- parse request body
|
||||||
|
if req.body:
|
||||||
|
des_body = self._deserialize(req.body, req.best_match_content_type())
|
||||||
|
data = des_body and des_body.get(self._resource_name, None)
|
||||||
|
param_value = data and data.get(param_name, None)
|
||||||
|
if not param_value:
|
||||||
|
# 2- parse request headers
|
||||||
|
# prepend param name with a 'x-' prefix
|
||||||
|
param_value = req.headers.get("x-" + param_name, None)
|
||||||
|
# 3- parse request query parameters
|
||||||
|
if not param_value:
|
||||||
|
try:
|
||||||
|
param_value = req.str_GET[param_name]
|
||||||
|
except KeyError:
|
||||||
|
#param not found
|
||||||
|
pass
|
||||||
|
if not param_value and param['required']:
|
||||||
|
msg = ("Failed to parse request. " +
|
||||||
|
"Parameter: %(param_name)s not specified" % locals())
|
||||||
|
for line in msg.split('\n'):
|
||||||
|
LOG.error(line)
|
||||||
|
raise exc.HTTPBadRequest(msg)
|
||||||
|
results[param_name]=param_value or param.get('default-value')
|
||||||
|
return results
|
||||||
|
|
||||||
|
def _setup_network_manager(self):
|
||||||
|
self.network_manager=manager.QuantumManager().get_manager()
|
||||||
|
|
||||||
|
@ -28,11 +28,12 @@ class Fault(webob.exc.HTTPException):
|
|||||||
_fault_names = {
|
_fault_names = {
|
||||||
400: "malformedRequest",
|
400: "malformedRequest",
|
||||||
401: "unauthorized",
|
401: "unauthorized",
|
||||||
402: "networkNotFound",
|
420: "networkNotFound",
|
||||||
403: "requestedStateInvalid",
|
421: "networkInUse",
|
||||||
460: "networkInUse",
|
430: "portNotFound",
|
||||||
461: "alreadyAttached",
|
431: "requestedStateInvalid",
|
||||||
462: "portInUse",
|
432: "portInUse",
|
||||||
|
440: "alreadyAttached",
|
||||||
470: "serviceUnavailable",
|
470: "serviceUnavailable",
|
||||||
471: "pluginFault"
|
471: "pluginFault"
|
||||||
}
|
}
|
||||||
@ -50,8 +51,8 @@ class Fault(webob.exc.HTTPException):
|
|||||||
fault_data = {
|
fault_data = {
|
||||||
fault_name: {
|
fault_name: {
|
||||||
'code': code,
|
'code': code,
|
||||||
'message': self.wrapped_exc.explanation}}
|
'message': self.wrapped_exc.explanation,
|
||||||
#TODO (salvatore-orlando): place over-limit stuff here
|
'detail': self.wrapped_exc.detail}}
|
||||||
# 'code' is an attribute on the fault tag itself
|
# 'code' is an attribute on the fault tag itself
|
||||||
metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
|
metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
|
||||||
default_xmlns = common.XML_NS_V10
|
default_xmlns = common.XML_NS_V10
|
||||||
@ -60,3 +61,85 @@ class Fault(webob.exc.HTTPException):
|
|||||||
self.wrapped_exc.body = serializer.serialize(fault_data, content_type)
|
self.wrapped_exc.body = serializer.serialize(fault_data, content_type)
|
||||||
self.wrapped_exc.content_type = content_type
|
self.wrapped_exc.content_type = content_type
|
||||||
return self.wrapped_exc
|
return self.wrapped_exc
|
||||||
|
|
||||||
|
class NetworkNotFound(webob.exc.HTTPClientError):
|
||||||
|
"""
|
||||||
|
subclass of :class:`~HTTPClientError`
|
||||||
|
|
||||||
|
This indicates that the server did not find the network specified
|
||||||
|
in the HTTP request
|
||||||
|
|
||||||
|
code: 420, title: Network not Found
|
||||||
|
"""
|
||||||
|
code = 420
|
||||||
|
title = 'Network not Found'
|
||||||
|
explanation = ('Unable to find a network with the specified identifier.')
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkInUse(webob.exc.HTTPClientError):
|
||||||
|
"""
|
||||||
|
subclass of :class:`~HTTPClientError`
|
||||||
|
|
||||||
|
This indicates that the server could not delete the network as there is
|
||||||
|
at least an attachment plugged into its ports
|
||||||
|
|
||||||
|
code: 421, title: Network In Use
|
||||||
|
"""
|
||||||
|
code = 421
|
||||||
|
title = 'Network in Use'
|
||||||
|
explanation = ('Unable to remove the network: attachments still plugged.')
|
||||||
|
|
||||||
|
|
||||||
|
class PortNotFound(webob.exc.HTTPClientError):
|
||||||
|
"""
|
||||||
|
subclass of :class:`~HTTPClientError`
|
||||||
|
|
||||||
|
This indicates that the server did not find the port specified
|
||||||
|
in the HTTP request for a given network
|
||||||
|
|
||||||
|
code: 430, title: Port not Found
|
||||||
|
"""
|
||||||
|
code = 430
|
||||||
|
title = 'Port not Found'
|
||||||
|
explanation = ('Unable to find a port with the specified identifier.')
|
||||||
|
|
||||||
|
|
||||||
|
class RequestedStateInvalid(webob.exc.HTTPClientError):
|
||||||
|
"""
|
||||||
|
subclass of :class:`~HTTPClientError`
|
||||||
|
|
||||||
|
This indicates that the server could not update the port state to
|
||||||
|
to the request value
|
||||||
|
|
||||||
|
code: 431, title: Requested State Invalid
|
||||||
|
"""
|
||||||
|
code = 431
|
||||||
|
title = 'Requested State Invalid'
|
||||||
|
explanation = ('Unable to update port state with specified value.')
|
||||||
|
|
||||||
|
|
||||||
|
class PortInUse(webob.exc.HTTPClientError):
|
||||||
|
"""
|
||||||
|
subclass of :class:`~HTTPClientError`
|
||||||
|
|
||||||
|
This indicates that the server could not remove o port or attach
|
||||||
|
a resource to it because there is an attachment plugged into the port
|
||||||
|
|
||||||
|
code: 432, title: PortInUse
|
||||||
|
"""
|
||||||
|
code = 432
|
||||||
|
title = 'Port in Use'
|
||||||
|
explanation = ('A resource is currently attached to the logical port')
|
||||||
|
|
||||||
|
class AlreadyAttached(webob.exc.HTTPClientError):
|
||||||
|
"""
|
||||||
|
subclass of :class:`~HTTPClientError`
|
||||||
|
|
||||||
|
This indicates that the server refused an attempt to re-attach a resource
|
||||||
|
already attached to the network
|
||||||
|
|
||||||
|
code: 440, title: AlreadyAttached
|
||||||
|
"""
|
||||||
|
code = 440
|
||||||
|
title = 'Already Attached'
|
||||||
|
explanation = ('The resource is already attached to another port')
|
||||||
|
@ -13,24 +13,19 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import httplib
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from webob import exc
|
from webob import exc
|
||||||
from xml.dom import minidom
|
|
||||||
|
|
||||||
from quantum import manager
|
from quantum.api import api_common as common
|
||||||
from quantum.common import exceptions as exception
|
|
||||||
from quantum.common import flags
|
|
||||||
from quantum.common import wsgi
|
|
||||||
from quantum.api import faults
|
from quantum.api import faults
|
||||||
from quantum.api.views import networks as networks_view
|
from quantum.api.views import networks as networks_view
|
||||||
|
from quantum.common import exceptions as exception
|
||||||
|
|
||||||
LOG = logging.getLogger('quantum.api.networks')
|
LOG = logging.getLogger('quantum.api.networks')
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(common.QuantumController):
|
||||||
""" Network API controller for Quantum API """
|
""" Network API controller for Quantum API """
|
||||||
|
|
||||||
_network_ops_param_list = [{
|
_network_ops_param_list = [{
|
||||||
@ -41,40 +36,16 @@ class Controller(wsgi.Controller):
|
|||||||
"application/xml": {
|
"application/xml": {
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"network": ["id","name"],
|
"network": ["id","name"],
|
||||||
"link": ["rel", "type", "href"],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, plugin_conf_file=None):
|
def __init__(self, plugin_conf_file=None):
|
||||||
self._setup_network_manager()
|
self._resource_name = 'network'
|
||||||
super(Controller, self).__init__()
|
super(Controller, self).__init__()
|
||||||
|
|
||||||
def _parse_request_params(self, req, params):
|
|
||||||
results = {}
|
|
||||||
for param in params:
|
|
||||||
param_name = param['param-name']
|
|
||||||
# 1- parse request body
|
|
||||||
# 2- parse request headers
|
|
||||||
# prepend param name with a 'x-' prefix
|
|
||||||
param_value = req.headers.get("x-" + param_name, None)
|
|
||||||
# 3- parse request query parameters
|
|
||||||
if not param_value:
|
|
||||||
param_value = req.str_GET[param_name]
|
|
||||||
if not param_value and param['required']:
|
|
||||||
msg = ("Failed to parse request. " +
|
|
||||||
"Parameter: %(param)s not specified" % locals())
|
|
||||||
for line in msg.split('\n'):
|
|
||||||
LOG.error(line)
|
|
||||||
raise exc.HTTPBadRequest(msg)
|
|
||||||
results[param_name]=param_value
|
|
||||||
return results
|
|
||||||
|
|
||||||
def _setup_network_manager(self):
|
|
||||||
self.network_manager=manager.QuantumManager().get_manager()
|
|
||||||
|
|
||||||
def index(self, req, tenant_id):
|
def index(self, req, tenant_id):
|
||||||
""" Returns a list of network names and ids """
|
""" Returns a list of network ids """
|
||||||
#TODO: this should be for a given tenant!!!
|
#TODO: this should be for a given tenant!!!
|
||||||
return self._items(req, tenant_id, is_detail=False)
|
return self._items(req, tenant_id, is_detail=False)
|
||||||
|
|
||||||
@ -87,7 +58,7 @@ class Controller(wsgi.Controller):
|
|||||||
return dict(networks=result)
|
return dict(networks=result)
|
||||||
|
|
||||||
def show(self, req, tenant_id, id):
|
def show(self, req, tenant_id, id):
|
||||||
""" Returns network details by network id """
|
""" Returns network details for the given network id """
|
||||||
try:
|
try:
|
||||||
network = self.network_manager.get_network_details(
|
network = self.network_manager.get_network_details(
|
||||||
tenant_id,id)
|
tenant_id,id)
|
||||||
@ -95,14 +66,17 @@ class Controller(wsgi.Controller):
|
|||||||
#build response with details
|
#build response with details
|
||||||
result = builder.build(network, True)
|
result = builder.build(network, True)
|
||||||
return dict(networks=result)
|
return dict(networks=result)
|
||||||
except exception.NotFound:
|
except exception.NetworkNotFound as e:
|
||||||
return faults.Fault(exc.HTTPNotFound())
|
return faults.Fault(faults.NetworkNotFound(e))
|
||||||
|
|
||||||
def create(self, req, tenant_id):
|
def create(self, req, tenant_id):
|
||||||
""" Creates a new network for a given tenant """
|
""" Creates a new network for a given tenant """
|
||||||
#look for network name in request
|
#look for network name in request
|
||||||
req_params = \
|
try:
|
||||||
self._parse_request_params(req, self._network_ops_param_list)
|
req_params = \
|
||||||
|
self._parse_request_params(req, self._network_ops_param_list)
|
||||||
|
except exc.HTTPError as e:
|
||||||
|
return faults.Fault(e)
|
||||||
network = self.network_manager.create_network(tenant_id, req_params['network-name'])
|
network = self.network_manager.create_network(tenant_id, req_params['network-name'])
|
||||||
builder = networks_view.get_view_builder(req)
|
builder = networks_view.get_view_builder(req)
|
||||||
result = builder.build(network)
|
result = builder.build(network)
|
||||||
@ -111,36 +85,26 @@ class Controller(wsgi.Controller):
|
|||||||
def update(self, req, tenant_id, id):
|
def update(self, req, tenant_id, id):
|
||||||
""" Updates the name for the network with the given id """
|
""" Updates the name for the network with the given id """
|
||||||
try:
|
try:
|
||||||
network_name = req.headers['x-network-name']
|
req_params = \
|
||||||
except KeyError as e:
|
self._parse_request_params(req, self._network_ops_param_list)
|
||||||
msg = ("Failed to create network. Got error: %(e)s" % locals())
|
except exc.HTTPError as e:
|
||||||
for line in msg.split('\n'):
|
return faults.Fault(e)
|
||||||
LOG.error(line)
|
try:
|
||||||
raise exc.HTTPBadRequest(msg)
|
network = self.network_manager.rename_network(tenant_id,
|
||||||
|
id,req_params['network-name'])
|
||||||
network = self.network_manager.rename_network(tenant_id,
|
|
||||||
id,network_name)
|
|
||||||
if not network:
|
|
||||||
raise exc.HTTPNotFound("Network %(id)s could not be found" % locals())
|
|
||||||
builder = networks_view.get_view_builder(req)
|
|
||||||
result = builder.build(network, True)
|
|
||||||
return dict(networks=result)
|
|
||||||
|
|
||||||
|
builder = networks_view.get_view_builder(req)
|
||||||
|
result = builder.build(network, True)
|
||||||
|
return dict(networks=result)
|
||||||
|
except exception.NetworkNotFound as e:
|
||||||
|
return faults.Fault(faults.NetworkNotFound(e))
|
||||||
|
|
||||||
def delete(self, req, tenant_id, id):
|
def delete(self, req, tenant_id, id):
|
||||||
""" Destroys the network with the given id """
|
""" Destroys the network with the given id """
|
||||||
try:
|
try:
|
||||||
network_name = req.headers['x-network-name']
|
self.network_manager.delete_network(tenant_id, id)
|
||||||
except KeyError as e:
|
return exc.HTTPAccepted()
|
||||||
msg = ("Failed to create network. Got error: %(e)s" % locals())
|
except exception.NetworkNotFound as e:
|
||||||
for line in msg.split('\n'):
|
return faults.Fault(faults.NetworkNotFound(e))
|
||||||
LOG.error(line)
|
except exception.NetworkInUse as e:
|
||||||
raise exc.HTTPBadRequest(msg)
|
return faults.Fault(faults.NetworkInUse(e))
|
||||||
|
|
||||||
network = self.network_manager.delete_network(tenant_id, id)
|
|
||||||
if not network:
|
|
||||||
raise exc.HTTPNotFound("Network %(id)s could not be found" % locals())
|
|
||||||
|
|
||||||
return exc.HTTPAccepted()
|
|
||||||
|
|
||||||
|
|
||||||
|
48
quantum/api/views/ports.py
Normal file
48
quantum/api/views/ports.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 Citrix Systems
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
def get_view_builder(req):
|
||||||
|
base_url = req.application_url
|
||||||
|
return ViewBuilder(base_url)
|
||||||
|
|
||||||
|
|
||||||
|
class ViewBuilder(object):
|
||||||
|
|
||||||
|
def __init__(self, base_url):
|
||||||
|
"""
|
||||||
|
:param base_url: url of the root wsgi application
|
||||||
|
"""
|
||||||
|
self.base_url = base_url
|
||||||
|
|
||||||
|
def build(self, port_data, is_detail=False):
|
||||||
|
"""Generic method used to generate a port entity."""
|
||||||
|
print "PORT-DATA:%s" %port_data
|
||||||
|
if is_detail:
|
||||||
|
port = self._build_detail(port_data)
|
||||||
|
else:
|
||||||
|
port = self._build_simple(port_data)
|
||||||
|
return port
|
||||||
|
|
||||||
|
def _build_simple(self, port_data):
|
||||||
|
"""Return a simple model of a server."""
|
||||||
|
return dict(port=dict(id=port_data['port-id']))
|
||||||
|
|
||||||
|
def _build_detail(self, port_data):
|
||||||
|
"""Return a simple model of a server."""
|
||||||
|
return dict(port=dict(id=port_data['port-id'],
|
||||||
|
state=port_data['port-state']))
|
@ -21,10 +21,30 @@ Quantum-type exceptions. SHOULD include dedicated exception logging.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
|
class QuantumException(Exception):
|
||||||
|
"""Base Quantum Exception
|
||||||
|
|
||||||
|
Taken from nova.exception.NovaException
|
||||||
|
To correctly use this class, inherit from it and define
|
||||||
|
a 'message' property. That message will get printf'd
|
||||||
|
with the keyword arguments provided to the constructor.
|
||||||
|
|
||||||
|
"""
|
||||||
|
message = _("An unknown exception occurred.")
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
try:
|
||||||
|
self._error_string = self.message % kwargs
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
# at least get the core message out if something happened
|
||||||
|
self._error_string = self.message
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self._error_string
|
||||||
|
|
||||||
class ProcessExecutionError(IOError):
|
class ProcessExecutionError(IOError):
|
||||||
def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
|
def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
|
||||||
description=None):
|
description=None):
|
||||||
@ -49,7 +69,7 @@ class ApiError(Error):
|
|||||||
super(ApiError, self).__init__('%s: %s' % (code, message))
|
super(ApiError, self).__init__('%s: %s' % (code, message))
|
||||||
|
|
||||||
|
|
||||||
class NotFound(Error):
|
class NotFound(QuantumException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -57,6 +77,34 @@ class ClassNotFound(NotFound):
|
|||||||
message = _("Class %(class_name)s could not be found")
|
message = _("Class %(class_name)s could not be found")
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkNotFound(NotFound):
|
||||||
|
message = _("Network %(net_id)s could not be found")
|
||||||
|
|
||||||
|
|
||||||
|
class PortNotFound(NotFound):
|
||||||
|
message = _("Port %(port_id)s could not be found " \
|
||||||
|
"on network %(net_id)s")
|
||||||
|
|
||||||
|
|
||||||
|
class StateInvalid(QuantumException):
|
||||||
|
message = _("Unsupported port state: %(port_state)s")
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkInUse(QuantumException):
|
||||||
|
message = _("Unable to complete operation on network %(net_id)s. " \
|
||||||
|
"There is one or more attachments plugged into its ports.")
|
||||||
|
|
||||||
|
|
||||||
|
class PortInUse(QuantumException):
|
||||||
|
message = _("Unable to complete operation on port %(port_id)s " \
|
||||||
|
"for network %(net_id)s. The attachment '%(att_id)s" \
|
||||||
|
"is plugged into the logical port.")
|
||||||
|
|
||||||
|
class AlreadyAttached(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 port %(att_port_id)s")
|
||||||
|
|
||||||
class Duplicate(Error):
|
class Duplicate(Error):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
# @author: Somik Behera, Nicira Networks, Inc.
|
# @author: Somik Behera, Nicira Networks, Inc.
|
||||||
|
|
||||||
|
from quantum.common import exceptions as exc
|
||||||
|
|
||||||
class QuantumEchoPlugin(object):
|
class QuantumEchoPlugin(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -267,19 +269,69 @@ class FakePlugin(object):
|
|||||||
in-memory data structures to aid in quantum
|
in-memory data structures to aid in quantum
|
||||||
client/cli/api development
|
client/cli/api development
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#static data for networks and ports
|
||||||
|
_port_dict_1 = {
|
||||||
|
1 : {'port-id': 1,
|
||||||
|
'port-state': 'DOWN',
|
||||||
|
'attachment': None},
|
||||||
|
2 : {'port-id': 2,
|
||||||
|
'port-state':'UP',
|
||||||
|
'attachment': None}
|
||||||
|
}
|
||||||
|
_port_dict_2 = {
|
||||||
|
1 : {'port-id': 1,
|
||||||
|
'port-state': 'UP',
|
||||||
|
'attachment': 'SomeFormOfVIFID'},
|
||||||
|
2 : {'port-id': 2,
|
||||||
|
'port-state':'DOWN',
|
||||||
|
'attachment': None}
|
||||||
|
}
|
||||||
|
_networks={'001':
|
||||||
|
{
|
||||||
|
'net-id':'001',
|
||||||
|
'net-name':'pippotest',
|
||||||
|
'net-ports': _port_dict_1
|
||||||
|
},
|
||||||
|
'002':
|
||||||
|
{
|
||||||
|
'net-id':'002',
|
||||||
|
'net-name':'cicciotest',
|
||||||
|
'net-ports': _port_dict_2
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
#add a first sample network on init
|
FakePlugin._net_counter=len(FakePlugin._networks)
|
||||||
self._networks={'001':
|
|
||||||
{
|
def _get_network(self, tenant_id, network_id):
|
||||||
'net-id':'001',
|
network = FakePlugin._networks.get(network_id)
|
||||||
'net-name':'pippotest'
|
if not network:
|
||||||
},
|
raise exc.NetworkNotFound(net_id=network_id)
|
||||||
'002':
|
return network
|
||||||
{
|
|
||||||
'net-id':'002',
|
|
||||||
'net-name':'cicciotest'
|
def _get_port(self, tenant_id, network_id, port_id):
|
||||||
}}
|
net = self._get_network(tenant_id, network_id)
|
||||||
self._net_counter=len(self._networks)
|
port = net['net-ports'].get(int(port_id))
|
||||||
|
if not port:
|
||||||
|
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
|
||||||
|
return port
|
||||||
|
|
||||||
|
def _validate_port_state(self, port_state):
|
||||||
|
if port_state.upper() not in ('UP','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['net-ports'].values():
|
||||||
|
if port['attachment'] == remote_interface_id:
|
||||||
|
raise exc.AlreadyAttached(net_id = network_id,
|
||||||
|
port_id = port_id,
|
||||||
|
att_id = port['attachment'],
|
||||||
|
att_port_id = port['port-id'])
|
||||||
|
|
||||||
def get_all_networks(self, tenant_id):
|
def get_all_networks(self, tenant_id):
|
||||||
"""
|
"""
|
||||||
@ -288,7 +340,7 @@ class FakePlugin(object):
|
|||||||
the specified tenant.
|
the specified tenant.
|
||||||
"""
|
"""
|
||||||
print("get_all_networks() called\n")
|
print("get_all_networks() called\n")
|
||||||
return self._networks.values()
|
return FakePlugin._networks.values()
|
||||||
|
|
||||||
def get_network_details(self, tenant_id, net_id):
|
def get_network_details(self, tenant_id, net_id):
|
||||||
"""
|
"""
|
||||||
@ -296,8 +348,7 @@ class FakePlugin(object):
|
|||||||
are attached to the network
|
are attached to the network
|
||||||
"""
|
"""
|
||||||
print("get_network_details() called\n")
|
print("get_network_details() called\n")
|
||||||
return self._networks.get(net_id)
|
return self._get_network(tenant_id, net_id)
|
||||||
|
|
||||||
|
|
||||||
def create_network(self, tenant_id, net_name):
|
def create_network(self, tenant_id, net_name):
|
||||||
"""
|
"""
|
||||||
@ -305,29 +356,34 @@ class FakePlugin(object):
|
|||||||
a symbolic name.
|
a symbolic name.
|
||||||
"""
|
"""
|
||||||
print("create_network() called\n")
|
print("create_network() called\n")
|
||||||
self._net_counter += 1
|
FakePlugin._net_counter += 1
|
||||||
new_net_id=("0" * (3 - len(str(self._net_counter)))) + \
|
new_net_id=("0" * (3 - len(str(FakePlugin._net_counter)))) + \
|
||||||
str(self._net_counter)
|
str(FakePlugin._net_counter)
|
||||||
print new_net_id
|
print new_net_id
|
||||||
new_net_dict={'net-id':new_net_id,
|
new_net_dict={'net-id':new_net_id,
|
||||||
'net-name':net_name}
|
'net-name':net_name,
|
||||||
self._networks[new_net_id]=new_net_dict
|
'net-ports': {}}
|
||||||
|
FakePlugin._networks[new_net_id]=new_net_dict
|
||||||
# return network_id of the created network
|
# return network_id of the created network
|
||||||
return new_net_dict
|
return new_net_dict
|
||||||
|
|
||||||
|
|
||||||
def delete_network(self, tenant_id, net_id):
|
def delete_network(self, tenant_id, net_id):
|
||||||
"""
|
"""
|
||||||
Deletes the network with the specified network identifier
|
Deletes the network with the specified network identifier
|
||||||
belonging to the specified tenant.
|
belonging to the specified tenant.
|
||||||
"""
|
"""
|
||||||
print("delete_network() called\n")
|
print("delete_network() called\n")
|
||||||
net = self._networks.get(net_id)
|
net = FakePlugin._networks.get(net_id)
|
||||||
|
# Verify that no attachments are plugged into the network
|
||||||
if net:
|
if net:
|
||||||
self._networks.pop(net_id)
|
if net['net-ports']:
|
||||||
|
for port in net['net-ports'].values():
|
||||||
|
if port['attachment']:
|
||||||
|
raise exc.NetworkInUse(net_id=net_id)
|
||||||
|
FakePlugin._networks.pop(net_id)
|
||||||
return net
|
return net
|
||||||
return None
|
# Network not found
|
||||||
|
raise exc.NetworkNotFound(net_id=net_id)
|
||||||
|
|
||||||
def rename_network(self, tenant_id, net_id, new_name):
|
def rename_network(self, tenant_id, net_id, new_name):
|
||||||
"""
|
"""
|
||||||
@ -335,32 +391,54 @@ class FakePlugin(object):
|
|||||||
Virtual Network.
|
Virtual Network.
|
||||||
"""
|
"""
|
||||||
print("rename_network() called\n")
|
print("rename_network() called\n")
|
||||||
net = self._networks.get(net_id, None)
|
net = self._get_network(tenant_id, net_id)
|
||||||
if net:
|
net['net-name']=new_name
|
||||||
net['net-name']=new_name
|
return net
|
||||||
return net
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
#TODO - neeed to update methods from this point onwards
|
|
||||||
def get_all_ports(self, tenant_id, net_id):
|
def get_all_ports(self, tenant_id, net_id):
|
||||||
"""
|
"""
|
||||||
Retrieves all port identifiers belonging to the
|
Retrieves all port identifiers belonging to the
|
||||||
specified Virtual Network.
|
specified Virtual Network.
|
||||||
"""
|
"""
|
||||||
print("get_all_ports() called\n")
|
print("get_all_ports() called\n")
|
||||||
port_ids_on_net = ["2", "3", "4"]
|
network = self._get_network(tenant_id, net_id)
|
||||||
return port_ids_on_net
|
ports_on_net = network['net-ports'].values()
|
||||||
|
return ports_on_net
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
print("get_port_details() called\n")
|
||||||
|
return self._get_port(tenant_id, net_id, port_id)
|
||||||
|
|
||||||
def create_port(self, tenant_id, net_id):
|
def create_port(self, tenant_id, net_id, port_state=None):
|
||||||
"""
|
"""
|
||||||
Creates a port on the specified Virtual Network.
|
Creates a port on the specified Virtual Network.
|
||||||
"""
|
"""
|
||||||
print("create_port() called\n")
|
print("create_port() called\n")
|
||||||
#return the port id
|
net = self._get_network(tenant_id, net_id)
|
||||||
return 201
|
# check port state
|
||||||
|
# TODO(salvatore-orlando): Validate port state in API?
|
||||||
|
self._validate_port_state(port_state)
|
||||||
|
ports = net['net-ports']
|
||||||
|
new_port_id = max(ports.keys())+1
|
||||||
|
new_port_dict = {'port-id':new_port_id,
|
||||||
|
'port-state': port_state,
|
||||||
|
'attachment': None}
|
||||||
|
ports[new_port_id] = new_port_dict
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
print("create_port() called\n")
|
||||||
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
|
self._validate_port_state(port_state)
|
||||||
|
port['port-state'] = port_state
|
||||||
|
return port
|
||||||
|
|
||||||
def delete_port(self, tenant_id, net_id, port_id):
|
def delete_port(self, tenant_id, net_id, port_id):
|
||||||
"""
|
"""
|
||||||
@ -370,33 +448,15 @@ class FakePlugin(object):
|
|||||||
is deleted.
|
is deleted.
|
||||||
"""
|
"""
|
||||||
print("delete_port() called\n")
|
print("delete_port() called\n")
|
||||||
|
net = self._get_network(tenant_id, net_id)
|
||||||
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
def get_port_details(self, tenant_id, net_id, port_id):
|
if port['attachment']:
|
||||||
"""
|
raise exc.PortInUse(net_id=net_id,port_id=port_id,
|
||||||
This method allows the user to retrieve a remote interface
|
att_id=port['attachment'])
|
||||||
that is attached to this particular port.
|
try:
|
||||||
"""
|
net['net-ports'].pop(int(port_id))
|
||||||
print("get_port_details() called\n")
|
except KeyError:
|
||||||
#returns the remote interface UUID
|
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
|
||||||
return "/tenant1/networks/net_id/portid/vif2.1"
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
print("plug_interface() called\n")
|
|
||||||
|
|
||||||
|
|
||||||
def unplug_interface(self, tenant_id, net_id, port_id):
|
|
||||||
"""
|
|
||||||
Detaches a remote interface from the specified port on the
|
|
||||||
specified Virtual Network.
|
|
||||||
"""
|
|
||||||
print("unplug_interface() called\n")
|
|
||||||
|
|
||||||
|
|
||||||
def get_interface_details(self, tenant_id, net_id, port_id):
|
def get_interface_details(self, tenant_id, net_id, port_id):
|
||||||
"""
|
"""
|
||||||
@ -404,10 +464,36 @@ class FakePlugin(object):
|
|||||||
particular port.
|
particular port.
|
||||||
"""
|
"""
|
||||||
print("get_interface_details() called\n")
|
print("get_interface_details() called\n")
|
||||||
#returns the remote interface UUID
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
return "/tenant1/networks/net_id/portid/vif2.0"
|
return port['attachment']
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
print("plug_interface() called\n")
|
||||||
|
# Validate attachment
|
||||||
|
self._validate_attachment(tenant_id, net_id, port_id,
|
||||||
|
remote_interface_id)
|
||||||
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
|
if port['attachment']:
|
||||||
|
raise exc.PortInUse(net_id=net_id,port_id=port_id,
|
||||||
|
att_id=port['attachment'])
|
||||||
|
port['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.
|
||||||
|
"""
|
||||||
|
print("unplug_interface() called\n")
|
||||||
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
|
# TODO(salvatore-orlando):
|
||||||
|
# Should unplug on port without attachment raise an Error?
|
||||||
|
port['attachment'] = None
|
||||||
|
|
||||||
|
#TODO - neeed to update methods from this point onwards
|
||||||
def get_all_attached_interfaces(self, tenant_id, net_id):
|
def get_all_attached_interfaces(self, tenant_id, net_id):
|
||||||
"""
|
"""
|
||||||
Retrieves all remote interfaces that are attached to
|
Retrieves all remote interfaces that are attached to
|
||||||
|
@ -79,12 +79,20 @@ class QuantumPluginBase(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def create_port(self, tenant_id, net_id):
|
def create_port(self, tenant_id, net_id, port_state=None):
|
||||||
"""
|
"""
|
||||||
Creates a port on the specified Virtual Network.
|
Creates a port on the specified Virtual Network.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def update_port(self, tenant_id, net_id, port_id, port_state):
|
||||||
|
"""
|
||||||
|
Updates the state of a specific port on the
|
||||||
|
specified Virtual Network
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def delete_port(self, tenant_id, net_id, port_id):
|
def delete_port(self, tenant_id, net_id, port_id):
|
||||||
"""
|
"""
|
||||||
|
0
test_scripts/__init__.py
Normal file
0
test_scripts/__init__.py
Normal file
98
test_scripts/miniclient.py
Normal file
98
test_scripts/miniclient.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 Citrix Systems
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import httplib
|
||||||
|
import socket
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
class MiniClient(object):
|
||||||
|
|
||||||
|
"""A base client class - derived from Glance.BaseClient"""
|
||||||
|
|
||||||
|
action_prefix = '/v0.1/tenants/{tenant_id}'
|
||||||
|
|
||||||
|
def __init__(self, host, port, use_ssl):
|
||||||
|
"""
|
||||||
|
Creates a new client to some service.
|
||||||
|
|
||||||
|
:param host: The host where service resides
|
||||||
|
:param port: The port where service resides
|
||||||
|
:param use_ssl: Should we use HTTPS?
|
||||||
|
"""
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.use_ssl = use_ssl
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
def get_connection_type(self):
|
||||||
|
"""
|
||||||
|
Returns the proper connection type
|
||||||
|
"""
|
||||||
|
if self.use_ssl:
|
||||||
|
return httplib.HTTPSConnection
|
||||||
|
else:
|
||||||
|
return httplib.HTTPConnection
|
||||||
|
|
||||||
|
def do_request(self, tenant, method, action, body=None,
|
||||||
|
headers=None, params=None):
|
||||||
|
"""
|
||||||
|
Connects to the server and issues a request.
|
||||||
|
Returns the result data, or raises an appropriate exception if
|
||||||
|
HTTP status code is not 2xx
|
||||||
|
|
||||||
|
:param method: HTTP method ("GET", "POST", "PUT", etc...)
|
||||||
|
:param body: string of data to send, or None (default)
|
||||||
|
:param headers: mapping of key/value pairs to add as headers
|
||||||
|
:param params: dictionary of key/value pairs to add to append
|
||||||
|
to action
|
||||||
|
|
||||||
|
"""
|
||||||
|
action = MiniClient.action_prefix + action
|
||||||
|
action = action.replace('{tenant_id}',tenant)
|
||||||
|
if type(params) is dict:
|
||||||
|
action += '?' + urllib.urlencode(params)
|
||||||
|
|
||||||
|
try:
|
||||||
|
connection_type = self.get_connection_type()
|
||||||
|
headers = headers or {}
|
||||||
|
|
||||||
|
# Open connection and send request
|
||||||
|
c = connection_type(self.host, self.port)
|
||||||
|
c.request(method, action, body, headers)
|
||||||
|
res = c.getresponse()
|
||||||
|
status_code = self.get_status_code(res)
|
||||||
|
if status_code in (httplib.OK,
|
||||||
|
httplib.CREATED,
|
||||||
|
httplib.ACCEPTED,
|
||||||
|
httplib.NO_CONTENT):
|
||||||
|
return res
|
||||||
|
else:
|
||||||
|
raise Exception("Server returned error: %s" % res.read())
|
||||||
|
|
||||||
|
except (socket.error, IOError), e:
|
||||||
|
raise Exception("Unable to connect to "
|
||||||
|
"server. Got error: %s" % e)
|
||||||
|
|
||||||
|
def get_status_code(self, response):
|
||||||
|
"""
|
||||||
|
Returns the integer status code from the response, which
|
||||||
|
can be either a Webob.Response (used in testing) or httplib.Response
|
||||||
|
"""
|
||||||
|
if hasattr(response, 'status_int'):
|
||||||
|
return response.status_int
|
||||||
|
else:
|
||||||
|
return response.status
|
150
test_scripts/tests.py
Normal file
150
test_scripts/tests.py
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 Citrix Systems
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
|
||||||
|
gettext.install('quantum', unicode=1)
|
||||||
|
|
||||||
|
from miniclient import MiniClient
|
||||||
|
from quantum.common.wsgi import Serializer
|
||||||
|
|
||||||
|
HOST = '127.0.0.1'
|
||||||
|
PORT = 9696
|
||||||
|
USE_SSL = False
|
||||||
|
TENANT_ID = 'totore'
|
||||||
|
|
||||||
|
test_network_data = \
|
||||||
|
{'network': {'network-name': 'test' }}
|
||||||
|
|
||||||
|
def print_response(res):
|
||||||
|
content = res.read()
|
||||||
|
print "Status: %s" %res.status
|
||||||
|
print "Content: %s" %content
|
||||||
|
return content
|
||||||
|
|
||||||
|
def test_list_networks_and_ports(format = 'xml'):
|
||||||
|
client = MiniClient(HOST, PORT, USE_SSL)
|
||||||
|
print "TEST LIST NETWORKS AND PORTS -- FORMAT:%s" %format
|
||||||
|
print "----------------------------"
|
||||||
|
print "--> Step 1 - List All Networks"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 2 - Details for Network 001"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 3 - Ports for Network 001"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 4 - Details for Port 1"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks/001/ports/1." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "COMPLETED"
|
||||||
|
print "----------------------------"
|
||||||
|
|
||||||
|
def test_create_network(format = 'xml'):
|
||||||
|
client = MiniClient(HOST, PORT, USE_SSL)
|
||||||
|
print "TEST CREATE NETWORK -- FORMAT:%s" %format
|
||||||
|
print "----------------------------"
|
||||||
|
print "--> Step 1 - Create Network"
|
||||||
|
content_type = "application/" + format
|
||||||
|
body = Serializer().serialize(test_network_data, content_type)
|
||||||
|
res = client.do_request(TENANT_ID,'POST', "/networks." + format, body=body)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 2 - List All Networks"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "COMPLETED"
|
||||||
|
print "----------------------------"
|
||||||
|
|
||||||
|
def test_rename_network(format = 'xml'):
|
||||||
|
client = MiniClient(HOST, PORT, USE_SSL)
|
||||||
|
content_type = "application/" + format
|
||||||
|
print "TEST RENAME NETWORK -- FORMAT:%s" %format
|
||||||
|
print "----------------------------"
|
||||||
|
print "--> Step 1 - Retrieve network"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 2 - Rename network to 'test_renamed'"
|
||||||
|
test_network_data['network']['network-name'] = 'test_renamed'
|
||||||
|
body = Serializer().serialize(test_network_data, content_type)
|
||||||
|
res = client.do_request(TENANT_ID,'PUT', "/networks/001." + format, body=body)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 2 - Retrieve network (again)"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "COMPLETED"
|
||||||
|
print "----------------------------"
|
||||||
|
|
||||||
|
def test_delete_network(format = 'xml'):
|
||||||
|
client = MiniClient(HOST, PORT, USE_SSL)
|
||||||
|
content_type = "application/" + format
|
||||||
|
print "TEST DELETE NETWORK -- FORMAT:%s" %format
|
||||||
|
print "----------------------------"
|
||||||
|
print "--> Step 1 - List All Networks"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks." + format)
|
||||||
|
content = print_response(res)
|
||||||
|
network_data = Serializer().deserialize(content, content_type)
|
||||||
|
print network_data
|
||||||
|
net_id = network_data['networks'][0]['id']
|
||||||
|
print "--> Step 2 - Delete network %s" %net_id
|
||||||
|
res = client.do_request(TENANT_ID,'DELETE',
|
||||||
|
"/networks/" + net_id + "." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 3 - List All Networks (Again)"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "COMPLETED"
|
||||||
|
print "----------------------------"
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_port(format = 'xml'):
|
||||||
|
client = MiniClient(HOST, PORT, USE_SSL)
|
||||||
|
print "TEST CREATE PORT -- FORMAT:%s" %format
|
||||||
|
print "----------------------------"
|
||||||
|
print "--> Step 1 - List Ports for network 001"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 2 - Create Port for network 001"
|
||||||
|
res = client.do_request(TENANT_ID,'POST', "/networks/001/ports." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "--> Step 3 - List Ports for network 001 (again)"
|
||||||
|
res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
|
||||||
|
print_response(res)
|
||||||
|
print "COMPLETED"
|
||||||
|
print "----------------------------"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
test_list_networks_and_ports('xml')
|
||||||
|
test_list_networks_and_ports('json')
|
||||||
|
test_create_network('xml')
|
||||||
|
test_create_network('json')
|
||||||
|
test_rename_network('xml')
|
||||||
|
test_rename_network('json')
|
||||||
|
# NOTE: XML deserializer does not work properly
|
||||||
|
# disabling XML test - this is NOT a server-side issue
|
||||||
|
#test_delete_network('xml')
|
||||||
|
test_delete_network('json')
|
||||||
|
test_create_port('xml')
|
||||||
|
test_create_port('json')
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Standard boilerplate to call the main() function.
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user