Add more association support to network API
Adds API support to: - Disassociate only host from network - Disassociate only project from network - Associate network with host (project already supported) New functionality is added to a new extension networks_associate, but the original networks extension is modified to allow networks_associate to extend how it needs to. The original behavior of the networks extension is preserved (no new functionality, nor change in existing behavior) in terms of the API presented. Bumps RPC API version for network RPC server version in order add the associate function which allows to both associate and disassociate a network with a host and or tenant. Has overlap with disassociate and add_network_to_project but these are kept for backwards compatibility DocImpact Implmenents: blueprint apis-for-nova-manage Change-Id: I78fd76e0696d1c872646355ab04d32f051551def
This commit is contained in:
parent
f04722ee22
commit
37d42caeab
@ -296,6 +296,14 @@
|
||||
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
||||
"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",
|
||||
"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">
|
||||
<description>Admin-only Network Management Extension</description>
|
||||
</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">
|
||||
<description>Quota classes management support</description>
|
||||
</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:networks": "rule:admin_api",
|
||||
"compute_extension:networks:view": "",
|
||||
"compute_extension:networks_associate": "rule:admin_api",
|
||||
"compute_extension:quotas:show": "",
|
||||
"compute_extension:quotas:update": "rule:admin_api",
|
||||
"compute_extension:quota_classes": "",
|
||||
|
@ -21,6 +21,8 @@ import webob
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import network
|
||||
from nova.openstack.common import log as logging
|
||||
@ -52,35 +54,11 @@ def network_dict(context, network):
|
||||
return {}
|
||||
|
||||
|
||||
class NetworkController(object):
|
||||
class NetworkController(wsgi.Controller):
|
||||
|
||||
def __init__(self, network_api=None):
|
||||
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):
|
||||
context = req.environ['nova.context']
|
||||
authorize_view(context)
|
||||
@ -88,6 +66,18 @@ class NetworkController(object):
|
||||
result = [network_dict(context, net_ref) for net_ref in networks]
|
||||
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):
|
||||
context = req.environ['nova.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)
|
||||
|
||||
|
||||
def network_disassociate(context, network_id):
|
||||
"""Disassociate the network from project or raise if it does not exist."""
|
||||
return IMPL.network_disassociate(context, network_id)
|
||||
def network_disassociate(context, network_id, disassociate_host=True,
|
||||
disassociate_project=True):
|
||||
"""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"):
|
||||
|
@ -2135,9 +2135,14 @@ def network_delete_safe(context, network_id):
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def network_disassociate(context, network_id):
|
||||
network_update(context, network_id, {'project_id': None,
|
||||
'host': None})
|
||||
def network_disassociate(context, network_id, disassociate_host,
|
||||
disassociate_project):
|
||||
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
|
||||
|
@ -82,6 +82,8 @@ def update_instance_cache_with_nw_info(api, context, instance,
|
||||
class API(base.Base):
|
||||
"""API for interacting with the network manager."""
|
||||
|
||||
_sentinel = object()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.network_rpcapi = network_rpcapi.NetworkAPI()
|
||||
super(API, self).__init__(**kwargs)
|
||||
@ -232,6 +234,16 @@ class API(base.Base):
|
||||
self.network_rpcapi.add_network_to_project(context, project_id,
|
||||
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
|
||||
def get_instance_nw_info(self, context, 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
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '1.4'
|
||||
RPC_API_VERSION = '1.5'
|
||||
|
||||
# If True, this manager requires VIF to create a bridge.
|
||||
SHOULD_CREATE_BRIDGE = False
|
||||
@ -2210,6 +2210,27 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
|
||||
network_id = None
|
||||
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):
|
||||
# NOTE(vish): Don't allow access to networks with project_id=None as
|
||||
# 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.3 - Adds fanout cast update_dns for multi_host networks
|
||||
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',
|
||||
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,
|
||||
rxtx_factor, host, project_id):
|
||||
return self.call(ctxt, self.make_msg('get_instance_nw_info',
|
||||
|
@ -23,6 +23,9 @@ import uuid
|
||||
import webob
|
||||
|
||||
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.openstack.common import cfg
|
||||
from nova import test
|
||||
@ -93,6 +96,8 @@ NEW_NETWORK = {
|
||||
|
||||
class FakeNetworkAPI(object):
|
||||
|
||||
_sentinel = object()
|
||||
|
||||
def __init__(self):
|
||||
self.networks = copy.deepcopy(FAKE_NETWORKS)
|
||||
|
||||
@ -110,6 +115,17 @@ class FakeNetworkAPI(object):
|
||||
return True
|
||||
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,
|
||||
project_id, network_uuid=None):
|
||||
if network_uuid:
|
||||
@ -165,6 +181,8 @@ class NetworksTest(test.TestCase):
|
||||
super(NetworksTest, self).setUp()
|
||||
self.fake_network_api = FakeNetworkAPI()
|
||||
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_rate_limiting(self.stubs)
|
||||
|
||||
@ -194,13 +212,35 @@ class NetworksTest(test.TestCase):
|
||||
def test_network_disassociate(self):
|
||||
uuid = FAKE_NETWORKS[0]['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(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):
|
||||
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/100/action')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.action,
|
||||
self.controller._disassociate_host_and_project,
|
||||
req, 100, {'disassociate': None})
|
||||
|
||||
def test_network_get_as_user(self):
|
||||
@ -246,6 +286,17 @@ class NetworksTest(test.TestCase):
|
||||
res_dict = self.controller.show(req, uuid)
|
||||
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):
|
||||
req = fakes.HTTPRequest.blank('/v2/1234/os-networks')
|
||||
res_dict = self.controller.create(req, NEW_NETWORK)
|
||||
|
@ -137,6 +137,7 @@ policy_data = """
|
||||
"compute_extension:multinic": "",
|
||||
"compute_extension:networks": "",
|
||||
"compute_extension:networks:view": "",
|
||||
"compute_extension:networks_associate": "",
|
||||
"compute_extension:quotas:show": "",
|
||||
"compute_extension:quotas:update": "",
|
||||
"compute_extension:quota_classes": "",
|
||||
|
@ -304,6 +304,14 @@
|
||||
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
||||
"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",
|
||||
"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">
|
||||
<description>%(text)s</description>
|
||||
</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">
|
||||
<description>%(text)s</description>
|
||||
</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 db
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova.network import api
|
||||
from nova.network.manager import NetworkManager
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import importutils
|
||||
@ -2048,3 +2049,56 @@ class DiskConfigJsonTest(ServersSampleBase):
|
||||
|
||||
class DiskConfigXmlTest(DiskConfigJsonTest):
|
||||
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',
|
||||
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):
|
||||
self._test_network_api('get_fixed_ip', rpc_method='call', id='id')
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user