Merge "Add more association support to network API"
This commit is contained in:
commit
38fd49bb30
@ -296,6 +296,14 @@
|
|||||||
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
||||||
"updated": "2011-12-23T00:00:00+00:00"
|
"updated": "2011-12-23T00:00:00+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": "os-networks-associate",
|
||||||
|
"description": "Network association support",
|
||||||
|
"links": [],
|
||||||
|
"name": "NetworkAssociationSupport",
|
||||||
|
"namespace": "http://docs.openstack.org/compute/ext/networks_associate/api/v2",
|
||||||
|
"updated": "2012-11-19T00:00:00+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": "os-quota-class-sets",
|
"alias": "os-quota-class-sets",
|
||||||
"description": "Quota classes management support",
|
"description": "Quota classes management support",
|
||||||
|
@ -125,6 +125,9 @@
|
|||||||
<extension alias="os-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/networks/api/v1.1" name="Networks">
|
<extension alias="os-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/networks/api/v1.1" name="Networks">
|
||||||
<description>Admin-only Network Management Extension</description>
|
<description>Admin-only Network Management Extension</description>
|
||||||
</extension>
|
</extension>
|
||||||
|
<extension alias="os-networks-associate" updated="2012-11-19T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport">
|
||||||
|
<description>Network association support</description>
|
||||||
|
</extension>
|
||||||
<extension alias="os-quota-class-sets" updated="2012-03-12T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
|
<extension alias="os-quota-class-sets" updated="2012-03-12T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
|
||||||
<description>Quota classes management support</description>
|
<description>Quota classes management support</description>
|
||||||
</extension>
|
</extension>
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"associate_host": "testHost"
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<associate_host>testHost</associate_host>
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"disassociate_host": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<disassociate_host/>
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"disassociate_project": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<disassociate_project/>
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"disassociate": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<disassociate/>
|
@ -61,6 +61,7 @@
|
|||||||
"compute_extension:multinic": "",
|
"compute_extension:multinic": "",
|
||||||
"compute_extension:networks": "rule:admin_api",
|
"compute_extension:networks": "rule:admin_api",
|
||||||
"compute_extension:networks:view": "",
|
"compute_extension:networks:view": "",
|
||||||
|
"compute_extension:networks_associate": "rule:admin_api",
|
||||||
"compute_extension:quotas:show": "",
|
"compute_extension:quotas:show": "",
|
||||||
"compute_extension:quotas:update": "rule:admin_api",
|
"compute_extension:quotas:update": "rule:admin_api",
|
||||||
"compute_extension:quota_classes": "",
|
"compute_extension:quota_classes": "",
|
||||||
|
@ -21,6 +21,8 @@ import webob
|
|||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
|
from nova.api.openstack import wsgi
|
||||||
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import network
|
from nova import network
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
@ -52,35 +54,11 @@ def network_dict(context, network):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class NetworkController(object):
|
class NetworkController(wsgi.Controller):
|
||||||
|
|
||||||
def __init__(self, network_api=None):
|
def __init__(self, network_api=None):
|
||||||
self.network_api = network_api or network.API()
|
self.network_api = network_api or network.API()
|
||||||
|
|
||||||
def action(self, req, id, body):
|
|
||||||
_actions = {
|
|
||||||
'disassociate': self._disassociate,
|
|
||||||
}
|
|
||||||
|
|
||||||
for action, data in body.iteritems():
|
|
||||||
try:
|
|
||||||
return _actions[action](req, id, body)
|
|
||||||
except KeyError:
|
|
||||||
msg = _("Network does not have %s action") % action
|
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
raise exc.HTTPBadRequest(explanation=_("Invalid request body"))
|
|
||||||
|
|
||||||
def _disassociate(self, request, network_id, body):
|
|
||||||
context = request.environ['nova.context']
|
|
||||||
authorize(context)
|
|
||||||
LOG.debug(_("Disassociating network with id %s"), network_id)
|
|
||||||
try:
|
|
||||||
self.network_api.disassociate(context, network_id)
|
|
||||||
except exception.NetworkNotFound:
|
|
||||||
raise exc.HTTPNotFound(_("Network not found"))
|
|
||||||
return exc.HTTPAccepted()
|
|
||||||
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize_view(context)
|
authorize_view(context)
|
||||||
@ -88,6 +66,18 @@ class NetworkController(object):
|
|||||||
result = [network_dict(context, net_ref) for net_ref in networks]
|
result = [network_dict(context, net_ref) for net_ref in networks]
|
||||||
return {'networks': result}
|
return {'networks': result}
|
||||||
|
|
||||||
|
@wsgi.action("disassociate")
|
||||||
|
def _disassociate_host_and_project(self, req, id, body):
|
||||||
|
context = req.environ['nova.context']
|
||||||
|
authorize(context)
|
||||||
|
LOG.debug(_("Disassociating network with id %s"), id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.network_api.associate(context, id, host=None, project=None)
|
||||||
|
except exception.NetworkNotFound:
|
||||||
|
raise exc.HTTPNotFound(_("Network not found"))
|
||||||
|
return exc.HTTPAccepted()
|
||||||
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize_view(context)
|
authorize_view(context)
|
||||||
|
69
nova/api/openstack/compute/contrib/networks_associate.py
Normal file
69
nova/api/openstack/compute/contrib/networks_associate.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import netaddr
|
||||||
|
import webob
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from nova.api.openstack import extensions
|
||||||
|
from nova.api.openstack import wsgi
|
||||||
|
from nova import exception
|
||||||
|
from nova import network
|
||||||
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
authorize = extensions.extension_authorizer('compute', 'networks_associate')
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkAssociateActionController(wsgi.Controller):
|
||||||
|
"""Network Association API Controller."""
|
||||||
|
|
||||||
|
def __init__(self, network_api=None):
|
||||||
|
self.network_api = network_api or network.API()
|
||||||
|
|
||||||
|
@wsgi.action("disassociate_host")
|
||||||
|
def _disassociate_host_only(self, req, id, body):
|
||||||
|
context = req.environ['nova.context']
|
||||||
|
authorize(context)
|
||||||
|
LOG.debug(_("Disassociating host with network with id %s"), id)
|
||||||
|
try:
|
||||||
|
self.network_api.associate(context, id, host=None)
|
||||||
|
except exception.NetworkNotFound:
|
||||||
|
raise exc.HTTPNotFound(_("Network not found"))
|
||||||
|
return exc.HTTPAccepted()
|
||||||
|
|
||||||
|
@wsgi.action("disassociate_project")
|
||||||
|
def _disassociate_project_only(self, req, id, body):
|
||||||
|
context = req.environ['nova.context']
|
||||||
|
authorize(context)
|
||||||
|
LOG.debug(_("Disassociating project with network with id %s"), id)
|
||||||
|
try:
|
||||||
|
self.network_api.associate(context, id, project=None)
|
||||||
|
except exception.NetworkNotFound:
|
||||||
|
raise exc.HTTPNotFound(_("Network not found"))
|
||||||
|
return exc.HTTPAccepted()
|
||||||
|
|
||||||
|
@wsgi.action("associate_host")
|
||||||
|
def _associate_host(self, req, id, body):
|
||||||
|
context = req.environ['nova.context']
|
||||||
|
authorize(context)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.network_api.associate(context, id,
|
||||||
|
host=body['associate_host'])
|
||||||
|
except exception.NetworkNotFound:
|
||||||
|
raise exc.HTTPNotFound(_("Network not found"))
|
||||||
|
return exc.HTTPAccepted()
|
||||||
|
|
||||||
|
|
||||||
|
class Networks_associate(extensions.ExtensionDescriptor):
|
||||||
|
"""Network association support"""
|
||||||
|
|
||||||
|
name = "NetworkAssociationSupport"
|
||||||
|
alias = "os-networks-associate"
|
||||||
|
namespace = ("http://docs.openstack.org/compute/ext/"
|
||||||
|
"networks_associate/api/v2")
|
||||||
|
updated = "2012-11-19T00:00:00+00:00"
|
||||||
|
|
||||||
|
def get_controller_extensions(self):
|
||||||
|
extension = extensions.ControllerExtension(
|
||||||
|
self, 'os-networks', NetworkAssociateActionController())
|
||||||
|
|
||||||
|
return [extension]
|
@ -785,9 +785,12 @@ def network_delete_safe(context, network_id):
|
|||||||
return IMPL.network_delete_safe(context, network_id)
|
return IMPL.network_delete_safe(context, network_id)
|
||||||
|
|
||||||
|
|
||||||
def network_disassociate(context, network_id):
|
def network_disassociate(context, network_id, disassociate_host=True,
|
||||||
"""Disassociate the network from project or raise if it does not exist."""
|
disassociate_project=True):
|
||||||
return IMPL.network_disassociate(context, network_id)
|
"""Disassociate the network from project or host and raise if it does
|
||||||
|
not exist."""
|
||||||
|
return IMPL.network_disassociate(context, network_id, disassociate_host,
|
||||||
|
disassociate_project)
|
||||||
|
|
||||||
|
|
||||||
def network_get(context, network_id, project_only="allow_none"):
|
def network_get(context, network_id, project_only="allow_none"):
|
||||||
|
@ -2135,9 +2135,14 @@ def network_delete_safe(context, network_id):
|
|||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
@require_admin_context
|
||||||
def network_disassociate(context, network_id):
|
def network_disassociate(context, network_id, disassociate_host,
|
||||||
network_update(context, network_id, {'project_id': None,
|
disassociate_project):
|
||||||
'host': None})
|
net_update = {}
|
||||||
|
if disassociate_project:
|
||||||
|
net_update['project_id'] = None
|
||||||
|
if disassociate_host:
|
||||||
|
net_update['host'] = None
|
||||||
|
network_update(context, network_id, net_update)
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
|
@ -82,6 +82,8 @@ def update_instance_cache_with_nw_info(api, context, instance,
|
|||||||
class API(base.Base):
|
class API(base.Base):
|
||||||
"""API for interacting with the network manager."""
|
"""API for interacting with the network manager."""
|
||||||
|
|
||||||
|
_sentinel = object()
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.network_rpcapi = network_rpcapi.NetworkAPI()
|
self.network_rpcapi = network_rpcapi.NetworkAPI()
|
||||||
super(API, self).__init__(**kwargs)
|
super(API, self).__init__(**kwargs)
|
||||||
@ -232,6 +234,16 @@ class API(base.Base):
|
|||||||
self.network_rpcapi.add_network_to_project(context, project_id,
|
self.network_rpcapi.add_network_to_project(context, project_id,
|
||||||
network_uuid)
|
network_uuid)
|
||||||
|
|
||||||
|
def associate(self, context, network_uuid, host=_sentinel,
|
||||||
|
project=_sentinel):
|
||||||
|
"""Associate or disassociate host or project to network"""
|
||||||
|
associations = {}
|
||||||
|
if host is not API._sentinel:
|
||||||
|
associations['host'] = host
|
||||||
|
if project is not API._sentinel:
|
||||||
|
associations['project'] = project
|
||||||
|
self.network_rpcapi.associate(context, network_uuid, associations)
|
||||||
|
|
||||||
@refresh_cache
|
@refresh_cache
|
||||||
def get_instance_nw_info(self, context, instance):
|
def get_instance_nw_info(self, context, instance):
|
||||||
"""Returns all network info related to an instance."""
|
"""Returns all network info related to an instance."""
|
||||||
|
@ -887,7 +887,7 @@ class NetworkManager(manager.SchedulerDependentManager):
|
|||||||
The one at a time part is to flatten the layout to help scale
|
The one at a time part is to flatten the layout to help scale
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RPC_API_VERSION = '1.4'
|
RPC_API_VERSION = '1.5'
|
||||||
|
|
||||||
# If True, this manager requires VIF to create a bridge.
|
# If True, this manager requires VIF to create a bridge.
|
||||||
SHOULD_CREATE_BRIDGE = False
|
SHOULD_CREATE_BRIDGE = False
|
||||||
@ -2210,6 +2210,27 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
|
|||||||
network_id = None
|
network_id = None
|
||||||
self.db.network_associate(context, project_id, network_id, force=True)
|
self.db.network_associate(context, project_id, network_id, force=True)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
|
def associate(self, context, network_uuid, associations):
|
||||||
|
"""Associate or disassociate host or project to network."""
|
||||||
|
network_id = self.get_network(context, network_uuid)['id']
|
||||||
|
if 'host' in associations:
|
||||||
|
host = associations['host']
|
||||||
|
if host is None:
|
||||||
|
self.db.network_disassociate(context, network_id,
|
||||||
|
disassociate_host=True,
|
||||||
|
disassociate_project=False)
|
||||||
|
else:
|
||||||
|
self.db.network_set_host(context, network_id, host)
|
||||||
|
if 'project' in associations:
|
||||||
|
project = associations['project']
|
||||||
|
if project is None:
|
||||||
|
self.db.network_disassociate(context, network_id,
|
||||||
|
disassociate_host=False,
|
||||||
|
disassociate_project=True)
|
||||||
|
else:
|
||||||
|
self.db.network_associate(context, project, network_id, True)
|
||||||
|
|
||||||
def _get_network_by_id(self, context, network_id):
|
def _get_network_by_id(self, context, network_id):
|
||||||
# NOTE(vish): Don't allow access to networks with project_id=None as
|
# NOTE(vish): Don't allow access to networks with project_id=None as
|
||||||
# these are networksa that haven't been allocated to a
|
# these are networksa that haven't been allocated to a
|
||||||
|
@ -37,6 +37,7 @@ class NetworkAPI(rpc_proxy.RpcProxy):
|
|||||||
1.2 - Make migrate_instance_[start|finish] a little more flexible
|
1.2 - Make migrate_instance_[start|finish] a little more flexible
|
||||||
1.3 - Adds fanout cast update_dns for multi_host networks
|
1.3 - Adds fanout cast update_dns for multi_host networks
|
||||||
1.4 - Add get_backdoor_port()
|
1.4 - Add get_backdoor_port()
|
||||||
|
1.5 - Adds associate
|
||||||
'''
|
'''
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -163,6 +164,11 @@ class NetworkAPI(rpc_proxy.RpcProxy):
|
|||||||
return self.call(ctxt, self.make_msg('add_network_to_project',
|
return self.call(ctxt, self.make_msg('add_network_to_project',
|
||||||
project_id=project_id, network_uuid=network_uuid))
|
project_id=project_id, network_uuid=network_uuid))
|
||||||
|
|
||||||
|
def associate(self, ctxt, network_uuid, associations):
|
||||||
|
return self.call(ctxt, self.make_msg('associate',
|
||||||
|
network_uuid=network_uuid, associations=associations),
|
||||||
|
self.topic, version="1.5")
|
||||||
|
|
||||||
def get_instance_nw_info(self, ctxt, instance_id, instance_uuid,
|
def get_instance_nw_info(self, ctxt, instance_id, instance_uuid,
|
||||||
rxtx_factor, host, project_id):
|
rxtx_factor, host, project_id):
|
||||||
return self.call(ctxt, self.make_msg('get_instance_nw_info',
|
return self.call(ctxt, self.make_msg('get_instance_nw_info',
|
||||||
|
@ -23,6 +23,9 @@ import uuid
|
|||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute.contrib import networks
|
from nova.api.openstack.compute.contrib import networks
|
||||||
|
from nova.api.openstack.compute.contrib import networks_associate
|
||||||
|
from nova import config
|
||||||
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import cfg
|
from nova.openstack.common import cfg
|
||||||
from nova import test
|
from nova import test
|
||||||
@ -93,6 +96,8 @@ NEW_NETWORK = {
|
|||||||
|
|
||||||
class FakeNetworkAPI(object):
|
class FakeNetworkAPI(object):
|
||||||
|
|
||||||
|
_sentinel = object()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.networks = copy.deepcopy(FAKE_NETWORKS)
|
self.networks = copy.deepcopy(FAKE_NETWORKS)
|
||||||
|
|
||||||
@ -110,6 +115,17 @@ class FakeNetworkAPI(object):
|
|||||||
return True
|
return True
|
||||||
raise exception.NetworkNotFound()
|
raise exception.NetworkNotFound()
|
||||||
|
|
||||||
|
def associate(self, context, network_uuid, host=_sentinel,
|
||||||
|
project=_sentinel):
|
||||||
|
for network in self.networks:
|
||||||
|
if network.get('uuid') == network_uuid:
|
||||||
|
if host is not FakeNetworkAPI._sentinel:
|
||||||
|
network['host'] = host
|
||||||
|
if project is not FakeNetworkAPI._sentinel:
|
||||||
|
network['project_id'] = project
|
||||||
|
return True
|
||||||
|
raise exception.NetworkNotFound()
|
||||||
|
|
||||||
def add_network_to_project(self, context,
|
def add_network_to_project(self, context,
|
||||||
project_id, network_uuid=None):
|
project_id, network_uuid=None):
|
||||||
if network_uuid:
|
if network_uuid:
|
||||||
@ -165,6 +181,8 @@ class NetworksTest(test.TestCase):
|
|||||||
super(NetworksTest, self).setUp()
|
super(NetworksTest, self).setUp()
|
||||||
self.fake_network_api = FakeNetworkAPI()
|
self.fake_network_api = FakeNetworkAPI()
|
||||||
self.controller = networks.NetworkController(self.fake_network_api)
|
self.controller = networks.NetworkController(self.fake_network_api)
|
||||||
|
self.associate_controller = networks_associate\
|
||||||
|
.NetworkAssociateActionController(self.fake_network_api)
|
||||||
fakes.stub_out_networking(self.stubs)
|
fakes.stub_out_networking(self.stubs)
|
||||||
fakes.stub_out_rate_limiting(self.stubs)
|
fakes.stub_out_rate_limiting(self.stubs)
|
||||||
|
|
||||||
@ -194,13 +212,35 @@ class NetworksTest(test.TestCase):
|
|||||||
def test_network_disassociate(self):
|
def test_network_disassociate(self):
|
||||||
uuid = FAKE_NETWORKS[0]['uuid']
|
uuid = FAKE_NETWORKS[0]['uuid']
|
||||||
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s/action' % uuid)
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s/action' % uuid)
|
||||||
res = self.controller.action(req, uuid, {'disassociate': None})
|
res = self.controller._disassociate_host_and_project(
|
||||||
|
req, uuid, {'disassociate': None})
|
||||||
self.assertEqual(res.status_int, 202)
|
self.assertEqual(res.status_int, 202)
|
||||||
|
self.assertEqual(self.fake_network_api.networks[0]['project_id'], None)
|
||||||
|
self.assertEqual(self.fake_network_api.networks[0]['host'], None)
|
||||||
|
|
||||||
|
def test_network_disassociate_host_only(self):
|
||||||
|
uuid = FAKE_NETWORKS[0]['uuid']
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s/action' % uuid)
|
||||||
|
res = self.associate_controller._disassociate_host_only(
|
||||||
|
req, uuid, {'disassociate_host': None})
|
||||||
|
self.assertEqual(res.status_int, 202)
|
||||||
|
self.assertNotEqual(self.fake_network_api.networks[0]['project_id'],
|
||||||
|
None)
|
||||||
|
self.assertEqual(self.fake_network_api.networks[0]['host'], None)
|
||||||
|
|
||||||
|
def test_network_disassociate_project_only(self):
|
||||||
|
uuid = FAKE_NETWORKS[0]['uuid']
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s/action' % uuid)
|
||||||
|
res = self.associate_controller._disassociate_project_only(
|
||||||
|
req, uuid, {'disassociate_project': None})
|
||||||
|
self.assertEqual(res.status_int, 202)
|
||||||
|
self.assertEqual(self.fake_network_api.networks[0]['project_id'], None)
|
||||||
|
self.assertNotEqual(self.fake_network_api.networks[0]['host'], None)
|
||||||
|
|
||||||
def test_network_disassociate_not_found(self):
|
def test_network_disassociate_not_found(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/100/action')
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/100/action')
|
||||||
self.assertRaises(webob.exc.HTTPNotFound,
|
self.assertRaises(webob.exc.HTTPNotFound,
|
||||||
self.controller.action,
|
self.controller._disassociate_host_and_project,
|
||||||
req, 100, {'disassociate': None})
|
req, 100, {'disassociate': None})
|
||||||
|
|
||||||
def test_network_get_as_user(self):
|
def test_network_get_as_user(self):
|
||||||
@ -246,6 +286,17 @@ class NetworksTest(test.TestCase):
|
|||||||
res_dict = self.controller.show(req, uuid)
|
res_dict = self.controller.show(req, uuid)
|
||||||
self.assertEqual(res_dict['network']['project_id'], 'fake')
|
self.assertEqual(res_dict['network']['project_id'], 'fake')
|
||||||
|
|
||||||
|
def test_network_associate_with_host(self):
|
||||||
|
uuid = FAKE_NETWORKS[1]['uuid']
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s/action' % uuid)
|
||||||
|
res = self.associate_controller._associate_host(
|
||||||
|
req, uuid, {'associate_host': "TestHost"})
|
||||||
|
self.assertEqual(res.status_int, 202)
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s' % uuid)
|
||||||
|
req.environ["nova.context"].is_admin = True
|
||||||
|
res_dict = self.controller.show(req, uuid)
|
||||||
|
self.assertEqual(res_dict['network']['host'], 'TestHost')
|
||||||
|
|
||||||
def test_network_create(self):
|
def test_network_create(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/1234/os-networks')
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks')
|
||||||
res_dict = self.controller.create(req, NEW_NETWORK)
|
res_dict = self.controller.create(req, NEW_NETWORK)
|
||||||
|
@ -137,6 +137,7 @@ policy_data = """
|
|||||||
"compute_extension:multinic": "",
|
"compute_extension:multinic": "",
|
||||||
"compute_extension:networks": "",
|
"compute_extension:networks": "",
|
||||||
"compute_extension:networks:view": "",
|
"compute_extension:networks:view": "",
|
||||||
|
"compute_extension:networks_associate": "",
|
||||||
"compute_extension:quotas:show": "",
|
"compute_extension:quotas:show": "",
|
||||||
"compute_extension:quotas:update": "",
|
"compute_extension:quotas:update": "",
|
||||||
"compute_extension:quota_classes": "",
|
"compute_extension:quota_classes": "",
|
||||||
|
@ -304,6 +304,14 @@
|
|||||||
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
||||||
"updated": "%(timestamp)s"
|
"updated": "%(timestamp)s"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": "os-networks-associate",
|
||||||
|
"description": "%(text)s",
|
||||||
|
"links": [],
|
||||||
|
"name": "NetworkAssociationSupport",
|
||||||
|
"namespace": "http://docs.openstack.org/compute/ext/networks_associate/api/v2",
|
||||||
|
"updated": "%(timestamp)s"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": "os-quota-class-sets",
|
"alias": "os-quota-class-sets",
|
||||||
"description": "%(text)s",
|
"description": "%(text)s",
|
||||||
|
@ -114,6 +114,9 @@
|
|||||||
<extension alias="os-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks/api/v1.1" name="Networks">
|
<extension alias="os-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks/api/v1.1" name="Networks">
|
||||||
<description>%(text)s</description>
|
<description>%(text)s</description>
|
||||||
</extension>
|
</extension>
|
||||||
|
<extension alias="os-networks-associate" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport">
|
||||||
|
<description>%(text)s</description>
|
||||||
|
</extension>
|
||||||
<extension alias="os-quota-class-sets" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
|
<extension alias="os-quota-class-sets" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
|
||||||
<description>%(text)s</description>
|
<description>%(text)s</description>
|
||||||
</extension>
|
</extension>
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"associate_host": "%(host)s"
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<associate_host>%(host)s</associate_host>
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"disassociate_host": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<disassociate_host/>
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"disassociate_project": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<disassociate_project/>
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"disassociate": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<disassociate/>
|
@ -30,6 +30,7 @@ from nova.compute import api
|
|||||||
from nova import context
|
from nova import context
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova.db.sqlalchemy import models
|
from nova.db.sqlalchemy import models
|
||||||
|
from nova.network import api
|
||||||
from nova.network.manager import NetworkManager
|
from nova.network.manager import NetworkManager
|
||||||
from nova.openstack.common import cfg
|
from nova.openstack.common import cfg
|
||||||
from nova.openstack.common import importutils
|
from nova.openstack.common import importutils
|
||||||
@ -2048,3 +2049,56 @@ class DiskConfigJsonTest(ServersSampleBase):
|
|||||||
|
|
||||||
class DiskConfigXmlTest(DiskConfigJsonTest):
|
class DiskConfigXmlTest(DiskConfigJsonTest):
|
||||||
ctype = 'xml'
|
ctype = 'xml'
|
||||||
|
|
||||||
|
|
||||||
|
class NetworksAssociateJsonTests(ApiSampleTestBase):
|
||||||
|
extension_name = ("nova.api.openstack.compute.contrib"
|
||||||
|
".networks_associate.Networks_associate")
|
||||||
|
|
||||||
|
_sentinel = object()
|
||||||
|
|
||||||
|
def _get_flags(self):
|
||||||
|
f = super(NetworksAssociateJsonTests, self)._get_flags()
|
||||||
|
f['osapi_compute_extension'] = CONF.osapi_compute_extension[:]
|
||||||
|
# Networks_associate requires Networks to be update
|
||||||
|
f['osapi_compute_extension'].append(
|
||||||
|
'nova.api.openstack.compute.contrib.networks.Networks')
|
||||||
|
return f
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(NetworksAssociateJsonTests, self).setUp()
|
||||||
|
|
||||||
|
def fake_associate(self, context, network_id,
|
||||||
|
host=NetworksAssociateJsonTests._sentinel,
|
||||||
|
project=NetworksAssociateJsonTests._sentinel):
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.stubs.Set(api.API, "associate", fake_associate)
|
||||||
|
|
||||||
|
def test_disassociate(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-disassociate-req',
|
||||||
|
{})
|
||||||
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
|
def test_disassociate_host(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-disassociate-host-req',
|
||||||
|
{})
|
||||||
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
|
def test_disassociate_project(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-disassociate-project-req',
|
||||||
|
{})
|
||||||
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
|
def test_associate_host(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-associate-host-req',
|
||||||
|
{"host": "testHost"})
|
||||||
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworksAssociateXmlTests(NetworksAssociateJsonTests):
|
||||||
|
ctype = 'xml'
|
||||||
|
@ -92,6 +92,13 @@ class NetworkRpcAPITestCase(test.TestCase):
|
|||||||
self._test_network_api('disassociate_network', rpc_method='call',
|
self._test_network_api('disassociate_network', rpc_method='call',
|
||||||
network_uuid='fake_uuid')
|
network_uuid='fake_uuid')
|
||||||
|
|
||||||
|
def test_associate_host_and_project(self):
|
||||||
|
self._test_network_api('associate', rpc_method='call',
|
||||||
|
network_uuid='fake_uuid',
|
||||||
|
associations={'host': "testHost",
|
||||||
|
'project': 'testProject'},
|
||||||
|
version="1.5")
|
||||||
|
|
||||||
def test_get_fixed_ip(self):
|
def test_get_fixed_ip(self):
|
||||||
self._test_network_api('get_fixed_ip', rpc_method='call', id='id')
|
self._test_network_api('get_fixed_ip', rpc_method='call', id='id')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user