Merge with quantum-unit-tests
This commit is contained in:
commit
70d793c816
47
TESTING
Normal file
47
TESTING
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
Testing Quantum
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
Overview
|
||||||
|
|
||||||
|
There are two types of tests in quantum: functional and unit. Their
|
||||||
|
respective directories are located in the tests/ directory.
|
||||||
|
|
||||||
|
The functional tests are intended to be used when the service is running.
|
||||||
|
Their goal is to make sure the service is working end to end and also to
|
||||||
|
test any plugin for conformance and functionality. Also note that the
|
||||||
|
functional tests expect quantum to be running on the local machine. If it
|
||||||
|
isn't you will have to change the tests to point to your quuntum instance.
|
||||||
|
|
||||||
|
The unit tests can be run without the service running. They are designed
|
||||||
|
to test the various pieces of the quantum tree to make sure any new
|
||||||
|
changes don't break existing functionality.
|
||||||
|
|
||||||
|
Running tests
|
||||||
|
|
||||||
|
All tests can be run via the run_tests.sh script, which will allow you to
|
||||||
|
run them in the standard environment or create a virtual environment to
|
||||||
|
run them in. All of the functional tests will fail if the service isn't
|
||||||
|
running. One current TODO item is to be able to specify whether you want
|
||||||
|
to run the functional or unit tests via run_tests.sh.
|
||||||
|
|
||||||
|
After running all of the tests, run_test.sh will report any pep8 errors
|
||||||
|
found in the tree.
|
||||||
|
|
||||||
|
Adding more tests
|
||||||
|
|
||||||
|
Quantum is a pretty new code base at this point and there is plenty of
|
||||||
|
areas that need tests. The current blueprint and branch for adding tests
|
||||||
|
is located at:
|
||||||
|
https://code.launchpad.net/~netstack/quantum/quantum-unit-tests
|
||||||
|
|
||||||
|
Also, there is a wiki page tracking the status of the test effort:
|
||||||
|
http://wiki.openstack.org/QuantumUnitTestStatus
|
||||||
|
|
||||||
|
Development process
|
||||||
|
|
||||||
|
It is expected that any new changes that are proposed for merge come with
|
||||||
|
unit tests for that feature or code area. Ideally any bugs fixes that are
|
||||||
|
submitted also have unit tests to prove that they stay fixed! :) In
|
||||||
|
addition, before proposing for merge, all of the current unit tests should
|
||||||
|
be passing.
|
||||||
|
|
@ -25,6 +25,7 @@ import routes
|
|||||||
import webob.dec
|
import webob.dec
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
|
from quantum import manager
|
||||||
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.api import ports
|
||||||
@ -41,41 +42,41 @@ class APIRouterV01(wsgi.Router):
|
|||||||
Routes requests on the Quantum API to the appropriate controller
|
Routes requests on the Quantum API to the appropriate controller
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ext_mgr=None):
|
def __init__(self, options=None):
|
||||||
mapper = routes.Mapper()
|
mapper = routes.Mapper()
|
||||||
self._setup_routes(mapper)
|
self._setup_routes(mapper, options)
|
||||||
super(APIRouterV01, self).__init__(mapper)
|
super(APIRouterV01, self).__init__(mapper)
|
||||||
|
|
||||||
def _setup_routes(self, mapper):
|
def _setup_routes(self, mapper, options):
|
||||||
|
# Loads the quantum plugin
|
||||||
|
plugin = manager.QuantumManager(options).get_plugin()
|
||||||
uri_prefix = '/tenants/{tenant_id}/'
|
uri_prefix = '/tenants/{tenant_id}/'
|
||||||
mapper.resource('network', 'networks',
|
mapper.resource('network', 'networks',
|
||||||
controller=networks.Controller(),
|
controller=networks.Controller(plugin),
|
||||||
collection={'detail': 'GET'},
|
collection={'detail': 'GET'},
|
||||||
member={'detail': 'GET'},
|
member={'detail': 'GET'},
|
||||||
path_prefix=uri_prefix)
|
path_prefix=uri_prefix)
|
||||||
mapper.resource('port', 'ports',
|
mapper.resource('port', 'ports',
|
||||||
controller=ports.Controller(),
|
controller=ports.Controller(plugin),
|
||||||
parent_resource=dict(member_name='network',
|
parent_resource=dict(member_name='network',
|
||||||
collection_name=uri_prefix +\
|
collection_name=uri_prefix +\
|
||||||
'networks'))
|
'networks'))
|
||||||
|
|
||||||
mapper.connect("get_resource",
|
mapper.connect("get_resource",
|
||||||
uri_prefix + 'networks/{network_id}/' \
|
uri_prefix + 'networks/{network_id}/' \
|
||||||
'ports/{id}/attachment{.format}',
|
'ports/{id}/attachment{.format}',
|
||||||
controller=ports.Controller(),
|
controller=ports.Controller(plugin),
|
||||||
action="get_resource",
|
action="get_resource",
|
||||||
conditions=dict(method=['GET']))
|
conditions=dict(method=['GET']))
|
||||||
mapper.connect("attach_resource",
|
mapper.connect("attach_resource",
|
||||||
uri_prefix + 'networks/{network_id}/' \
|
uri_prefix + 'networks/{network_id}/' \
|
||||||
'ports/{id}/attachment{.format}',
|
'ports/{id}/attachment{.format}',
|
||||||
controller=ports.Controller(),
|
controller=ports.Controller(plugin),
|
||||||
action="attach_resource",
|
action="attach_resource",
|
||||||
conditions=dict(method=['PUT']))
|
conditions=dict(method=['PUT']))
|
||||||
mapper.connect("detach_resource",
|
mapper.connect("detach_resource",
|
||||||
uri_prefix + 'networks/{network_id}/' \
|
uri_prefix + 'networks/{network_id}/' \
|
||||||
'ports/{id}/attachment{.format}',
|
'ports/{id}/attachment{.format}',
|
||||||
controller=ports.Controller(),
|
controller=ports.Controller(plugin),
|
||||||
action="detach_resource",
|
action="detach_resource",
|
||||||
conditions=dict(method=['DELETE']))
|
conditions=dict(method=['DELETE']))
|
||||||
print "MAPPED ROUTES"
|
print "MAPPED ROUTES"
|
||||||
|
@ -19,7 +19,6 @@ import logging
|
|||||||
|
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from quantum import manager
|
|
||||||
from quantum.common import wsgi
|
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'
|
||||||
@ -30,8 +29,8 @@ LOG = logging.getLogger('quantum.api.api_common')
|
|||||||
class QuantumController(wsgi.Controller):
|
class QuantumController(wsgi.Controller):
|
||||||
""" Base controller class for Quantum API """
|
""" Base controller class for Quantum API """
|
||||||
|
|
||||||
def __init__(self, plugin_conf_file=None):
|
def __init__(self, plugin):
|
||||||
self._setup_network_manager()
|
self._plugin = plugin
|
||||||
super(QuantumController, self).__init__()
|
super(QuantumController, self).__init__()
|
||||||
|
|
||||||
def _parse_request_params(self, req, params):
|
def _parse_request_params(self, req, params):
|
||||||
@ -64,6 +63,3 @@ class QuantumController(wsgi.Controller):
|
|||||||
raise exc.HTTPBadRequest(msg)
|
raise exc.HTTPBadRequest(msg)
|
||||||
results[param_name] = param_value or param.get('default-value')
|
results[param_name] = param_value or param.get('default-value')
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _setup_network_manager(self):
|
|
||||||
self.network_manager = manager.QuantumManager().get_manager()
|
|
||||||
|
@ -29,7 +29,7 @@ class Controller(common.QuantumController):
|
|||||||
""" Network API controller for Quantum API """
|
""" Network API controller for Quantum API """
|
||||||
|
|
||||||
_network_ops_param_list = [{
|
_network_ops_param_list = [{
|
||||||
'param-name': 'network-name',
|
'param-name': 'net-name',
|
||||||
'required': True}, ]
|
'required': True}, ]
|
||||||
|
|
||||||
_serialization_metadata = {
|
_serialization_metadata = {
|
||||||
@ -41,9 +41,9 @@ class Controller(common.QuantumController):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, plugin_conf_file=None):
|
def __init__(self, plugin):
|
||||||
self._resource_name = 'network'
|
self._resource_name = 'network'
|
||||||
super(Controller, self).__init__()
|
super(Controller, self).__init__(plugin)
|
||||||
|
|
||||||
def index(self, request, tenant_id):
|
def index(self, request, tenant_id):
|
||||||
""" Returns a list of network ids """
|
""" Returns a list of network ids """
|
||||||
@ -60,7 +60,7 @@ class Controller(common.QuantumController):
|
|||||||
|
|
||||||
def _items(self, req, tenant_id, net_details=False, port_details=False):
|
def _items(self, req, tenant_id, net_details=False, port_details=False):
|
||||||
""" Returns a list of networks. """
|
""" Returns a list of networks. """
|
||||||
networks = self.network_manager.get_all_networks(tenant_id)
|
networks = self._plugin.get_all_networks(tenant_id)
|
||||||
builder = networks_view.get_view_builder(req)
|
builder = networks_view.get_view_builder(req)
|
||||||
result = [builder.build(network, net_details, port_details)['network']
|
result = [builder.build(network, net_details, port_details)['network']
|
||||||
for network in networks]
|
for network in networks]
|
||||||
@ -85,6 +85,12 @@ class Controller(common.QuantumController):
|
|||||||
#do like show but with detaik
|
#do like show but with detaik
|
||||||
return self._items(request, tenant_id,
|
return self._items(request, tenant_id,
|
||||||
net_details=True, port_details=False)
|
net_details=True, port_details=False)
|
||||||
|
network = self._plugin.get_network_details(
|
||||||
|
tenant_id, id)
|
||||||
|
builder = networks_view.get_view_builder(request)
|
||||||
|
#build response with details
|
||||||
|
result = builder.build(network, True)
|
||||||
|
return dict(networks=result)
|
||||||
except exception.NetworkNotFound as e:
|
except exception.NetworkNotFound as e:
|
||||||
return faults.Fault(faults.NetworkNotFound(e))
|
return faults.Fault(faults.NetworkNotFound(e))
|
||||||
|
|
||||||
@ -97,9 +103,9 @@ class Controller(common.QuantumController):
|
|||||||
self._network_ops_param_list)
|
self._network_ops_param_list)
|
||||||
except exc.HTTPError as e:
|
except exc.HTTPError as e:
|
||||||
return faults.Fault(e)
|
return faults.Fault(e)
|
||||||
network = self.network_manager.\
|
network = self._plugin.\
|
||||||
create_network(tenant_id,
|
create_network(tenant_id,
|
||||||
request_params['network-name'])
|
request_params['net-name'])
|
||||||
builder = networks_view.get_view_builder(request)
|
builder = networks_view.get_view_builder(request)
|
||||||
result = builder.build(network)
|
result = builder.build(network)
|
||||||
return dict(networks=result)
|
return dict(networks=result)
|
||||||
@ -113,19 +119,16 @@ class Controller(common.QuantumController):
|
|||||||
except exc.HTTPError as e:
|
except exc.HTTPError as e:
|
||||||
return faults.Fault(e)
|
return faults.Fault(e)
|
||||||
try:
|
try:
|
||||||
network = self.network_manager.rename_network(tenant_id,
|
self._plugin.rename_network(tenant_id, id,
|
||||||
id, request_params['network-name'])
|
request_params['net-name'])
|
||||||
|
return exc.HTTPAccepted()
|
||||||
builder = networks_view.get_view_builder(request)
|
|
||||||
result = builder.build(network, True)
|
|
||||||
return dict(networks=result)
|
|
||||||
except exception.NetworkNotFound as e:
|
except exception.NetworkNotFound as e:
|
||||||
return faults.Fault(faults.NetworkNotFound(e))
|
return faults.Fault(faults.NetworkNotFound(e))
|
||||||
|
|
||||||
def delete(self, request, tenant_id, id):
|
def delete(self, request, tenant_id, id):
|
||||||
""" Destroys the network with the given id """
|
""" Destroys the network with the given id """
|
||||||
try:
|
try:
|
||||||
self.network_manager.delete_network(tenant_id, id)
|
self._plugin.delete_network(tenant_id, id)
|
||||||
return exc.HTTPAccepted()
|
return exc.HTTPAccepted()
|
||||||
except exception.NetworkNotFound as e:
|
except exception.NetworkNotFound as e:
|
||||||
return faults.Fault(faults.NetworkNotFound(e))
|
return faults.Fault(faults.NetworkNotFound(e))
|
||||||
|
@ -42,9 +42,9 @@ class Controller(common.QuantumController):
|
|||||||
"attributes": {
|
"attributes": {
|
||||||
"port": ["id", "state"], }, }, }
|
"port": ["id", "state"], }, }, }
|
||||||
|
|
||||||
def __init__(self, plugin_conf_file=None):
|
def __init__(self, plugin):
|
||||||
self._resource_name = 'port'
|
self._resource_name = 'port'
|
||||||
super(Controller, self).__init__()
|
super(Controller, self).__init__(plugin)
|
||||||
|
|
||||||
def index(self, request, tenant_id, network_id):
|
def index(self, request, tenant_id, network_id):
|
||||||
""" Returns a list of port ids for a given network """
|
""" Returns a list of port ids for a given network """
|
||||||
@ -53,7 +53,7 @@ class Controller(common.QuantumController):
|
|||||||
def _items(self, request, tenant_id, network_id, is_detail):
|
def _items(self, request, tenant_id, network_id, is_detail):
|
||||||
""" Returns a list of networks. """
|
""" Returns a list of networks. """
|
||||||
try:
|
try:
|
||||||
ports = self.network_manager.get_all_ports(tenant_id, network_id)
|
ports = self._plugin.get_all_ports(tenant_id, network_id)
|
||||||
builder = ports_view.get_view_builder(request)
|
builder = ports_view.get_view_builder(request)
|
||||||
result = [builder.build(port, is_detail)['port']
|
result = [builder.build(port, is_detail)['port']
|
||||||
for port in ports]
|
for port in ports]
|
||||||
@ -64,7 +64,7 @@ class Controller(common.QuantumController):
|
|||||||
def show(self, request, tenant_id, network_id, id):
|
def show(self, request, tenant_id, network_id, id):
|
||||||
""" Returns port details for given port and network """
|
""" Returns port details for given port and network """
|
||||||
try:
|
try:
|
||||||
port = self.network_manager.get_port_details(
|
port = self._plugin.get_port_details(
|
||||||
tenant_id, network_id, id)
|
tenant_id, network_id, id)
|
||||||
builder = ports_view.get_view_builder(request)
|
builder = ports_view.get_view_builder(request)
|
||||||
#build response with details
|
#build response with details
|
||||||
@ -84,7 +84,7 @@ class Controller(common.QuantumController):
|
|||||||
except exc.HTTPError as e:
|
except exc.HTTPError as e:
|
||||||
return faults.Fault(e)
|
return faults.Fault(e)
|
||||||
try:
|
try:
|
||||||
port = self.network_manager.create_port(tenant_id,
|
port = self._plugin.create_port(tenant_id,
|
||||||
network_id,
|
network_id,
|
||||||
request_params['port-state'])
|
request_params['port-state'])
|
||||||
builder = ports_view.get_view_builder(request)
|
builder = ports_view.get_view_builder(request)
|
||||||
@ -104,8 +104,7 @@ class Controller(common.QuantumController):
|
|||||||
except exc.HTTPError as e:
|
except exc.HTTPError as e:
|
||||||
return faults.Fault(e)
|
return faults.Fault(e)
|
||||||
try:
|
try:
|
||||||
port = self.network_manager.\
|
port = self._plugin.update_port(tenant_id, network_id, id,
|
||||||
update_port(tenant_id, network_id, id,
|
|
||||||
request_params['port-state'])
|
request_params['port-state'])
|
||||||
builder = ports_view.get_view_builder(request)
|
builder = ports_view.get_view_builder(request)
|
||||||
result = builder.build(port, True)
|
result = builder.build(port, True)
|
||||||
@ -121,7 +120,7 @@ class Controller(common.QuantumController):
|
|||||||
""" Destroys the port with the given id """
|
""" Destroys the port with the given id """
|
||||||
#look for port state in request
|
#look for port state in request
|
||||||
try:
|
try:
|
||||||
self.network_manager.delete_port(tenant_id, network_id, id)
|
self._plugin.delete_port(tenant_id, network_id, id)
|
||||||
return exc.HTTPAccepted()
|
return exc.HTTPAccepted()
|
||||||
# TODO(salvatore-orlando): Handle portInUse error
|
# TODO(salvatore-orlando): Handle portInUse error
|
||||||
except exception.NetworkNotFound as e:
|
except exception.NetworkNotFound as e:
|
||||||
@ -133,8 +132,9 @@ class Controller(common.QuantumController):
|
|||||||
|
|
||||||
def get_resource(self, request, tenant_id, network_id, id):
|
def get_resource(self, request, tenant_id, network_id, id):
|
||||||
try:
|
try:
|
||||||
result = self.network_manager.get_port_details(
|
result = self._plugin.get_port_details(
|
||||||
tenant_id, network_id, id).get('attachment', None)
|
tenant_id, network_id, id).get('attachment-id',
|
||||||
|
None)
|
||||||
return dict(attachment=result)
|
return dict(attachment=result)
|
||||||
except exception.NetworkNotFound as e:
|
except exception.NetworkNotFound as e:
|
||||||
return faults.Fault(faults.NetworkNotFound(e))
|
return faults.Fault(faults.NetworkNotFound(e))
|
||||||
@ -142,8 +142,6 @@ class Controller(common.QuantumController):
|
|||||||
return faults.Fault(faults.PortNotFound(e))
|
return faults.Fault(faults.PortNotFound(e))
|
||||||
|
|
||||||
def attach_resource(self, request, tenant_id, network_id, id):
|
def attach_resource(self, request, tenant_id, network_id, id):
|
||||||
content_type = request.best_match_content_type()
|
|
||||||
print "Content type:%s" % content_type
|
|
||||||
try:
|
try:
|
||||||
request_params = \
|
request_params = \
|
||||||
self._parse_request_params(request,
|
self._parse_request_params(request,
|
||||||
@ -151,7 +149,7 @@ class Controller(common.QuantumController):
|
|||||||
except exc.HTTPError as e:
|
except exc.HTTPError as e:
|
||||||
return faults.Fault(e)
|
return faults.Fault(e)
|
||||||
try:
|
try:
|
||||||
self.network_manager.plug_interface(tenant_id,
|
self._plugin.plug_interface(tenant_id,
|
||||||
network_id, id,
|
network_id, id,
|
||||||
request_params['attachment-id'])
|
request_params['attachment-id'])
|
||||||
return exc.HTTPAccepted()
|
return exc.HTTPAccepted()
|
||||||
@ -166,7 +164,7 @@ class Controller(common.QuantumController):
|
|||||||
|
|
||||||
def detach_resource(self, request, tenant_id, network_id, id):
|
def detach_resource(self, request, tenant_id, network_id, id):
|
||||||
try:
|
try:
|
||||||
self.network_manager.unplug_interface(tenant_id,
|
self._plugin.unplug_interface(tenant_id,
|
||||||
network_id, id)
|
network_id, id)
|
||||||
return exc.HTTPAccepted()
|
return exc.HTTPAccepted()
|
||||||
except exception.NetworkNotFound as e:
|
except exception.NetworkNotFound as e:
|
||||||
|
@ -240,6 +240,7 @@ def api_create_port(client, *args):
|
|||||||
|
|
||||||
def delete_port(manager, *args):
|
def delete_port(manager, *args):
|
||||||
tid, nid, pid = args
|
tid, nid, pid = args
|
||||||
|
manager.delete_port(tid, nid, pid)
|
||||||
LOG.info("Deleted Virtual Port:%s " \
|
LOG.info("Deleted Virtual Port:%s " \
|
||||||
"on Virtual Network:%s" % (pid, nid))
|
"on Virtual Network:%s" % (pid, nid))
|
||||||
|
|
||||||
@ -299,7 +300,8 @@ def api_plug_iface(client, *args):
|
|||||||
LOG.error("Failed to plug iface \"%s\" to port \"%s\": %s" % (vid,
|
LOG.error("Failed to plug iface \"%s\" to port \"%s\": %s" % (vid,
|
||||||
pid, output))
|
pid, output))
|
||||||
return
|
return
|
||||||
print "Plugged interface \"%s\" to port:%s on network:%s" % (vid, pid, nid)
|
print "Plugged interface \"%s\" to port:%s on network:%s" % (vid, pid,
|
||||||
|
nid)
|
||||||
|
|
||||||
|
|
||||||
def unplug_iface(manager, *args):
|
def unplug_iface(manager, *args):
|
||||||
@ -318,8 +320,8 @@ def api_unplug_iface(client, *args):
|
|||||||
output = res.read()
|
output = res.read()
|
||||||
LOG.debug(output)
|
LOG.debug(output)
|
||||||
if res.status != 202:
|
if res.status != 202:
|
||||||
LOG.error("Failed to unplug iface from port \"%s\": %s" % (vid,
|
LOG.error("Failed to unplug iface from port \"%s\": %s" % \
|
||||||
pid, output))
|
(pid, output))
|
||||||
return
|
return
|
||||||
print "Unplugged interface from port:%s on network:%s" % (pid, nid)
|
print "Unplugged interface from port:%s on network:%s" % (pid, nid)
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ def parse_isotime(timestr):
|
|||||||
return datetime.datetime.strptime(timestr, TIME_FORMAT)
|
return datetime.datetime.strptime(timestr, TIME_FORMAT)
|
||||||
|
|
||||||
|
|
||||||
def getPluginFromConfig(file="config.ini"):
|
def get_plugin_from_config(file="config.ini"):
|
||||||
Config = ConfigParser.ConfigParser()
|
Config = ConfigParser.ConfigParser()
|
||||||
Config.read(file)
|
Config.read(file)
|
||||||
return Config.get("PLUGIN", "provider")
|
return Config.get("PLUGIN", "provider")
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker, exc
|
from sqlalchemy.orm import sessionmaker, exc
|
||||||
import models
|
|
||||||
|
from quantum.db import models
|
||||||
|
|
||||||
_ENGINE = None
|
_ENGINE = None
|
||||||
_MAKER = None
|
_MAKER = None
|
||||||
@ -42,6 +43,13 @@ def configure_db(options):
|
|||||||
register_models()
|
register_models()
|
||||||
|
|
||||||
|
|
||||||
|
def clear_db():
|
||||||
|
global _ENGINE
|
||||||
|
assert _ENGINE
|
||||||
|
for table in reversed(BASE.metadata.sorted_tables):
|
||||||
|
_ENGINE.execute(table.delete())
|
||||||
|
|
||||||
|
|
||||||
def get_session(autocommit=True, expire_on_commit=False):
|
def get_session(autocommit=True, expire_on_commit=False):
|
||||||
"""Helper method to grab session"""
|
"""Helper method to grab session"""
|
||||||
global _MAKER, _ENGINE
|
global _MAKER, _ENGINE
|
||||||
@ -72,9 +80,10 @@ def network_create(tenant_id, name):
|
|||||||
net = None
|
net = None
|
||||||
try:
|
try:
|
||||||
net = session.query(models.Network).\
|
net = session.query(models.Network).\
|
||||||
filter_by(name=name).\
|
filter_by(tenant_id=tenant_id, name=name).\
|
||||||
one()
|
one()
|
||||||
raise Exception("Network with name \"%s\" already exists" % name)
|
raise Exception("Network with name %(name)s already " \
|
||||||
|
"exists for tenant %(tenant_id)s" % locals())
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
with session.begin():
|
with session.begin():
|
||||||
net = models.Network(tenant_id, name)
|
net = models.Network(tenant_id, name)
|
||||||
@ -104,7 +113,7 @@ def network_rename(net_id, tenant_id, new_name):
|
|||||||
session = get_session()
|
session = get_session()
|
||||||
try:
|
try:
|
||||||
res = session.query(models.Network).\
|
res = session.query(models.Network).\
|
||||||
filter_by(name=new_name).\
|
filter_by(tenant_id=tenant_id, name=new_name).\
|
||||||
one()
|
one()
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
net = network_get(net_id)
|
net = network_get(net_id)
|
||||||
@ -128,10 +137,11 @@ def network_destroy(net_id):
|
|||||||
raise Exception("No network found with id = %s" % net_id)
|
raise Exception("No network found with id = %s" % net_id)
|
||||||
|
|
||||||
|
|
||||||
def port_create(net_id):
|
def port_create(net_id, state=None):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
with session.begin():
|
with session.begin():
|
||||||
port = models.Port(net_id)
|
port = models.Port(net_id)
|
||||||
|
port['state'] = state or 'DOWN'
|
||||||
session.add(port)
|
session.add(port)
|
||||||
session.flush()
|
session.flush()
|
||||||
return port
|
return port
|
||||||
@ -154,9 +164,20 @@ def port_get(port_id):
|
|||||||
raise Exception("No port found with id = %s " % port_id)
|
raise Exception("No port found with id = %s " % port_id)
|
||||||
|
|
||||||
|
|
||||||
|
def port_set_state(port_id, new_state):
|
||||||
|
port = port_get(port_id)
|
||||||
|
if port:
|
||||||
|
session = get_session()
|
||||||
|
port.state = new_state
|
||||||
|
session.merge(port)
|
||||||
|
session.flush()
|
||||||
|
return port
|
||||||
|
|
||||||
|
|
||||||
def port_set_attachment(port_id, new_interface_id):
|
def port_set_attachment(port_id, new_interface_id):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
ports = None
|
ports = []
|
||||||
|
if new_interface_id != "":
|
||||||
try:
|
try:
|
||||||
ports = session.query(models.Port).\
|
ports = session.query(models.Port).\
|
||||||
filter_by(interface_id=new_interface_id).\
|
filter_by(interface_id=new_interface_id).\
|
||||||
@ -174,6 +195,14 @@ def port_set_attachment(port_id, new_interface_id):
|
|||||||
% (new_interface_id))
|
% (new_interface_id))
|
||||||
|
|
||||||
|
|
||||||
|
def port_unset_attachment(port_id):
|
||||||
|
session = get_session()
|
||||||
|
port = port_get(port_id)
|
||||||
|
port.interface_id = None
|
||||||
|
session.merge(port)
|
||||||
|
session.flush
|
||||||
|
|
||||||
|
|
||||||
def port_destroy(port_id):
|
def port_destroy(port_id):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
try:
|
try:
|
||||||
|
@ -19,14 +19,49 @@
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from sqlalchemy import Column, Integer, String, ForeignKey
|
from sqlalchemy import Column, String, ForeignKey
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import relation
|
from sqlalchemy.orm import relation, object_mapper
|
||||||
|
|
||||||
BASE = declarative_base()
|
BASE = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
class Port(BASE):
|
class QuantumBase(object):
|
||||||
|
"""Base class for Quantum Models."""
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return getattr(self, key)
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
return getattr(self, key, default)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self._i = iter(object_mapper(self).columns)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
n = self._i.next().name
|
||||||
|
return n, getattr(self, n)
|
||||||
|
|
||||||
|
def update(self, values):
|
||||||
|
"""Make the model object behave like a dict"""
|
||||||
|
for k, v in values.iteritems():
|
||||||
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
def iteritems(self):
|
||||||
|
"""Make the model object behave like a dict.
|
||||||
|
Includes attributes from joins."""
|
||||||
|
local = dict(self)
|
||||||
|
joined = dict([(k, v) for k, v in self.__dict__.iteritems()
|
||||||
|
if not k[0] == '_'])
|
||||||
|
local.update(joined)
|
||||||
|
return local.iteritems()
|
||||||
|
|
||||||
|
|
||||||
|
class Port(BASE, QuantumBase):
|
||||||
"""Represents a port on a quantum network"""
|
"""Represents a port on a quantum network"""
|
||||||
__tablename__ = 'ports'
|
__tablename__ = 'ports'
|
||||||
|
|
||||||
@ -34,17 +69,20 @@ class Port(BASE):
|
|||||||
network_id = Column(String(255), ForeignKey("networks.uuid"),
|
network_id = Column(String(255), ForeignKey("networks.uuid"),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
interface_id = Column(String(255))
|
interface_id = Column(String(255))
|
||||||
|
# Port state - Hardcoding string value at the moment
|
||||||
|
state = Column(String(8))
|
||||||
|
|
||||||
def __init__(self, network_id):
|
def __init__(self, network_id):
|
||||||
self.uuid = uuid.uuid4()
|
self.uuid = str(uuid.uuid4())
|
||||||
self.network_id = network_id
|
self.network_id = network_id
|
||||||
|
self.state = "DOWN"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Port(%s,%s,%s)>" % (self.uuid, self.network_id,
|
return "<Port(%s,%s,%s,%s)>" % (self.uuid, self.network_id,
|
||||||
self.interface_id)
|
self.state, self.interface_id)
|
||||||
|
|
||||||
|
|
||||||
class Network(BASE):
|
class Network(BASE, QuantumBase):
|
||||||
"""Represents a quantum network"""
|
"""Represents a quantum network"""
|
||||||
__tablename__ = 'networks'
|
__tablename__ = 'networks'
|
||||||
|
|
||||||
@ -54,7 +92,7 @@ class Network(BASE):
|
|||||||
ports = relation(Port, order_by=Port.uuid, backref="network")
|
ports = relation(Port, order_by=Port.uuid, backref="network")
|
||||||
|
|
||||||
def __init__(self, tenant_id, name):
|
def __init__(self, tenant_id, name):
|
||||||
self.uuid = uuid.uuid4()
|
self.uuid = str(uuid.uuid4())
|
||||||
self.tenant_id = tenant_id
|
self.tenant_id = tenant_id
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
@ -24,15 +24,16 @@ class.
|
|||||||
The caller should make sure that QuantumManager is a singleton.
|
The caller should make sure that QuantumManager is a singleton.
|
||||||
"""
|
"""
|
||||||
import gettext
|
import gettext
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
gettext.install('quantum', unicode=1)
|
|
||||||
|
|
||||||
import os
|
gettext.install('quantum', unicode=1)
|
||||||
|
|
||||||
from common import utils
|
from common import utils
|
||||||
from quantum_plugin_base import QuantumPluginBase
|
from quantum_plugin_base import QuantumPluginBase
|
||||||
|
|
||||||
CONFIG_FILE = "plugins.ini"
|
CONFIG_FILE = "plugins.ini"
|
||||||
|
LOG = logging.getLogger('quantum.manager')
|
||||||
|
|
||||||
|
|
||||||
def find_config(basepath):
|
def find_config(basepath):
|
||||||
@ -43,22 +44,27 @@ def find_config(basepath):
|
|||||||
|
|
||||||
|
|
||||||
class QuantumManager(object):
|
class QuantumManager(object):
|
||||||
|
def __init__(self, options=None, config_file=None):
|
||||||
def __init__(self, config=None):
|
if config_file == None:
|
||||||
if config == None:
|
|
||||||
self.configuration_file = find_config(
|
self.configuration_file = find_config(
|
||||||
os.path.abspath(os.path.dirname(__file__)))
|
os.path.abspath(os.path.dirname(__file__)))
|
||||||
else:
|
else:
|
||||||
self.configuration_file = config
|
self.configuration_file = config_file
|
||||||
plugin_location = utils.getPluginFromConfig(self.configuration_file)
|
# If no options have been provided, create an empty dict
|
||||||
plugin_klass = utils.import_class(plugin_location)
|
if not options:
|
||||||
|
options = {}
|
||||||
|
if not 'plugin_provider' in options:
|
||||||
|
options['plugin_provider'] = \
|
||||||
|
utils.get_plugin_from_config(self.configuration_file)
|
||||||
|
LOG.debug("Plugin location:%s", options['plugin_provider'])
|
||||||
|
plugin_klass = utils.import_class(options['plugin_provider'])
|
||||||
if not issubclass(plugin_klass, QuantumPluginBase):
|
if not issubclass(plugin_klass, QuantumPluginBase):
|
||||||
raise Exception("Configured Quantum plug-in " \
|
raise Exception("Configured Quantum plug-in " \
|
||||||
"didn't pass compatibility test")
|
"didn't pass compatibility test")
|
||||||
else:
|
else:
|
||||||
print("Successfully imported Quantum plug-in." \
|
LOG.debug("Successfully imported Quantum plug-in." \
|
||||||
"All compatibility tests passed\n")
|
"All compatibility tests passed")
|
||||||
self.plugin = plugin_klass()
|
self.plugin = plugin_klass()
|
||||||
|
|
||||||
def get_manager(self):
|
def get_plugin(self):
|
||||||
return self.plugin
|
return self.plugin
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
[PLUGIN]
|
[PLUGIN]
|
||||||
# Quantum plugin provider module
|
# Quantum plugin provider module
|
||||||
provider = quantum.plugins.SamplePlugin.FakePlugin
|
#provider = quantum.plugins.SamplePlugin.FakePlugin
|
||||||
|
provider = quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPlugin
|
||||||
|
@ -14,8 +14,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# @author: Somik Behera, Nicira Networks, Inc.
|
# @author: Somik Behera, Nicira Networks, Inc.
|
||||||
|
# @author: Salvatore Orlando, Citrix
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from quantum.common import exceptions as exc
|
from quantum.common import exceptions as exc
|
||||||
|
from quantum.db import api as db
|
||||||
|
|
||||||
|
LOG = logging.getLogger('quantum.plugins.SamplePlugin')
|
||||||
|
|
||||||
|
|
||||||
class QuantumEchoPlugin(object):
|
class QuantumEchoPlugin(object):
|
||||||
@ -220,63 +226,41 @@ class FakePlugin(object):
|
|||||||
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': 'ACTIVE',
|
|
||||||
'attachment': None}}
|
|
||||||
_port_dict_2 = {
|
|
||||||
1: {'port-id': 1,
|
|
||||||
'port-state': 'ACTIVE',
|
|
||||||
'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):
|
||||||
FakePlugin._net_counter = len(FakePlugin._networks)
|
db.configure_db({'sql_connection': 'sqlite:///:memory:'})
|
||||||
|
FakePlugin._net_counter = 0
|
||||||
|
|
||||||
def _get_network(self, tenant_id, network_id):
|
def _get_network(self, tenant_id, network_id):
|
||||||
network = FakePlugin._networks.get(network_id)
|
try:
|
||||||
if not network:
|
network = db.network_get(network_id)
|
||||||
|
except:
|
||||||
raise exc.NetworkNotFound(net_id=network_id)
|
raise exc.NetworkNotFound(net_id=network_id)
|
||||||
return network
|
return network
|
||||||
|
|
||||||
def _get_port(self, tenant_id, network_id, port_id):
|
def _get_port(self, tenant_id, network_id, port_id):
|
||||||
net = self._get_network(tenant_id, network_id)
|
net = self._get_network(tenant_id, network_id)
|
||||||
port = net['net-ports'].get(int(port_id))
|
try:
|
||||||
if not port:
|
port = db.port_get(port_id)
|
||||||
|
except:
|
||||||
|
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
|
||||||
|
# Port must exist and belong to the appropriate network.
|
||||||
|
if port['network_id'] != net['uuid']:
|
||||||
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
|
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
|
||||||
return port
|
return port
|
||||||
|
|
||||||
def _validate_port_state(self, port_state):
|
def _validate_port_state(self, port_state):
|
||||||
if port_state.upper() not in ('UP', 'DOWN'):
|
if port_state.upper() not in ('ACTIVE', 'DOWN'):
|
||||||
raise exc.StateInvalid(port_state=port_state)
|
raise exc.StateInvalid(port_state=port_state)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _validate_attachment(self, tenant_id, network_id, port_id,
|
def _validate_attachment(self, tenant_id, network_id, port_id,
|
||||||
remote_interface_id):
|
remote_interface_id):
|
||||||
network = self._get_network(tenant_id, network_id)
|
for port in db.port_list(network_id):
|
||||||
for port in network['net-ports'].values():
|
if port['interface_id'] == remote_interface_id:
|
||||||
if port['attachment'] == remote_interface_id:
|
|
||||||
raise exc.AlreadyAttached(net_id=network_id,
|
raise exc.AlreadyAttached(net_id=network_id,
|
||||||
port_id=port_id,
|
port_id=port_id,
|
||||||
att_id=port['attachment'],
|
att_id=port['interface_id'],
|
||||||
att_port_id=port['port-id'])
|
att_port_id=port['uuid'])
|
||||||
|
|
||||||
def get_all_networks(self, tenant_id):
|
def get_all_networks(self, tenant_id):
|
||||||
"""
|
"""
|
||||||
@ -284,49 +268,47 @@ class FakePlugin(object):
|
|||||||
<network_uuid, network_name> for
|
<network_uuid, network_name> for
|
||||||
the specified tenant.
|
the specified tenant.
|
||||||
"""
|
"""
|
||||||
print("get_all_networks() called\n")
|
LOG.debug("FakePlugin.get_all_networks() called")
|
||||||
return FakePlugin._networks.values()
|
nets = []
|
||||||
|
for net in db.network_list(tenant_id):
|
||||||
|
net_item = {'net-id': str(net.uuid),
|
||||||
|
'net-name': net.name}
|
||||||
|
nets.append(net_item)
|
||||||
|
return nets
|
||||||
|
|
||||||
def get_network_details(self, tenant_id, net_id):
|
def get_network_details(self, tenant_id, net_id):
|
||||||
"""
|
"""
|
||||||
retrieved a list of all the remote vifs that
|
retrieved a list of all the remote vifs that
|
||||||
are attached to the network
|
are attached to the network
|
||||||
"""
|
"""
|
||||||
print("get_network_details() called\n")
|
LOG.debug("FakePlugin.get_network_details() called")
|
||||||
return self._get_network(tenant_id, net_id)
|
net = self._get_network(tenant_id, net_id)
|
||||||
|
return {'net-id': str(net.uuid),
|
||||||
|
'net-name': net.name}
|
||||||
|
|
||||||
def create_network(self, tenant_id, net_name):
|
def create_network(self, tenant_id, net_name):
|
||||||
"""
|
"""
|
||||||
Creates a new Virtual Network, and assigns it
|
Creates a new Virtual Network, and assigns it
|
||||||
a symbolic name.
|
a symbolic name.
|
||||||
"""
|
"""
|
||||||
print("create_network() called\n")
|
LOG.debug("FakePlugin.create_network() called")
|
||||||
FakePlugin._net_counter += 1
|
new_net = db.network_create(tenant_id, net_name)
|
||||||
new_net_id = ("0" * (3 - len(str(FakePlugin._net_counter)))) + \
|
# Return uuid for newly created network as net-id.
|
||||||
str(FakePlugin._net_counter)
|
return {'net-id': new_net['uuid']}
|
||||||
print new_net_id
|
|
||||||
new_net_dict = {'net-id': new_net_id,
|
|
||||||
'net-name': net_name,
|
|
||||||
'net-ports': {}}
|
|
||||||
|
|
||||||
FakePlugin._networks[new_net_id] = new_net_dict
|
|
||||||
# return network_id of the created network
|
|
||||||
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")
|
LOG.debug("FakePlugin.delete_network() called")
|
||||||
net = FakePlugin._networks.get(net_id)
|
net = self._get_network(tenant_id, net_id)
|
||||||
# Verify that no attachments are plugged into the network
|
# Verify that no attachments are plugged into the network
|
||||||
if net:
|
if net:
|
||||||
if net['net-ports']:
|
for port in db.port_list(net_id):
|
||||||
for port in net['net-ports'].values():
|
if port['interface_id']:
|
||||||
if port['attachment']:
|
|
||||||
raise exc.NetworkInUse(net_id=net_id)
|
raise exc.NetworkInUse(net_id=net_id)
|
||||||
FakePlugin._networks.pop(net_id)
|
db.network_destroy(net_id)
|
||||||
return net
|
return net
|
||||||
# Network not found
|
# Network not found
|
||||||
raise exc.NetworkNotFound(net_id=net_id)
|
raise exc.NetworkNotFound(net_id=net_id)
|
||||||
@ -336,9 +318,12 @@ class FakePlugin(object):
|
|||||||
Updates the symbolic name belonging to a particular
|
Updates the symbolic name belonging to a particular
|
||||||
Virtual Network.
|
Virtual Network.
|
||||||
"""
|
"""
|
||||||
print("rename_network() called\n")
|
LOG.debug("FakePlugin.rename_network() called")
|
||||||
|
try:
|
||||||
|
db.network_rename(net_id, tenant_id, new_name)
|
||||||
|
except:
|
||||||
|
raise exc.NetworkNotFound(net_id=net_id)
|
||||||
net = self._get_network(tenant_id, net_id)
|
net = self._get_network(tenant_id, net_id)
|
||||||
net['net-name'] = new_name
|
|
||||||
return net
|
return net
|
||||||
|
|
||||||
def get_all_ports(self, tenant_id, net_id):
|
def get_all_ports(self, tenant_id, net_id):
|
||||||
@ -346,45 +331,49 @@ class FakePlugin(object):
|
|||||||
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")
|
LOG.debug("FakePlugin.get_all_ports() called")
|
||||||
network = self._get_network(tenant_id, net_id)
|
port_ids = []
|
||||||
ports_on_net = network['net-ports'].values()
|
ports = db.port_list(net_id)
|
||||||
return ports_on_net
|
for x in ports:
|
||||||
|
d = {'port-id': str(x.uuid)}
|
||||||
|
port_ids.append(d)
|
||||||
|
return port_ids
|
||||||
|
|
||||||
def get_port_details(self, tenant_id, net_id, port_id):
|
def get_port_details(self, tenant_id, net_id, port_id):
|
||||||
"""
|
"""
|
||||||
This method allows the user to retrieve a remote interface
|
This method allows the user to retrieve a remote interface
|
||||||
that is attached to this particular port.
|
that is attached to this particular port.
|
||||||
"""
|
"""
|
||||||
print("get_port_details() called\n")
|
LOG.debug("FakePlugin.get_port_details() called")
|
||||||
return self._get_port(tenant_id, net_id, port_id)
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
|
return {'port-id': str(port.uuid),
|
||||||
|
'attachment-id': port.interface_id,
|
||||||
|
'port-state': port.state}
|
||||||
|
|
||||||
def create_port(self, tenant_id, net_id, port_state=None):
|
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")
|
LOG.debug("FakePlugin.create_port() called")
|
||||||
net = self._get_network(tenant_id, net_id)
|
# verify net_id
|
||||||
# check port state
|
self._get_network(tenant_id, net_id)
|
||||||
# TODO(salvatore-orlando): Validate port state in API?
|
port = db.port_create(net_id, port_state)
|
||||||
self._validate_port_state(port_state)
|
port_item = {'port-id': str(port.uuid)}
|
||||||
ports = net['net-ports']
|
return port_item
|
||||||
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):
|
def update_port(self, tenant_id, net_id, port_id, new_state):
|
||||||
"""
|
"""
|
||||||
Updates the state of a port on the specified Virtual Network.
|
Updates the state of a port on the specified Virtual Network.
|
||||||
"""
|
"""
|
||||||
print("create_port() called\n")
|
LOG.debug("FakePlugin.update_port() called")
|
||||||
port = self._get_port(tenant_id, net_id, port_id)
|
#validate port and network ids
|
||||||
self._validate_port_state(port_state)
|
self._get_network(tenant_id, net_id)
|
||||||
port['port-state'] = port_state
|
self._get_port(tenant_id, net_id, port_id)
|
||||||
return port
|
self._validate_port_state(new_state)
|
||||||
|
db.port_set_state(port_id, new_state)
|
||||||
|
port_item = {'port-id': port_id,
|
||||||
|
'port-state': new_state}
|
||||||
|
return port_item
|
||||||
|
|
||||||
def delete_port(self, tenant_id, net_id, port_id):
|
def delete_port(self, tenant_id, net_id, port_id):
|
||||||
"""
|
"""
|
||||||
@ -393,39 +382,42 @@ class FakePlugin(object):
|
|||||||
the remote interface is first un-plugged and then the port
|
the remote interface is first un-plugged and then the port
|
||||||
is deleted.
|
is deleted.
|
||||||
"""
|
"""
|
||||||
print("delete_port() called\n")
|
LOG.debug("FakePlugin.delete_port() called")
|
||||||
net = self._get_network(tenant_id, net_id)
|
net = self._get_network(tenant_id, net_id)
|
||||||
port = self._get_port(tenant_id, net_id, port_id)
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
if port['attachment']:
|
if port['interface_id']:
|
||||||
raise exc.PortInUse(net_id=net_id, port_id=port_id,
|
raise exc.PortInUse(net_id=net_id, port_id=port_id,
|
||||||
att_id=port['attachment'])
|
att_id=port['interface_id'])
|
||||||
try:
|
try:
|
||||||
net['net-ports'].pop(int(port_id))
|
port = db.port_destroy(port_id)
|
||||||
except KeyError:
|
except Exception, e:
|
||||||
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
|
raise Exception("Failed to delete port: %s" % str(e))
|
||||||
|
d = {}
|
||||||
|
d["port-id"] = str(port.uuid)
|
||||||
|
return d
|
||||||
|
|
||||||
def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
|
def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
|
||||||
"""
|
"""
|
||||||
Attaches a remote interface to the specified port on the
|
Attaches a remote interface to the specified port on the
|
||||||
specified Virtual Network.
|
specified Virtual Network.
|
||||||
"""
|
"""
|
||||||
print("plug_interface() called\n")
|
LOG.debug("FakePlugin.plug_interface() called")
|
||||||
# Validate attachment
|
# Validate attachment
|
||||||
self._validate_attachment(tenant_id, net_id, port_id,
|
self._validate_attachment(tenant_id, net_id, port_id,
|
||||||
remote_interface_id)
|
remote_interface_id)
|
||||||
port = self._get_port(tenant_id, net_id, port_id)
|
port = self._get_port(tenant_id, net_id, port_id)
|
||||||
if port['attachment']:
|
if port['interface_id']:
|
||||||
raise exc.PortInUse(net_id=net_id, port_id=port_id,
|
raise exc.PortInUse(net_id=net_id, port_id=port_id,
|
||||||
att_id=port['attachment'])
|
att_id=port['interface_id'])
|
||||||
port['attachment'] = remote_interface_id
|
db.port_set_attachment(port_id, remote_interface_id)
|
||||||
|
|
||||||
def unplug_interface(self, tenant_id, net_id, port_id):
|
def unplug_interface(self, tenant_id, net_id, port_id):
|
||||||
"""
|
"""
|
||||||
Detaches a remote interface from the specified port on the
|
Detaches a remote interface from the specified port on the
|
||||||
specified Virtual Network.
|
specified Virtual Network.
|
||||||
"""
|
"""
|
||||||
print("unplug_interface() called\n")
|
LOG.debug("FakePlugin.unplug_interface() called")
|
||||||
port = self._get_port(tenant_id, net_id, port_id)
|
self._get_port(tenant_id, net_id, port_id)
|
||||||
# TODO(salvatore-orlando):
|
# TODO(salvatore-orlando):
|
||||||
# Should unplug on port without attachment raise an Error?
|
# Should unplug on port without attachment raise an Error?
|
||||||
port['attachment'] = None
|
db.port_unset_attachment(port_id)
|
||||||
|
@ -62,20 +62,25 @@ mysql> FLUSH PRIVILEGES;
|
|||||||
distribution tarball (see below) and the agent will use the credentials here
|
distribution tarball (see below) and the agent will use the credentials here
|
||||||
to access the database.
|
to access the database.
|
||||||
|
|
||||||
# -- Agent configuration
|
# -- XenServer Agent configuration
|
||||||
|
|
||||||
- Create the agent distribution tarball
|
- Create the agent distribution tarball
|
||||||
|
|
||||||
$ make agent-dist
|
$ make agent-dist
|
||||||
- Copy the resulting tarball to your xenserver(s) (copy to dom0, not the nova
|
- Copy the resulting tarball to your xenserver(s) (copy to dom0, not the nova
|
||||||
compute node)
|
compute node)
|
||||||
- Unpack the tarball and run install.sh. This will install all of the
|
- Unpack the tarball and run xenserver_install.sh. This will install all of the
|
||||||
necessary pieces into /etc/xapi.d/plugins. It will also spit out the name
|
necessary pieces into /etc/xapi.d/plugins. It will also spit out the name
|
||||||
of the integration bridge that you'll need for your nova configuration.
|
of the integration bridge that you'll need for your nova configuration.
|
||||||
Make sure to specify this in your nova flagfile as --flat_network_bridge.
|
Make sure to specify this in your nova flagfile as --flat_network_bridge.
|
||||||
- Run the agent [on your hypervisor (dom0)]:
|
- Run the agent [on your hypervisor (dom0)]:
|
||||||
$ /etc/xapi.d/plugins/ovs_quantum_agent.py /etc/xapi.d/plugins/ovs_quantum_plugin.ini
|
$ /etc/xapi.d/plugins/ovs_quantum_agent.py /etc/xapi.d/plugins/ovs_quantum_plugin.ini
|
||||||
|
|
||||||
|
# -- KVM Agent configuration
|
||||||
|
|
||||||
|
- Copy ovs_quantum_agent.py and ovs_quantum_plugin.ini to the Linux host and run:
|
||||||
|
$ python ovs_quantum_agent.py ovs_quantum_plugin.ini
|
||||||
|
|
||||||
# -- Getting quantum up and running
|
# -- Getting quantum up and running
|
||||||
|
|
||||||
- Start quantum [on the quantum service host]:
|
- Start quantum [on the quantum service host]:
|
||||||
|
@ -130,40 +130,40 @@ class OVSBridge:
|
|||||||
def get_port_stats(self, port_name):
|
def get_port_stats(self, port_name):
|
||||||
return self.db_get_map("Interface", port_name, "statistics")
|
return self.db_get_map("Interface", port_name, "statistics")
|
||||||
|
|
||||||
|
# this is a hack that should go away once nova properly reports bindings
|
||||||
|
# to quantum. We have this here for now as it lets us work with
|
||||||
|
# unmodified nova
|
||||||
|
def xapi_get_port(self, name):
|
||||||
|
external_ids = self.db_get_map("Interface", name, "external_ids")
|
||||||
|
if "attached-mac" not in external_ids:
|
||||||
|
return None
|
||||||
|
vm_uuid = external_ids.get("xs-vm-uuid", "")
|
||||||
|
if len(vm_uuid) == 0:
|
||||||
|
return None
|
||||||
|
LOG.debug("iface-id not set, got xs-vm-uuid: %s" % vm_uuid)
|
||||||
|
res = os.popen("xe vm-list uuid=%s params=name-label --minimal" \
|
||||||
|
% vm_uuid).readline().strip()
|
||||||
|
if len(res) == 0:
|
||||||
|
return None
|
||||||
|
external_ids["iface-id"] = res
|
||||||
|
LOG.info("Setting interface \"%s\" iface-id to \"%s\"" % (name, res))
|
||||||
|
self.set_db_attribute("Interface", name,
|
||||||
|
"external-ids:iface-id", res)
|
||||||
|
ofport = self.db_get_val("Interface", name, "ofport")
|
||||||
|
return VifPort(name, ofport, external_ids["iface-id"],
|
||||||
|
external_ids["attached-mac"], self)
|
||||||
|
|
||||||
# returns a VIF object for each VIF port
|
# returns a VIF object for each VIF port
|
||||||
def get_vif_ports(self):
|
def get_vif_ports(self):
|
||||||
edge_ports = []
|
edge_ports = []
|
||||||
port_names = self.get_port_name_list()
|
port_names = self.get_port_name_list()
|
||||||
for name in port_names:
|
for name in port_names:
|
||||||
external_ids = self.db_get_map("Interface", name, "external_ids")
|
external_ids = self.db_get_map("Interface", name, "external_ids")
|
||||||
if "iface-id" in external_ids and "attached-mac" in external_ids:
|
if "xs-vm-uuid" in external_ids:
|
||||||
ofport = self.db_get_val("Interface", name, "ofport")
|
p = xapi_get_port(name)
|
||||||
p = VifPort(name, ofport, external_ids["iface-id"],
|
if p is not None:
|
||||||
external_ids["attached-mac"], self)
|
|
||||||
edge_ports.append(p)
|
edge_ports.append(p)
|
||||||
else:
|
elif "iface-id" in external_ids and "attached-mac" in external_ids:
|
||||||
# iface-id might not be set. See if we can figure it out and
|
|
||||||
# set it here.
|
|
||||||
external_ids = self.db_get_map("Interface", name,
|
|
||||||
"external_ids")
|
|
||||||
if "attached-mac" not in external_ids:
|
|
||||||
continue
|
|
||||||
vif_uuid = external_ids.get("xs-vif-uuid", "")
|
|
||||||
if len(vif_uuid) == 0:
|
|
||||||
continue
|
|
||||||
LOG.debug("iface-id not set, got vif-uuid: %s" % vif_uuid)
|
|
||||||
res = os.popen("xe vif-param-get param-name=other-config "
|
|
||||||
"uuid=%s | grep nicira-iface-id | "
|
|
||||||
"awk '{print $2}'"
|
|
||||||
% vif_uuid).readline()
|
|
||||||
res = res.strip()
|
|
||||||
if len(res) == 0:
|
|
||||||
continue
|
|
||||||
external_ids["iface-id"] = res
|
|
||||||
LOG.info("Setting interface \"%s\" iface-id to \"%s\""
|
|
||||||
% (name, res))
|
|
||||||
self.set_db_attribute("Interface", name,
|
|
||||||
"external-ids:iface-id", res)
|
|
||||||
ofport = self.db_get_val("Interface", name, "ofport")
|
ofport = self.db_get_val("Interface", name, "ofport")
|
||||||
p = VifPort(name, ofport, external_ids["iface-id"],
|
p = VifPort(name, ofport, external_ids["iface-id"],
|
||||||
external_ids["attached-mac"], self)
|
external_ids["attached-mac"], self)
|
||||||
@ -171,13 +171,15 @@ class OVSBridge:
|
|||||||
return edge_ports
|
return edge_ports
|
||||||
|
|
||||||
|
|
||||||
class OVSNaaSPlugin:
|
class OVSQuantumAgent:
|
||||||
|
|
||||||
def __init__(self, integ_br):
|
def __init__(self, integ_br):
|
||||||
self.setup_integration_br(integ_br)
|
self.setup_integration_br(integ_br)
|
||||||
|
|
||||||
def port_bound(self, port, vlan_id):
|
def port_bound(self, port, vlan_id):
|
||||||
self.int_br.set_db_attribute("Port", port.port_name, "tag",
|
self.int_br.set_db_attribute("Port", port.port_name, "tag",
|
||||||
str(vlan_id))
|
str(vlan_id))
|
||||||
|
self.int_br.delete_flows(match="in_port=%s" % port.ofport)
|
||||||
|
|
||||||
def port_unbound(self, port, still_exists):
|
def port_unbound(self, port, still_exists):
|
||||||
if still_exists:
|
if still_exists:
|
||||||
@ -186,13 +188,8 @@ class OVSNaaSPlugin:
|
|||||||
def setup_integration_br(self, integ_br):
|
def setup_integration_br(self, integ_br):
|
||||||
self.int_br = OVSBridge(integ_br)
|
self.int_br = OVSBridge(integ_br)
|
||||||
self.int_br.remove_all_flows()
|
self.int_br.remove_all_flows()
|
||||||
# drop all traffic on the 'dead vlan'
|
# switch all traffic using L2 learning
|
||||||
self.int_br.add_flow(priority=2, match="dl_vlan=4095", actions="drop")
|
|
||||||
# switch all other traffic using L2 learning
|
|
||||||
self.int_br.add_flow(priority=1, actions="normal")
|
self.int_br.add_flow(priority=1, actions="normal")
|
||||||
# FIXME send broadcast everywhere, regardless of tenant
|
|
||||||
#int_br.add_flow(priority=3, match="dl_dst=ff:ff:ff:ff:ff:ff",
|
|
||||||
# actions="normal")
|
|
||||||
|
|
||||||
def daemon_loop(self, conn):
|
def daemon_loop(self, conn):
|
||||||
self.local_vlan_map = {}
|
self.local_vlan_map = {}
|
||||||
@ -201,7 +198,7 @@ class OVSNaaSPlugin:
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT * FROM network_bindings")
|
cursor.execute("SELECT * FROM ports")
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
all_bindings = {}
|
all_bindings = {}
|
||||||
@ -227,21 +224,25 @@ class OVSNaaSPlugin:
|
|||||||
# no binding, put him on the 'dead vlan'
|
# no binding, put him on the 'dead vlan'
|
||||||
self.int_br.set_db_attribute("Port", p.port_name, "tag",
|
self.int_br.set_db_attribute("Port", p.port_name, "tag",
|
||||||
"4095")
|
"4095")
|
||||||
|
self.int_br.add_flow(priority=2,
|
||||||
|
match="in_port=%s" % p.ofport, actions="drop")
|
||||||
|
|
||||||
old_b = old_local_bindings.get(p.vif_id, None)
|
old_b = old_local_bindings.get(p.vif_id, None)
|
||||||
new_b = new_local_bindings.get(p.vif_id, None)
|
new_b = new_local_bindings.get(p.vif_id, None)
|
||||||
|
|
||||||
if old_b != new_b:
|
if old_b != new_b:
|
||||||
if old_b is not None:
|
if old_b is not None:
|
||||||
LOG.info("Removing binding to net-id = %s for %s"
|
LOG.info("Removing binding to net-id = %s for %s"
|
||||||
% (old_b, str(p)))
|
% (old_b, str(p)))
|
||||||
self.port_unbound(p, True)
|
self.port_unbound(p, True)
|
||||||
if new_b is not None:
|
if new_b is not None:
|
||||||
LOG.info("Adding binding to net-id = %s for %s" \
|
|
||||||
% (new_b, str(p)))
|
|
||||||
# If we don't have a binding we have to stick it on
|
# If we don't have a binding we have to stick it on
|
||||||
# the dead vlan
|
# the dead vlan
|
||||||
vlan_id = vlan_bindings.get(all_bindings[p.vif_id],
|
vlan_id = vlan_bindings.get(all_bindings[p.vif_id],
|
||||||
"4095")
|
"4095")
|
||||||
self.port_bound(p, vlan_id)
|
self.port_bound(p, vlan_id)
|
||||||
|
LOG.info("Adding binding to net-id = %s " \
|
||||||
|
"for %s on vlan %s" % (new_b, str(p), vlan_id))
|
||||||
for vif_id in old_vif_ports.keys():
|
for vif_id in old_vif_ports.keys():
|
||||||
if vif_id not in new_vif_ports:
|
if vif_id not in new_vif_ports:
|
||||||
LOG.info("Port Disappeared: %s" % vif_id)
|
LOG.info("Port Disappeared: %s" % vif_id)
|
||||||
@ -251,8 +252,6 @@ class OVSNaaSPlugin:
|
|||||||
|
|
||||||
old_vif_ports = new_vif_ports
|
old_vif_ports = new_vif_ports
|
||||||
old_local_bindings = new_local_bindings
|
old_local_bindings = new_local_bindings
|
||||||
self.int_br.run_cmd(["bash",
|
|
||||||
"/etc/xapi.d/plugins/set_external_ids.sh"])
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@ -291,7 +290,7 @@ if __name__ == "__main__":
|
|||||||
LOG.info("Connecting to database \"%s\" on %s" % (db_name, db_host))
|
LOG.info("Connecting to database \"%s\" on %s" % (db_name, db_host))
|
||||||
conn = MySQLdb.connect(host=db_host, user=db_user,
|
conn = MySQLdb.connect(host=db_host, user=db_user,
|
||||||
passwd=db_pass, db=db_name)
|
passwd=db_pass, db=db_name)
|
||||||
plugin = OVSNaaSPlugin(integ_br)
|
plugin = OVSQuantumAgent(integ_br)
|
||||||
plugin.daemon_loop(conn)
|
plugin.daemon_loop(conn)
|
||||||
finally:
|
finally:
|
||||||
if conn:
|
if conn:
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
VIFLIST=`xe vif-list params=uuid --minimal | sed s/,/" "/g`
|
|
||||||
for VIF_UUID in $VIFLIST; do
|
|
||||||
DEVICE_NUM=`xe vif-list params=device uuid=$VIF_UUID --minimal`
|
|
||||||
VM_NAME=`xe vif-list params=vm-name-label uuid=$VIF_UUID --minimal`
|
|
||||||
NAME="$VM_NAME-eth$DEVICE_NUM"
|
|
||||||
echo "Vif: $VIF_UUID is '$NAME'"
|
|
||||||
xe vif-param-set uuid=$VIF_UUID other-config:nicira-iface-id="$NAME"
|
|
||||||
done
|
|
||||||
|
|
||||||
ps auxw | grep -v grep | grep ovs-xapi-sync > /dev/null 2>&1
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
killall -HUP ovs-xapi-sync
|
|
||||||
fi
|
|
||||||
|
|
@ -56,21 +56,3 @@ def remove_vlan_binding(netid):
|
|||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
pass
|
pass
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
|
|
||||||
def update_network_binding(netid, ifaceid):
|
|
||||||
session = db.get_session()
|
|
||||||
# Add to or delete from the bindings table
|
|
||||||
if ifaceid == None:
|
|
||||||
try:
|
|
||||||
binding = session.query(ovs_models.NetworkBinding).\
|
|
||||||
filter_by(network_id=netid).\
|
|
||||||
one()
|
|
||||||
session.delete(binding)
|
|
||||||
except exc.NoResultFound:
|
|
||||||
raise Exception("No binding found with network_id = %s" % netid)
|
|
||||||
else:
|
|
||||||
binding = ovs_models.NetworkBinding(netid, ifaceid)
|
|
||||||
session.add(binding)
|
|
||||||
|
|
||||||
session.flush()
|
|
||||||
|
@ -26,23 +26,6 @@ from sqlalchemy.orm import relation
|
|||||||
from quantum.db.models import BASE
|
from quantum.db.models import BASE
|
||||||
|
|
||||||
|
|
||||||
class NetworkBinding(BASE):
|
|
||||||
"""Represents a binding of network_id, vif_id"""
|
|
||||||
__tablename__ = 'network_bindings'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
||||||
network_id = Column(String(255))
|
|
||||||
vif_id = Column(String(255))
|
|
||||||
|
|
||||||
def __init__(self, network_id, vif_id):
|
|
||||||
self.network_id = network_id
|
|
||||||
self.vif_id = vif_id
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<NetworkBinding(%s,%s)>" % \
|
|
||||||
(self.network_id, self.vif_id)
|
|
||||||
|
|
||||||
|
|
||||||
class VlanBinding(BASE):
|
class VlanBinding(BASE):
|
||||||
"""Represents a binding of network_id, vlan_id"""
|
"""Represents a binding of network_id, vlan_id"""
|
||||||
__tablename__ = 'vlan_bindings'
|
__tablename__ = 'vlan_bindings'
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
[DATABASE]
|
[DATABASE]
|
||||||
name = ovs_naas
|
name = ovs_quantum
|
||||||
user = root
|
user = root
|
||||||
pass = foobar
|
pass = nova
|
||||||
host = 127.0.0.1
|
host = 127.0.0.1
|
||||||
port = 3306
|
port = 3306
|
||||||
|
|
||||||
[OVS]
|
[OVS]
|
||||||
integration-bridge = xapi1
|
integration-bridge = br100
|
||||||
|
@ -200,11 +200,9 @@ class OVSQuantumPlugin(QuantumPluginBase):
|
|||||||
|
|
||||||
def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id):
|
def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id):
|
||||||
db.port_set_attachment(port_id, remote_iface_id)
|
db.port_set_attachment(port_id, remote_iface_id)
|
||||||
ovs_db.update_network_binding(net_id, remote_iface_id)
|
|
||||||
|
|
||||||
def unplug_interface(self, tenant_id, net_id, port_id):
|
def unplug_interface(self, tenant_id, net_id, port_id):
|
||||||
db.port_set_attachment(port_id, "")
|
db.port_set_attachment(port_id, "")
|
||||||
ovs_db.update_network_binding(net_id, None)
|
|
||||||
|
|
||||||
def get_interface_details(self, tenant_id, net_id, port_id):
|
def get_interface_details(self, tenant_id, net_id, port_id):
|
||||||
res = db.port_get(port_id)
|
res = db.port_get(port_id)
|
||||||
|
@ -22,6 +22,7 @@ QuantumPluginBase provides the definition of minimum set of
|
|||||||
methods that needs to be implemented by a Quantum Plug-in.
|
methods that needs to be implemented by a Quantum Plug-in.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import inspect
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
@ -242,7 +243,17 @@ class QuantumPluginBase(object):
|
|||||||
"""
|
"""
|
||||||
if cls is QuantumPluginBase:
|
if cls is QuantumPluginBase:
|
||||||
for method in cls.__abstractmethods__:
|
for method in cls.__abstractmethods__:
|
||||||
if any(method in base.__dict__ for base in klass.__mro__):
|
method_ok = False
|
||||||
|
for base in klass.__mro__:
|
||||||
|
if method in base.__dict__:
|
||||||
|
fn_obj = base.__dict__[method]
|
||||||
|
if inspect.isfunction(fn_obj):
|
||||||
|
abstract_fn_obj = cls.__dict__[method]
|
||||||
|
arg_count = fn_obj.func_code.co_argcount
|
||||||
|
expected_arg_count = \
|
||||||
|
abstract_fn_obj.func_code.co_argcount
|
||||||
|
method_ok = arg_count == expected_arg_count
|
||||||
|
if method_ok:
|
||||||
continue
|
continue
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return True
|
return True
|
||||||
|
10
run_tests.py
10
run_tests.py
@ -63,6 +63,7 @@ To run a single functional test module::
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
import sys
|
import sys
|
||||||
@ -281,12 +282,19 @@ class QuantumTestRunner(core.TextTestRunner):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
# Set up test logger.
|
||||||
|
logger = logging.getLogger()
|
||||||
|
hdlr = logging.StreamHandler()
|
||||||
|
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
||||||
|
hdlr.setFormatter(formatter)
|
||||||
|
logger.addHandler(hdlr)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
working_dir = os.path.abspath("tests")
|
working_dir = os.path.abspath("tests")
|
||||||
c = config.Config(stream=sys.stdout,
|
c = config.Config(stream=sys.stdout,
|
||||||
env=os.environ,
|
env=os.environ,
|
||||||
verbosity=3,
|
verbosity=3,
|
||||||
workingDir=working_dir)
|
workingDir=working_dir)
|
||||||
|
|
||||||
runner = QuantumTestRunner(stream=c.stream,
|
runner = QuantumTestRunner(stream=c.stream,
|
||||||
verbosity=c.verbosity,
|
verbosity=c.verbosity,
|
||||||
config=c)
|
config=c)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
function usage {
|
function usage {
|
||||||
echo "Usage: $0 [OPTION]..."
|
echo "Usage: $0 [OPTION]..."
|
||||||
echo "Run Melange's test suite(s)"
|
echo "Run Quantum's test suite(s)"
|
||||||
echo ""
|
echo ""
|
||||||
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||||
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
# 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
|
|
@ -1,137 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2011 Citrix Systems
|
|
||||||
# Copyright 2011 Nicira Networks
|
|
||||||
# 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
|
|
||||||
import simplejson
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
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'
|
|
||||||
FORMAT = "json"
|
|
||||||
|
|
||||||
test_network1_data = \
|
|
||||||
{'network': {'network-name': 'test1'}}
|
|
||||||
test_network2_data = \
|
|
||||||
{'network': {'network-name': 'test2'}}
|
|
||||||
|
|
||||||
|
|
||||||
def print_response(res):
|
|
||||||
content = res.read()
|
|
||||||
print "Status: %s" % res.status
|
|
||||||
print "Content: %s" % content
|
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
class QuantumTest(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.client = MiniClient(HOST, PORT, USE_SSL)
|
|
||||||
|
|
||||||
def create_network(self, data):
|
|
||||||
content_type = "application/" + FORMAT
|
|
||||||
body = Serializer().serialize(data, content_type)
|
|
||||||
res = self.client.do_request(TENANT_ID, 'POST', "/networks." + FORMAT,
|
|
||||||
body=body)
|
|
||||||
self.assertEqual(res.status, 200, "bad response: %s" % res.read())
|
|
||||||
|
|
||||||
def test_listNetworks(self):
|
|
||||||
self.create_network(test_network1_data)
|
|
||||||
self.create_network(test_network2_data)
|
|
||||||
res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
|
|
||||||
self.assertEqual(res.status, 200, "bad response: %s" % res.read())
|
|
||||||
|
|
||||||
def test_createNetwork(self):
|
|
||||||
self.create_network(test_network1_data)
|
|
||||||
|
|
||||||
def test_createPort(self):
|
|
||||||
self.create_network(test_network1_data)
|
|
||||||
res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
|
|
||||||
resdict = simplejson.loads(res.read())
|
|
||||||
for n in resdict["networks"]:
|
|
||||||
net_id = n["id"]
|
|
||||||
|
|
||||||
# Step 1 - List Ports for network (should not find any)
|
|
||||||
res = self.client.do_request(TENANT_ID, 'GET',
|
|
||||||
"/networks/%s/ports.%s" % (net_id, FORMAT))
|
|
||||||
self.assertEqual(res.status, 200, "Bad response: %s" % res.read())
|
|
||||||
output = res.read()
|
|
||||||
self.assertTrue(len(output) == 0,
|
|
||||||
"Found unexpected ports: %s" % output)
|
|
||||||
|
|
||||||
# Step 2 - Create Port for network
|
|
||||||
res = self.client.do_request(TENANT_ID, 'POST',
|
|
||||||
"/networks/%s/ports.%s" % (net_id, FORMAT))
|
|
||||||
self.assertEqual(res.status, 200, "Bad response: %s" % output)
|
|
||||||
|
|
||||||
# Step 3 - List Ports for network (again); should find one
|
|
||||||
res = self.client.do_request(TENANT_ID, 'GET',
|
|
||||||
"/networks/%s/ports.%s" % (net_id, FORMAT))
|
|
||||||
output = res.read()
|
|
||||||
self.assertEqual(res.status, 200, "Bad response: %s" % output)
|
|
||||||
resdict = simplejson.loads(output)
|
|
||||||
ids = []
|
|
||||||
for p in resdict["ports"]:
|
|
||||||
ids.append(p["id"])
|
|
||||||
self.assertTrue(len(ids) == 1,
|
|
||||||
"Didn't find expected # of ports (1): %s" % ids)
|
|
||||||
|
|
||||||
def test_renameNetwork(self):
|
|
||||||
self.create_network(test_network1_data)
|
|
||||||
res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
|
|
||||||
resdict = simplejson.loads(res.read())
|
|
||||||
net_id = resdict["networks"][0]["id"]
|
|
||||||
|
|
||||||
data = test_network1_data.copy()
|
|
||||||
data['network']['network-name'] = 'test_renamed'
|
|
||||||
content_type = "application/" + FORMAT
|
|
||||||
body = Serializer().serialize(data, content_type)
|
|
||||||
res = self.client.do_request(TENANT_ID, 'PUT',
|
|
||||||
"/networks/%s.%s" % (net_id, FORMAT), body=body)
|
|
||||||
resdict = simplejson.loads(res.read())
|
|
||||||
self.assertTrue(resdict["networks"]["network"]["id"] == net_id,
|
|
||||||
"Network_rename: renamed network has a different uuid")
|
|
||||||
self.assertTrue(
|
|
||||||
resdict["networks"]["network"]["name"] == "test_renamed",
|
|
||||||
"Network rename didn't take effect")
|
|
||||||
|
|
||||||
def delete_networks(self):
|
|
||||||
# Remove all the networks created on the tenant
|
|
||||||
res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
|
|
||||||
resdict = simplejson.loads(res.read())
|
|
||||||
for n in resdict["networks"]:
|
|
||||||
net_id = n["id"]
|
|
||||||
res = self.client.do_request(TENANT_ID, 'DELETE',
|
|
||||||
"/networks/" + net_id + "." + FORMAT)
|
|
||||||
self.assertEqual(res.status, 202)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
pass
|
|
||||||
#self.delete_networks()
|
|
||||||
|
|
||||||
# Standard boilerplate to call the main() function.
|
|
||||||
if __name__ == '__main__':
|
|
||||||
suite = unittest.TestLoader().loadTestsFromTestCase(QuantumTest)
|
|
||||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
|
831
tests/unit/test_api.py
Normal file
831
tests/unit/test_api.py
Normal file
@ -0,0 +1,831 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010-2011 ????
|
||||||
|
# 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: Brad Hall, Nicira Networks
|
||||||
|
# @author: Salvatore Orlando, Citrix Systems
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import tests.unit.testlib_api as testlib
|
||||||
|
|
||||||
|
from quantum import api as server
|
||||||
|
from quantum.db import api as db
|
||||||
|
from quantum.common.wsgi import Serializer
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger('quantum.tests.test_api')
|
||||||
|
|
||||||
|
|
||||||
|
class APITest(unittest.TestCase):
|
||||||
|
|
||||||
|
def _create_network(self, format, name=None, custom_req_body=None,
|
||||||
|
expected_res_status=200):
|
||||||
|
LOG.debug("Creating network")
|
||||||
|
content_type = "application/" + format
|
||||||
|
if name:
|
||||||
|
net_name = name
|
||||||
|
else:
|
||||||
|
net_name = self.network_name
|
||||||
|
network_req = testlib.new_network_request(self.tenant_id,
|
||||||
|
net_name, format,
|
||||||
|
custom_req_body)
|
||||||
|
network_res = network_req.get_response(self.api)
|
||||||
|
self.assertEqual(network_res.status_int, expected_res_status)
|
||||||
|
if expected_res_status == 200:
|
||||||
|
network_data = Serializer().deserialize(network_res.body,
|
||||||
|
content_type)
|
||||||
|
return network_data['networks']['network']['id']
|
||||||
|
|
||||||
|
def _create_port(self, network_id, port_state, format,
|
||||||
|
custom_req_body=None, expected_res_status=200):
|
||||||
|
LOG.debug("Creating port for network %s", network_id)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
port_req = testlib.new_port_request(self.tenant_id, network_id,
|
||||||
|
port_state, format,
|
||||||
|
custom_req_body)
|
||||||
|
port_res = port_req.get_response(self.api)
|
||||||
|
self.assertEqual(port_res.status_int, expected_res_status)
|
||||||
|
if expected_res_status == 200:
|
||||||
|
port_data = Serializer().deserialize(port_res.body, content_type)
|
||||||
|
return port_data['ports']['port']['id']
|
||||||
|
|
||||||
|
def _test_create_network(self, format):
|
||||||
|
LOG.debug("_test_create_network - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
show_network_req = testlib.show_network_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
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(network_id,
|
||||||
|
network_data['networks']['network']['id'])
|
||||||
|
LOG.debug("_test_create_network - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_create_network_badrequest(self, format):
|
||||||
|
LOG.debug("_test_create_network_badrequest - format:%s - START",
|
||||||
|
format)
|
||||||
|
bad_body = {'network': {'bad-attribute': 'very-bad'}}
|
||||||
|
self._create_network(format, custom_req_body=bad_body,
|
||||||
|
expected_res_status=400)
|
||||||
|
LOG.debug("_test_create_network_badrequest - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_list_networks(self, format):
|
||||||
|
LOG.debug("_test_list_networks - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
self._create_network(format, "net_1")
|
||||||
|
self._create_network(format, "net_2")
|
||||||
|
list_network_req = testlib.network_list_request(self.tenant_id,
|
||||||
|
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)
|
||||||
|
# Check network count: should return 2
|
||||||
|
self.assertEqual(len(network_data['networks']), 2)
|
||||||
|
LOG.debug("_test_list_networks - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_show_network(self, format):
|
||||||
|
LOG.debug("_test_show_network - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
show_network_req = testlib.show_network_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
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'])
|
||||||
|
LOG.debug("_test_show_network - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_show_network_not_found(self, format):
|
||||||
|
LOG.debug("_test_show_network_not_found - format:%s - START", format)
|
||||||
|
show_network_req = testlib.show_network_request(self.tenant_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
format)
|
||||||
|
show_network_res = show_network_req.get_response(self.api)
|
||||||
|
self.assertEqual(show_network_res.status_int, 420)
|
||||||
|
LOG.debug("_test_show_network_not_found - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_rename_network(self, format):
|
||||||
|
LOG.debug("_test_rename_network - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
new_name = 'new_network_name'
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
update_network_req = testlib.update_network_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
new_name,
|
||||||
|
format)
|
||||||
|
update_network_res = update_network_req.get_response(self.api)
|
||||||
|
self.assertEqual(update_network_res.status_int, 202)
|
||||||
|
show_network_req = testlib.show_network_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
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'])
|
||||||
|
LOG.debug("_test_rename_network - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_rename_network_badrequest(self, format):
|
||||||
|
LOG.debug("_test_rename_network_badrequest - format:%s - START",
|
||||||
|
format)
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
bad_body = {'network': {'bad-attribute': 'very-bad'}}
|
||||||
|
update_network_req = testlib.\
|
||||||
|
update_network_request(self.tenant_id,
|
||||||
|
network_id, format,
|
||||||
|
custom_req_body=bad_body)
|
||||||
|
update_network_res = update_network_req.get_response(self.api)
|
||||||
|
self.assertEqual(update_network_res.status_int, 400)
|
||||||
|
LOG.debug("_test_rename_network_badrequest - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_rename_network_not_found(self, format):
|
||||||
|
LOG.debug("_test_rename_network_not_found - format:%s - START",
|
||||||
|
format)
|
||||||
|
new_name = 'new_network_name'
|
||||||
|
update_network_req = testlib.update_network_request(self.tenant_id,
|
||||||
|
"A BAD ID",
|
||||||
|
new_name,
|
||||||
|
format)
|
||||||
|
update_network_res = update_network_req.get_response(self.api)
|
||||||
|
self.assertEqual(update_network_res.status_int, 420)
|
||||||
|
LOG.debug("_test_rename_network_not_found - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_delete_network(self, format):
|
||||||
|
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())
|
||||||
|
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, 202)
|
||||||
|
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_count = len(network_list_data['networks'])
|
||||||
|
self.assertEqual(network_count, 0)
|
||||||
|
LOG.debug("_test_delete_network - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_delete_network_in_use(self, format):
|
||||||
|
LOG.debug("_test_delete_network_in_use - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
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())
|
||||||
|
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)
|
||||||
|
attachment_req = testlib.put_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
port_id,
|
||||||
|
attachment_id)
|
||||||
|
attachment_res = attachment_req.get_response(self.api)
|
||||||
|
self.assertEquals(attachment_res.status_int, 202)
|
||||||
|
|
||||||
|
LOG.debug("Deleting network %(network_id)s"\
|
||||||
|
" of tenant %(tenant_id)s", locals())
|
||||||
|
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, 421)
|
||||||
|
LOG.debug("_test_delete_network_in_use - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_list_ports(self, format):
|
||||||
|
LOG.debug("_test_list_ports - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
self._create_port(network_id, port_state, format)
|
||||||
|
self._create_port(network_id, port_state, format)
|
||||||
|
list_port_req = testlib.port_list_request(self.tenant_id,
|
||||||
|
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)
|
||||||
|
# Check port count: should return 2
|
||||||
|
self.assertEqual(len(port_data['ports']), 2)
|
||||||
|
LOG.debug("_test_list_ports - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_show_port(self, format):
|
||||||
|
LOG.debug("_test_show_port - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
show_port_req = testlib.show_port_request(self.tenant_id,
|
||||||
|
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({'id': port_id, 'state': port_state},
|
||||||
|
port_data['ports']['port'])
|
||||||
|
LOG.debug("_test_show_port - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_show_port_networknotfound(self, format):
|
||||||
|
LOG.debug("_test_show_port_networknotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
show_port_req = testlib.show_port_request(self.tenant_id,
|
||||||
|
"A_BAD_ID", port_id,
|
||||||
|
format)
|
||||||
|
show_port_res = show_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(show_port_res.status_int, 420)
|
||||||
|
LOG.debug("_test_show_port_networknotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_show_port_portnotfound(self, format):
|
||||||
|
LOG.debug("_test_show_port_portnotfound - format:%s - START", format)
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
show_port_req = testlib.show_port_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
format)
|
||||||
|
show_port_res = show_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(show_port_res.status_int, 430)
|
||||||
|
LOG.debug("_test_show_port_portnotfound - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_create_port(self, format):
|
||||||
|
LOG.debug("_test_create_port - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
show_port_req = testlib.show_port_request(self.tenant_id,
|
||||||
|
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'])
|
||||||
|
LOG.debug("_test_create_port - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_create_port_networknotfound(self, format):
|
||||||
|
LOG.debug("_test_create_port_networknotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
self._create_port("A_BAD_ID", port_state, format,
|
||||||
|
expected_res_status=420)
|
||||||
|
LOG.debug("_test_create_port_networknotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_create_port_badrequest(self, format):
|
||||||
|
LOG.debug("_test_create_port_badrequest - format:%s - START", format)
|
||||||
|
bad_body = {'bad-resource': {'bad-attribute': 'bad-value'}}
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
self._create_port(network_id, port_state, format,
|
||||||
|
custom_req_body=bad_body, expected_res_status=400)
|
||||||
|
LOG.debug("_test_create_port_badrequest - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_delete_port(self, format):
|
||||||
|
LOG.debug("_test_delete_port - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
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())
|
||||||
|
delete_port_req = testlib.port_delete_request(self.tenant_id,
|
||||||
|
network_id, port_id,
|
||||||
|
format)
|
||||||
|
delete_port_res = delete_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(delete_port_res.status_int, 202)
|
||||||
|
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_count = len(port_list_data['ports'])
|
||||||
|
self.assertEqual(port_count, 0)
|
||||||
|
LOG.debug("_test_delete_port - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_delete_port_in_use(self, format):
|
||||||
|
LOG.debug("_test_delete_port_in_use - format:%s - START", format)
|
||||||
|
content_type = "application/" + format
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
attachment_id = "test_attachment"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
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)
|
||||||
|
attachment_req = testlib.put_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
port_id,
|
||||||
|
attachment_id)
|
||||||
|
attachment_res = attachment_req.get_response(self.api)
|
||||||
|
self.assertEquals(attachment_res.status_int, 202)
|
||||||
|
LOG.debug("Deleting port %(port_id)s for network %(network_id)s"\
|
||||||
|
" of tenant %(tenant_id)s", locals())
|
||||||
|
delete_port_req = testlib.port_delete_request(self.tenant_id,
|
||||||
|
network_id, port_id,
|
||||||
|
format)
|
||||||
|
delete_port_res = delete_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(delete_port_res.status_int, 432)
|
||||||
|
LOG.debug("_test_delete_port_in_use - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_delete_port_with_bad_id(self, format):
|
||||||
|
LOG.debug("_test_delete_port_with_bad_id - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
self._create_port(network_id, port_state, format)
|
||||||
|
# Test for portnotfound
|
||||||
|
delete_port_req = testlib.port_delete_request(self.tenant_id,
|
||||||
|
network_id, "A_BAD_ID",
|
||||||
|
format)
|
||||||
|
delete_port_res = delete_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(delete_port_res.status_int, 430)
|
||||||
|
LOG.debug("_test_delete_port_with_bad_id - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_delete_port_networknotfound(self, format):
|
||||||
|
LOG.debug("_test_delete_port_networknotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
delete_port_req = testlib.port_delete_request(self.tenant_id,
|
||||||
|
"A_BAD_ID", port_id,
|
||||||
|
format)
|
||||||
|
delete_port_res = delete_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(delete_port_res.status_int, 420)
|
||||||
|
LOG.debug("_test_delete_port_networknotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_set_port_state(self, format):
|
||||||
|
LOG.debug("_test_set_port_state - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
port_state = 'DOWN'
|
||||||
|
new_port_state = 'ACTIVE'
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
update_port_req = testlib.update_port_request(self.tenant_id,
|
||||||
|
network_id, port_id,
|
||||||
|
new_port_state,
|
||||||
|
format)
|
||||||
|
update_port_res = update_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(update_port_res.status_int, 200)
|
||||||
|
show_port_req = testlib.show_port_request(self.tenant_id,
|
||||||
|
network_id, port_id,
|
||||||
|
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)
|
||||||
|
self.assertEqual({'id': port_id, 'state': new_port_state},
|
||||||
|
network_data['ports']['port'])
|
||||||
|
LOG.debug("_test_set_port_state - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_set_port_state_networknotfound(self, format):
|
||||||
|
LOG.debug("_test_set_port_state_networknotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = 'DOWN'
|
||||||
|
new_port_state = 'ACTIVE'
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
update_port_req = testlib.update_port_request(self.tenant_id,
|
||||||
|
"A_BAD_ID", port_id,
|
||||||
|
new_port_state,
|
||||||
|
format)
|
||||||
|
update_port_res = update_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(update_port_res.status_int, 420)
|
||||||
|
LOG.debug("_test_set_port_state_networknotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_set_port_state_portnotfound(self, format):
|
||||||
|
LOG.debug("_test_set_port_state_portnotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = 'DOWN'
|
||||||
|
new_port_state = 'ACTIVE'
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
self._create_port(network_id, port_state, format)
|
||||||
|
update_port_req = testlib.update_port_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
new_port_state,
|
||||||
|
format)
|
||||||
|
update_port_res = update_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(update_port_res.status_int, 430)
|
||||||
|
LOG.debug("_test_set_port_state_portnotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_set_port_state_stateinvalid(self, format):
|
||||||
|
LOG.debug("_test_set_port_state_stateinvalid - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = 'DOWN'
|
||||||
|
new_port_state = 'A_BAD_STATE'
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
update_port_req = testlib.update_port_request(self.tenant_id,
|
||||||
|
network_id, port_id,
|
||||||
|
new_port_state,
|
||||||
|
format)
|
||||||
|
update_port_res = update_port_req.get_response(self.api)
|
||||||
|
self.assertEqual(update_port_res.status_int, 431)
|
||||||
|
LOG.debug("_test_set_port_state_stateinvalid - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_show_attachment(self, format):
|
||||||
|
LOG.debug("_test_show_attachment - format:%s - START", format)
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
interface_id = "test_interface"
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
put_attachment_req = testlib.put_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
port_id,
|
||||||
|
interface_id,
|
||||||
|
format)
|
||||||
|
put_attachment_res = put_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(put_attachment_res.status_int, 202)
|
||||||
|
get_attachment_req = testlib.get_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
port_id,
|
||||||
|
format)
|
||||||
|
get_attachment_res = get_attachment_req.get_response(self.api)
|
||||||
|
attachment_data = Serializer().deserialize(get_attachment_res.body,
|
||||||
|
content_type)
|
||||||
|
self.assertEqual(attachment_data['attachment'], interface_id)
|
||||||
|
LOG.debug("_test_show_attachment - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_show_attachment_networknotfound(self, format):
|
||||||
|
LOG.debug("_test_show_attachment_networknotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
get_attachment_req = testlib.get_attachment_request(self.tenant_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
port_id,
|
||||||
|
format)
|
||||||
|
get_attachment_res = get_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(get_attachment_res.status_int, 420)
|
||||||
|
LOG.debug("_test_show_attachment_networknotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_show_attachment_portnotfound(self, format):
|
||||||
|
LOG.debug("_test_show_attachment_portnotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
self._create_port(network_id, port_state, format)
|
||||||
|
get_attachment_req = testlib.get_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
format)
|
||||||
|
get_attachment_res = get_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(get_attachment_res.status_int, 430)
|
||||||
|
LOG.debug("_test_show_attachment_portnotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_put_attachment(self, format):
|
||||||
|
LOG.debug("_test_put_attachment - format:%s - START", format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
interface_id = "test_interface"
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
put_attachment_req = testlib.put_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
port_id,
|
||||||
|
interface_id,
|
||||||
|
format)
|
||||||
|
put_attachment_res = put_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(put_attachment_res.status_int, 202)
|
||||||
|
LOG.debug("_test_put_attachment - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_put_attachment_networknotfound(self, format):
|
||||||
|
LOG.debug("_test_put_attachment_networknotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = 'DOWN'
|
||||||
|
interface_id = "test_interface"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
put_attachment_req = testlib.put_attachment_request(self.tenant_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
port_id,
|
||||||
|
interface_id,
|
||||||
|
format)
|
||||||
|
put_attachment_res = put_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(put_attachment_res.status_int, 420)
|
||||||
|
LOG.debug("_test_put_attachment_networknotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_put_attachment_portnotfound(self, format):
|
||||||
|
LOG.debug("_test_put_attachment_portnotfound - format:%s - START",
|
||||||
|
format)
|
||||||
|
port_state = 'DOWN'
|
||||||
|
interface_id = "test_interface"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
self._create_port(network_id, port_state, format)
|
||||||
|
put_attachment_req = testlib.put_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
interface_id,
|
||||||
|
format)
|
||||||
|
put_attachment_res = put_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(put_attachment_res.status_int, 430)
|
||||||
|
LOG.debug("_test_put_attachment_portnotfound - format:%s - END",
|
||||||
|
format)
|
||||||
|
|
||||||
|
def _test_delete_attachment(self, format):
|
||||||
|
LOG.debug("_test_delete_attachment - format:%s - START", format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
interface_id = "test_interface"
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
put_attachment_req = testlib.put_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
port_id,
|
||||||
|
interface_id,
|
||||||
|
format)
|
||||||
|
put_attachment_res = put_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(put_attachment_res.status_int, 202)
|
||||||
|
del_attachment_req = testlib.delete_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
port_id,
|
||||||
|
format)
|
||||||
|
del_attachment_res = del_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(del_attachment_res.status_int, 202)
|
||||||
|
LOG.debug("_test_delete_attachment - format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_delete_attachment_networknotfound(self, format):
|
||||||
|
LOG.debug("_test_delete_attachment_networknotfound -" \
|
||||||
|
" format:%s - START", format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
port_id = self._create_port(network_id, port_state, format)
|
||||||
|
del_attachment_req = testlib.delete_attachment_request(self.tenant_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
port_id,
|
||||||
|
format)
|
||||||
|
del_attachment_res = del_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(del_attachment_res.status_int, 420)
|
||||||
|
LOG.debug("_test_delete_attachment_networknotfound -" \
|
||||||
|
" format:%s - END", format)
|
||||||
|
|
||||||
|
def _test_delete_attachment_portnotfound(self, format):
|
||||||
|
LOG.debug("_test_delete_attachment_portnotfound - " \
|
||||||
|
" format:%s - START", format)
|
||||||
|
port_state = "ACTIVE"
|
||||||
|
network_id = self._create_network(format)
|
||||||
|
self._create_port(network_id, port_state, format)
|
||||||
|
del_attachment_req = testlib.delete_attachment_request(self.tenant_id,
|
||||||
|
network_id,
|
||||||
|
"A_BAD_ID",
|
||||||
|
format)
|
||||||
|
del_attachment_res = del_attachment_req.get_response(self.api)
|
||||||
|
self.assertEqual(del_attachment_res.status_int, 430)
|
||||||
|
LOG.debug("_test_delete_attachment_portnotfound - " \
|
||||||
|
"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"
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Clear the test environment"""
|
||||||
|
# Remove database contents
|
||||||
|
db.clear_db()
|
||||||
|
|
||||||
|
def test_list_networks_json(self):
|
||||||
|
self._test_list_networks('json')
|
||||||
|
|
||||||
|
def test_list_networks_xml(self):
|
||||||
|
self._test_list_networks('xml')
|
||||||
|
|
||||||
|
def test_create_network_json(self):
|
||||||
|
self._test_create_network('json')
|
||||||
|
|
||||||
|
def test_create_network_xml(self):
|
||||||
|
self._test_create_network('xml')
|
||||||
|
|
||||||
|
def test_create_network_badrequest_json(self):
|
||||||
|
self._test_create_network_badrequest('json')
|
||||||
|
|
||||||
|
def test_create_network_badreqyest_xml(self):
|
||||||
|
self._test_create_network_badrequest('xml')
|
||||||
|
|
||||||
|
def test_show_network_not_found_json(self):
|
||||||
|
self._test_show_network_not_found('json')
|
||||||
|
|
||||||
|
def test_show_network_not_found_xml(self):
|
||||||
|
self._test_show_network_not_found('xml')
|
||||||
|
|
||||||
|
def test_show_network_json(self):
|
||||||
|
self._test_show_network('json')
|
||||||
|
|
||||||
|
def test_show_network_xml(self):
|
||||||
|
self._test_show_network('xml')
|
||||||
|
|
||||||
|
def test_delete_network_json(self):
|
||||||
|
self._test_delete_network('json')
|
||||||
|
|
||||||
|
def test_delete_network_xml(self):
|
||||||
|
self._test_delete_network('xml')
|
||||||
|
|
||||||
|
def test_rename_network_json(self):
|
||||||
|
self._test_rename_network('json')
|
||||||
|
|
||||||
|
def test_rename_network_xml(self):
|
||||||
|
self._test_rename_network('xml')
|
||||||
|
|
||||||
|
def test_rename_network_badrequest_json(self):
|
||||||
|
self._test_rename_network_badrequest('json')
|
||||||
|
|
||||||
|
def test_rename_network_badrequest_xml(self):
|
||||||
|
self._test_rename_network_badrequest('xml')
|
||||||
|
|
||||||
|
def test_rename_network_not_found_json(self):
|
||||||
|
self._test_rename_network_not_found('json')
|
||||||
|
|
||||||
|
def test_rename_network_not_found_xml(self):
|
||||||
|
self._test_rename_network_not_found('xml')
|
||||||
|
|
||||||
|
def test_delete_network_in_use_json(self):
|
||||||
|
self._test_delete_network_in_use('json')
|
||||||
|
|
||||||
|
def test_delete_network_in_use_xml(self):
|
||||||
|
self._test_delete_network_in_use('xml')
|
||||||
|
|
||||||
|
def test_list_ports_json(self):
|
||||||
|
self._test_list_ports('json')
|
||||||
|
|
||||||
|
def test_list_ports_xml(self):
|
||||||
|
self._test_list_ports('xml')
|
||||||
|
|
||||||
|
def test_show_port_json(self):
|
||||||
|
self._test_show_port('json')
|
||||||
|
|
||||||
|
def test_show_port_xml(self):
|
||||||
|
self._test_show_port('xml')
|
||||||
|
|
||||||
|
def test_show_port_networknotfound_json(self):
|
||||||
|
self._test_show_port_networknotfound('json')
|
||||||
|
|
||||||
|
def test_show_port_networknotfound_xml(self):
|
||||||
|
self._test_show_port_networknotfound('xml')
|
||||||
|
|
||||||
|
def test_show_port_portnotfound_json(self):
|
||||||
|
self._test_show_port_portnotfound('json')
|
||||||
|
|
||||||
|
def test_show_port_portnotfound_xml(self):
|
||||||
|
self._test_show_port_portnotfound('xml')
|
||||||
|
|
||||||
|
def test_create_port_json(self):
|
||||||
|
self._test_create_port('json')
|
||||||
|
|
||||||
|
def test_create_port_xml(self):
|
||||||
|
self._test_create_port('xml')
|
||||||
|
|
||||||
|
def test_create_port_networknotfound_json(self):
|
||||||
|
self._test_create_port_networknotfound('json')
|
||||||
|
|
||||||
|
def test_create_port_networknotfound_xml(self):
|
||||||
|
self._test_create_port_networknotfound('xml')
|
||||||
|
|
||||||
|
def test_create_port_badrequest_json(self):
|
||||||
|
self._test_create_port_badrequest('json')
|
||||||
|
|
||||||
|
def test_create_port_badrequest_xml(self):
|
||||||
|
self._test_create_port_badrequest('xml')
|
||||||
|
|
||||||
|
def test_delete_port_xml(self):
|
||||||
|
self._test_delete_port('xml')
|
||||||
|
|
||||||
|
def test_delete_port_json(self):
|
||||||
|
self._test_delete_port('json')
|
||||||
|
|
||||||
|
def test_delete_port_in_use_xml(self):
|
||||||
|
self._test_delete_port_in_use('xml')
|
||||||
|
|
||||||
|
def test_delete_port_in_use_json(self):
|
||||||
|
self._test_delete_port_in_use('json')
|
||||||
|
|
||||||
|
def test_delete_port_networknotfound_xml(self):
|
||||||
|
self._test_delete_port_networknotfound('xml')
|
||||||
|
|
||||||
|
def test_delete_port_networknotfound_json(self):
|
||||||
|
self._test_delete_port_networknotfound('json')
|
||||||
|
|
||||||
|
def test_delete_port_with_bad_id_xml(self):
|
||||||
|
self._test_delete_port_with_bad_id('xml')
|
||||||
|
|
||||||
|
def test_delete_port_with_bad_id_json(self):
|
||||||
|
self._test_delete_port_with_bad_id('json')
|
||||||
|
|
||||||
|
def test_set_port_state_xml(self):
|
||||||
|
self._test_set_port_state('xml')
|
||||||
|
|
||||||
|
def test_set_port_state_json(self):
|
||||||
|
self._test_set_port_state('json')
|
||||||
|
|
||||||
|
def test_set_port_state_networknotfound_xml(self):
|
||||||
|
self._test_set_port_state_networknotfound('xml')
|
||||||
|
|
||||||
|
def test_set_port_state_networknotfound_json(self):
|
||||||
|
self._test_set_port_state_networknotfound('json')
|
||||||
|
|
||||||
|
def test_set_port_state_portnotfound_xml(self):
|
||||||
|
self._test_set_port_state_portnotfound('xml')
|
||||||
|
|
||||||
|
def test_set_port_state_portnotfound_json(self):
|
||||||
|
self._test_set_port_state_portnotfound('json')
|
||||||
|
|
||||||
|
def test_set_port_state_stateinvalid_xml(self):
|
||||||
|
self._test_set_port_state_stateinvalid('xml')
|
||||||
|
|
||||||
|
def test_set_port_state_stateinvalid_json(self):
|
||||||
|
self._test_set_port_state_stateinvalid('json')
|
||||||
|
|
||||||
|
def test_show_attachment_xml(self):
|
||||||
|
self._test_show_attachment('xml')
|
||||||
|
|
||||||
|
def test_show_attachment_json(self):
|
||||||
|
self._test_show_attachment('json')
|
||||||
|
|
||||||
|
def test_show_attachment_networknotfound_xml(self):
|
||||||
|
self._test_show_attachment_networknotfound('xml')
|
||||||
|
|
||||||
|
def test_show_attachment_networknotfound_json(self):
|
||||||
|
self._test_show_attachment_networknotfound('json')
|
||||||
|
|
||||||
|
def test_show_attachment_portnotfound_xml(self):
|
||||||
|
self._test_show_attachment_portnotfound('xml')
|
||||||
|
|
||||||
|
def test_show_attachment_portnotfound_json(self):
|
||||||
|
self._test_show_attachment_portnotfound('json')
|
||||||
|
|
||||||
|
def test_put_attachment_xml(self):
|
||||||
|
self._test_put_attachment('xml')
|
||||||
|
|
||||||
|
def test_put_attachment_json(self):
|
||||||
|
self._test_put_attachment('json')
|
||||||
|
|
||||||
|
def test_put_attachment_networknotfound_xml(self):
|
||||||
|
self._test_put_attachment_networknotfound('xml')
|
||||||
|
|
||||||
|
def test_put_attachment_networknotfound_json(self):
|
||||||
|
self._test_put_attachment_networknotfound('json')
|
||||||
|
|
||||||
|
def test_put_attachment_portnotfound_xml(self):
|
||||||
|
self._test_put_attachment_portnotfound('xml')
|
||||||
|
|
||||||
|
def test_put_attachment_portnotfound_json(self):
|
||||||
|
self._test_put_attachment_portnotfound('json')
|
||||||
|
|
||||||
|
def test_delete_attachment_xml(self):
|
||||||
|
self._test_delete_attachment('xml')
|
||||||
|
|
||||||
|
def test_delete_attachment_json(self):
|
||||||
|
self._test_delete_attachment('json')
|
||||||
|
|
||||||
|
def test_delete_attachment_networknotfound_xml(self):
|
||||||
|
self._test_delete_attachment_networknotfound('xml')
|
||||||
|
|
||||||
|
def test_delete_attachment_networknotfound_json(self):
|
||||||
|
self._test_delete_attachment_networknotfound('json')
|
||||||
|
|
||||||
|
def test_delete_attachment_portnotfound_xml(self):
|
||||||
|
self._test_delete_attachment_portnotfound('xml')
|
||||||
|
|
||||||
|
def test_delete_attachment_portnotfound_json(self):
|
||||||
|
self._test_delete_attachment_portnotfound('json')
|
130
tests/unit/testlib_api.py
Normal file
130
tests/unit/testlib_api.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import webob
|
||||||
|
|
||||||
|
from quantum.common.wsgi import Serializer
|
||||||
|
|
||||||
|
|
||||||
|
def create_request(path, body, content_type, method='GET'):
|
||||||
|
req = webob.Request.blank(path)
|
||||||
|
req.method = method
|
||||||
|
req.headers = {}
|
||||||
|
req.headers['Accept'] = content_type
|
||||||
|
req.body = body
|
||||||
|
return req
|
||||||
|
|
||||||
|
|
||||||
|
def network_list_request(tenant_id, format='xml'):
|
||||||
|
method = 'GET'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def show_network_request(tenant_id, network_id, format='xml'):
|
||||||
|
method = 'GET'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks" \
|
||||||
|
"/%(network_id)s.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def new_network_request(tenant_id, network_name='new_name',
|
||||||
|
format='xml', custom_req_body=None):
|
||||||
|
method = 'POST'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks.%(format)s" % locals()
|
||||||
|
data = custom_req_body or {'network': {'net-name': '%s' % network_name}}
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
body = Serializer().serialize(data, content_type)
|
||||||
|
return create_request(path, body, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def update_network_request(tenant_id, network_id, network_name, format='xml',
|
||||||
|
custom_req_body=None):
|
||||||
|
method = 'PUT'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks" \
|
||||||
|
"/%(network_id)s.%(format)s" % locals()
|
||||||
|
data = custom_req_body or {'network': {'net-name': '%s' % network_name}}
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
body = Serializer().serialize(data, content_type)
|
||||||
|
return create_request(path, body, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def network_delete_request(tenant_id, network_id, format='xml'):
|
||||||
|
method = 'DELETE'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/" \
|
||||||
|
"%(network_id)s.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def port_list_request(tenant_id, network_id, format='xml'):
|
||||||
|
method = 'GET'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/" \
|
||||||
|
"%(network_id)s/ports.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def show_port_request(tenant_id, network_id, port_id, format='xml'):
|
||||||
|
method = 'GET'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/%(network_id)s" \
|
||||||
|
"/ports/%(port_id)s.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def new_port_request(tenant_id, network_id, port_state,
|
||||||
|
format='xml', custom_req_body=None):
|
||||||
|
method = 'POST'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/" \
|
||||||
|
"%(network_id)s/ports.%(format)s" % locals()
|
||||||
|
data = custom_req_body or {'port': {'port-state': '%s' % port_state}}
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
body = Serializer().serialize(data, content_type)
|
||||||
|
return create_request(path, body, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def port_delete_request(tenant_id, network_id, port_id, format='xml'):
|
||||||
|
method = 'DELETE'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/" \
|
||||||
|
"%(network_id)s/ports/%(port_id)s.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def update_port_request(tenant_id, network_id, port_id, port_state,
|
||||||
|
format='xml', custom_req_body=None):
|
||||||
|
method = 'PUT'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks" \
|
||||||
|
"/%(network_id)s/ports/%(port_id)s.%(format)s" % locals()
|
||||||
|
data = custom_req_body or {'port': {'port-state': '%s' % port_state}}
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
body = Serializer().serialize(data, content_type)
|
||||||
|
return create_request(path, body, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def get_attachment_request(tenant_id, network_id, port_id, format='xml'):
|
||||||
|
method = 'GET'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/" \
|
||||||
|
"%(network_id)s/ports/%(port_id)s/attachment.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def put_attachment_request(tenant_id, network_id, port_id,
|
||||||
|
attachment_id, format='xml'):
|
||||||
|
method = 'PUT'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/" \
|
||||||
|
"%(network_id)s/ports/%(port_id)s/attachment.%(format)s" % locals()
|
||||||
|
data = {'port': {'attachment-id': attachment_id}}
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
body = Serializer().serialize(data, content_type)
|
||||||
|
return create_request(path, body, content_type, method)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_attachment_request(tenant_id, network_id, port_id,
|
||||||
|
attachment_id, format='xml'):
|
||||||
|
method = 'DELETE'
|
||||||
|
path = "/tenants/%(tenant_id)s/networks/" \
|
||||||
|
"%(network_id)s/ports/%(port_id)s/attachment.%(format)s" % locals()
|
||||||
|
content_type = "application/%s" % format
|
||||||
|
return create_request(path, None, content_type, method)
|
174
tools/batch_config.py
Normal file
174
tools/batch_config.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 Nicira Networks, Inc.
|
||||||
|
#
|
||||||
|
# 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: Dan Wendlandt, Nicira Networks, Inc.
|
||||||
|
|
||||||
|
import httplib
|
||||||
|
import logging as LOG
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from quantum.manager import QuantumManager
|
||||||
|
from optparse import OptionParser
|
||||||
|
from quantum.common.wsgi import Serializer
|
||||||
|
from quantum.cli import MiniClient
|
||||||
|
|
||||||
|
FORMAT = "json"
|
||||||
|
CONTENT_TYPE = "application/" + FORMAT
|
||||||
|
|
||||||
|
|
||||||
|
def delete_all_nets(client, tenant_id):
|
||||||
|
res = client.do_request(tenant_id, 'GET', "/networks." + FORMAT)
|
||||||
|
resdict = json.loads(res.read())
|
||||||
|
LOG.debug(resdict)
|
||||||
|
for n in resdict["networks"]:
|
||||||
|
nid = n["id"]
|
||||||
|
|
||||||
|
res = client.do_request(tenant_id, 'GET',
|
||||||
|
"/networks/%s/ports.%s" % (nid, FORMAT))
|
||||||
|
output = res.read()
|
||||||
|
if res.status != 200:
|
||||||
|
LOG.error("Failed to list ports: %s" % output)
|
||||||
|
continue
|
||||||
|
rd = json.loads(output)
|
||||||
|
LOG.debug(rd)
|
||||||
|
for port in rd["ports"]:
|
||||||
|
pid = port["id"]
|
||||||
|
|
||||||
|
data = {'port': {'attachment-id': ''}}
|
||||||
|
body = Serializer().serialize(data, CONTENT_TYPE)
|
||||||
|
res = client.do_request(tenant_id, 'DELETE',
|
||||||
|
"/networks/%s/ports/%s/attachment.%s" % \
|
||||||
|
(nid, pid, FORMAT), body=body)
|
||||||
|
output = res.read()
|
||||||
|
LOG.debug(output)
|
||||||
|
if res.status != 202:
|
||||||
|
LOG.error("Failed to unplug iface from port \"%s\": %s" % (vid,
|
||||||
|
pid, output))
|
||||||
|
continue
|
||||||
|
LOG.info("Unplugged interface from port:%s on network:%s" % (pid,
|
||||||
|
nid))
|
||||||
|
|
||||||
|
res = client.do_request(tenant_id, 'DELETE',
|
||||||
|
"/networks/%s/ports/%s.%s" % (nid, pid, FORMAT))
|
||||||
|
output = res.read()
|
||||||
|
if res.status != 202:
|
||||||
|
LOG.error("Failed to delete port: %s" % output)
|
||||||
|
continue
|
||||||
|
print "Deleted Virtual Port:%s " \
|
||||||
|
"on Virtual Network:%s" % (pid, nid)
|
||||||
|
|
||||||
|
res = client.do_request(tenant_id, 'DELETE',
|
||||||
|
"/networks/" + nid + "." + FORMAT)
|
||||||
|
status = res.status
|
||||||
|
if status != 202:
|
||||||
|
Log.error("Failed to delete network: %s" % nid)
|
||||||
|
output = res.read()
|
||||||
|
print output
|
||||||
|
else:
|
||||||
|
print "Deleted Virtual Network with ID:%s" % nid
|
||||||
|
|
||||||
|
|
||||||
|
def create_net_with_attachments(net_name, iface_ids):
|
||||||
|
data = {'network': {'network-name': '%s' % net_name}}
|
||||||
|
body = Serializer().serialize(data, CONTENT_TYPE)
|
||||||
|
res = client.do_request(tenant_id, 'POST',
|
||||||
|
"/networks." + FORMAT, body=body)
|
||||||
|
rd = json.loads(res.read())
|
||||||
|
LOG.debug(rd)
|
||||||
|
nid = rd["networks"]["network"]["id"]
|
||||||
|
print "Created a new Virtual Network %s with ID:%s" % (net_name, nid)
|
||||||
|
|
||||||
|
for iface_id in iface_ids:
|
||||||
|
res = client.do_request(tenant_id, 'POST',
|
||||||
|
"/networks/%s/ports.%s" % (nid, FORMAT))
|
||||||
|
output = res.read()
|
||||||
|
if res.status != 200:
|
||||||
|
LOG.error("Failed to create port: %s" % output)
|
||||||
|
continue
|
||||||
|
rd = json.loads(output)
|
||||||
|
new_port_id = rd["ports"]["port"]["id"]
|
||||||
|
print "Created Virtual Port:%s " \
|
||||||
|
"on Virtual Network:%s" % (new_port_id, nid)
|
||||||
|
data = {'port': {'attachment-id': '%s' % iface_id}}
|
||||||
|
body = Serializer().serialize(data, CONTENT_TYPE)
|
||||||
|
res = client.do_request(tenant_id, 'PUT',
|
||||||
|
"/networks/%s/ports/%s/attachment.%s" %\
|
||||||
|
(nid, new_port_id, FORMAT), body=body)
|
||||||
|
output = res.read()
|
||||||
|
LOG.debug(output)
|
||||||
|
if res.status != 202:
|
||||||
|
LOG.error("Failed to plug iface \"%s\" to port \"%s\": %s" % \
|
||||||
|
(iface_id, new_port_id, output))
|
||||||
|
continue
|
||||||
|
print "Plugged interface \"%s\" to port:%s on network:%s" % \
|
||||||
|
(iface_id, new_port_id, nid)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
usagestr = "Usage: %prog [OPTIONS] <tenant-id> <config-string> [args]\n" \
|
||||||
|
"Example config-string: net1=instance-1,instance-2"\
|
||||||
|
":net2=instance-3,instance-4\n" \
|
||||||
|
"This string would create two networks: \n" \
|
||||||
|
"'net1' would have two ports, with iface-ids "\
|
||||||
|
"instance-1 and instance-2 attached\n" \
|
||||||
|
"'net2' would have two ports, with iface-ids"\
|
||||||
|
" instance-3 and instance-4 attached\n"
|
||||||
|
parser = OptionParser(usage=usagestr)
|
||||||
|
parser.add_option("-H", "--host", dest="host",
|
||||||
|
type="string", default="127.0.0.1", help="ip address of api host")
|
||||||
|
parser.add_option("-p", "--port", dest="port",
|
||||||
|
type="int", default=9696, help="api poort")
|
||||||
|
parser.add_option("-s", "--ssl", dest="ssl",
|
||||||
|
action="store_true", default=False, help="use ssl")
|
||||||
|
parser.add_option("-v", "--verbose", dest="verbose",
|
||||||
|
action="store_true", default=False, help="turn on verbose logging")
|
||||||
|
parser.add_option("-d", "--delete", dest="delete",
|
||||||
|
action="store_true", default=False, \
|
||||||
|
help="delete existing tenants networks")
|
||||||
|
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
if options.verbose:
|
||||||
|
LOG.basicConfig(level=LOG.DEBUG)
|
||||||
|
else:
|
||||||
|
LOG.basicConfig(level=LOG.WARN)
|
||||||
|
|
||||||
|
if len(args) < 1:
|
||||||
|
parser.print_help()
|
||||||
|
help()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
nets = {}
|
||||||
|
tenant_id = args[0]
|
||||||
|
if len(args) > 1:
|
||||||
|
config_str = args[1]
|
||||||
|
for net_str in config_str.split(":"):
|
||||||
|
arr = net_str.split("=")
|
||||||
|
net_name = arr[0]
|
||||||
|
nets[net_name] = arr[1].split(",")
|
||||||
|
|
||||||
|
print "nets: %s" % str(nets)
|
||||||
|
|
||||||
|
client = MiniClient(options.host, options.port, options.ssl)
|
||||||
|
|
||||||
|
if options.delete:
|
||||||
|
delete_all_nets(client, tenant_id)
|
||||||
|
|
||||||
|
for net_name, iface_ids in nets.items():
|
||||||
|
create_net_with_attachments(net_name, iface_ids)
|
||||||
|
|
||||||
|
sys.exit(0)
|
Loading…
Reference in New Issue
Block a user