Port os-networks-associate plugin to v2.1(v3) infrastructure
Ports os-networks-associate extension and adapts it to the v2.1/v3 API framework. API behaviour is identical. - unittest code modified to share testing with both v2/v2.1 - Adds expected error decorators for API methods Partially implements blueprint v2-on-v3-api Change-Id: I6ccf9613abf999784c548fb058522ac60db7f052
This commit is contained in:
parent
cc452485c2
commit
0c1ad907c4
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"associate_host": "testHost"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"disassociate_host": null
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"disassociate_project": null
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"disassociate": null
|
||||||
|
}
|
|
@ -210,6 +210,8 @@
|
||||||
"compute_extension:v3:os-networks:view": "",
|
"compute_extension:v3:os-networks:view": "",
|
||||||
"compute_extension:v3:os-networks:discoverable": "",
|
"compute_extension:v3:os-networks:discoverable": "",
|
||||||
"compute_extension:networks_associate": "rule:admin_api",
|
"compute_extension:networks_associate": "rule:admin_api",
|
||||||
|
"compute_extension:v3:os-networks-associate": "rule:admin_api",
|
||||||
|
"compute_extension:v3:os-networks-associate:discoverable": "",
|
||||||
"compute_extension:v3:os-pause-server:discoverable": "",
|
"compute_extension:v3:os-pause-server:discoverable": "",
|
||||||
"compute_extension:v3:os-pause-server:pause": "rule:admin_or_owner",
|
"compute_extension:v3:os-pause-server:pause": "rule:admin_or_owner",
|
||||||
"compute_extension:v3:os-pause-server:unpause": "rule:admin_or_owner",
|
"compute_extension:v3:os-pause-server:unpause": "rule:admin_or_owner",
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from nova.api.openstack import extensions
|
||||||
|
from nova.api.openstack import wsgi
|
||||||
|
from nova import exception
|
||||||
|
from nova.i18n import _
|
||||||
|
from nova import network
|
||||||
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
ALIAS = "os-networks-associate"
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
@wsgi.response(202)
|
||||||
|
@extensions.expected_errors((404, 501))
|
||||||
|
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:
|
||||||
|
msg = _("Network not found")
|
||||||
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
|
except NotImplementedError:
|
||||||
|
msg = _('Disassociate host is not implemented by the configured '
|
||||||
|
'Network API')
|
||||||
|
raise exc.HTTPNotImplemented(explanation=msg)
|
||||||
|
|
||||||
|
@wsgi.action("disassociate_project")
|
||||||
|
@wsgi.response(202)
|
||||||
|
@extensions.expected_errors((404, 501))
|
||||||
|
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:
|
||||||
|
msg = _("Network not found")
|
||||||
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
|
except NotImplementedError:
|
||||||
|
msg = _('Disassociate project is not implemented by the '
|
||||||
|
'configured Network API')
|
||||||
|
raise exc.HTTPNotImplemented(explanation=msg)
|
||||||
|
|
||||||
|
@wsgi.action("associate_host")
|
||||||
|
@wsgi.response(202)
|
||||||
|
@extensions.expected_errors((404, 501))
|
||||||
|
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:
|
||||||
|
msg = _("Network not found")
|
||||||
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
|
except NotImplementedError:
|
||||||
|
msg = _('Associate host is not implemented by the configured '
|
||||||
|
'Network API')
|
||||||
|
raise exc.HTTPNotImplemented(explanation=msg)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworksAssociate(extensions.V3APIExtensionBase):
|
||||||
|
"""Network association support."""
|
||||||
|
|
||||||
|
name = "NetworkAssociationSupport"
|
||||||
|
alias = ALIAS
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
def get_controller_extensions(self):
|
||||||
|
extension = extensions.ControllerExtension(
|
||||||
|
self, 'os-networks', NetworkAssociateActionController())
|
||||||
|
|
||||||
|
return [extension]
|
||||||
|
|
||||||
|
def get_resources(self):
|
||||||
|
return []
|
|
@ -29,6 +29,8 @@ from nova.api.openstack.compute.contrib import networks_associate
|
||||||
from nova.api.openstack.compute.contrib import os_networks as networks
|
from nova.api.openstack.compute.contrib import os_networks as networks
|
||||||
from nova.api.openstack.compute.contrib import os_tenant_networks as tnet
|
from nova.api.openstack.compute.contrib import os_tenant_networks as tnet
|
||||||
from nova.api.openstack.compute.plugins.v3 import networks as networks_v21
|
from nova.api.openstack.compute.plugins.v3 import networks as networks_v21
|
||||||
|
from nova.api.openstack.compute.plugins.v3 import networks_associate as \
|
||||||
|
networks_associate_v21
|
||||||
from nova.api.openstack.compute.plugins.v3 import tenant_networks as tnet_v21
|
from nova.api.openstack.compute.plugins.v3 import tenant_networks as tnet_v21
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
import nova.context
|
import nova.context
|
||||||
|
@ -489,27 +491,31 @@ class NetworksTestV2(NetworksTestV21):
|
||||||
self.controller.create(req, net)
|
self.controller.create(req, net)
|
||||||
|
|
||||||
|
|
||||||
class NetworksAssociateTest(test.NoDBTestCase):
|
class NetworksAssociateTestV21(test.NoDBTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(NetworksAssociateTest, self).setUp()
|
super(NetworksAssociateTestV21, self).setUp()
|
||||||
self.fake_network_api = FakeNetworkAPI()
|
self.fake_network_api = FakeNetworkAPI()
|
||||||
ext_mgr = extensions.ExtensionManager()
|
self._setup()
|
||||||
ext_mgr.extensions = {'os-extended-networks': 'fake'}
|
|
||||||
self.controller = networks.NetworkController(
|
|
||||||
self.fake_network_api,
|
|
||||||
ext_mgr)
|
|
||||||
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)
|
||||||
|
|
||||||
|
def _setup(self):
|
||||||
|
self.controller = networks.NetworkController(self.fake_network_api)
|
||||||
|
self.associate_controller = networks_associate_v21\
|
||||||
|
.NetworkAssociateActionController(self.fake_network_api)
|
||||||
|
|
||||||
|
def _check_status(self, res, method, code):
|
||||||
|
self.assertEqual(method.wsgi_code, code)
|
||||||
|
|
||||||
def test_network_disassociate_host_only(self):
|
def test_network_disassociate_host_only(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.associate_controller._disassociate_host_only(
|
res = self.associate_controller._disassociate_host_only(
|
||||||
req, uuid, {'disassociate_host': None})
|
req, uuid, {'disassociate_host': None})
|
||||||
self.assertEqual(res.status_int, 202)
|
self._check_status(res,
|
||||||
|
self.associate_controller._disassociate_host_only,
|
||||||
|
202)
|
||||||
self.assertIsNotNone(self.fake_network_api.networks[0]['project_id'])
|
self.assertIsNotNone(self.fake_network_api.networks[0]['project_id'])
|
||||||
self.assertIsNone(self.fake_network_api.networks[0]['host'])
|
self.assertIsNone(self.fake_network_api.networks[0]['host'])
|
||||||
|
|
||||||
|
@ -518,16 +524,17 @@ class NetworksAssociateTest(test.NoDBTestCase):
|
||||||
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s/action' % uuid)
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s/action' % uuid)
|
||||||
res = self.associate_controller._disassociate_project_only(
|
res = self.associate_controller._disassociate_project_only(
|
||||||
req, uuid, {'disassociate_project': None})
|
req, uuid, {'disassociate_project': None})
|
||||||
self.assertEqual(res.status_int, 202)
|
self._check_status(
|
||||||
|
res, self.associate_controller._disassociate_project_only, 202)
|
||||||
self.assertIsNone(self.fake_network_api.networks[0]['project_id'])
|
self.assertIsNone(self.fake_network_api.networks[0]['project_id'])
|
||||||
self.assertIsNotNone(self.fake_network_api.networks[0]['host'])
|
self.assertIsNotNone(self.fake_network_api.networks[0]['host'])
|
||||||
|
|
||||||
def test_network_associate_with_host(self):
|
def test_network_associate_with_host(self):
|
||||||
uuid = FAKE_NETWORKS[1]['uuid']
|
uuid = FAKE_NETWORKS[1]['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.associate_controller._associate_host(
|
res = self.associate_controller._associate_host(
|
||||||
req, uuid, {'associate_host': "TestHost"})
|
req, uuid, {'associate_host': "TestHost"})
|
||||||
self.assertEqual(res.status_int, 202)
|
self._check_status(res, self.associate_controller._associate_host, 202)
|
||||||
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s' % uuid)
|
req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s' % uuid)
|
||||||
req.environ["nova.context"].is_admin = True
|
req.environ["nova.context"].is_admin = True
|
||||||
res_dict = self.controller.show(req, uuid)
|
res_dict = self.controller.show(req, uuid)
|
||||||
|
@ -563,6 +570,21 @@ class NetworksAssociateTest(test.NoDBTestCase):
|
||||||
req, uuid, {'disassociate_host': None})
|
req, uuid, {'disassociate_host': None})
|
||||||
|
|
||||||
|
|
||||||
|
class NetworksAssociateTestV2(NetworksAssociateTestV21):
|
||||||
|
|
||||||
|
def _setup(self):
|
||||||
|
ext_mgr = extensions.ExtensionManager()
|
||||||
|
ext_mgr.extensions = {'os-extended-networks': 'fake'}
|
||||||
|
self.controller = networks.NetworkController(
|
||||||
|
self.fake_network_api,
|
||||||
|
ext_mgr)
|
||||||
|
self.associate_controller = networks_associate\
|
||||||
|
.NetworkAssociateActionController(self.fake_network_api)
|
||||||
|
|
||||||
|
def _check_status(self, res, method, code):
|
||||||
|
self.assertEqual(res.status_int, 202)
|
||||||
|
|
||||||
|
|
||||||
class TenantNetworksTestV21(test.NoDBTestCase):
|
class TenantNetworksTestV21(test.NoDBTestCase):
|
||||||
ctrlr = tnet_v21.TenantNetworkController
|
ctrlr = tnet_v21.TenantNetworkController
|
||||||
|
|
||||||
|
|
|
@ -255,6 +255,7 @@ policy_data = """
|
||||||
"compute_extension:v3:os-networks": "",
|
"compute_extension:v3:os-networks": "",
|
||||||
"compute_extension:v3:os-networks:view": "",
|
"compute_extension:v3:os-networks:view": "",
|
||||||
"compute_extension:networks_associate": "",
|
"compute_extension:networks_associate": "",
|
||||||
|
"compute_extension:v3:os-networks-associate": "",
|
||||||
"compute_extension:os-tenant-networks": "",
|
"compute_extension:os-tenant-networks": "",
|
||||||
"compute_extension:v3:os-tenant-networks": "",
|
"compute_extension:v3:os-tenant-networks": "",
|
||||||
"compute_extension:v3:os-pause-server:pause": "",
|
"compute_extension:v3:os-pause-server:pause": "",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"associate_host": "%(host)s"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"disassociate_host": null
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"disassociate_project": null
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"disassociate": null
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Copyright 2012 Nebula, Inc.
|
||||||
|
# Copyright 2014 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from nova.network import api as network_api
|
||||||
|
from nova.tests.integrated.v3 import api_sample_base
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.import_opt('osapi_compute_extension',
|
||||||
|
'nova.api.openstack.compute.extensions')
|
||||||
|
|
||||||
|
|
||||||
|
class NetworksAssociateJsonTests(api_sample_base.ApiSampleTestBaseV3):
|
||||||
|
extension_name = "os-networks-associate"
|
||||||
|
extra_extensions_to_load = ["os-networks"]
|
||||||
|
|
||||||
|
_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.os_networks.Os_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(network_api.API, "associate", fake_associate)
|
||||||
|
|
||||||
|
def test_disassociate(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-disassociate-req',
|
||||||
|
{})
|
||||||
|
self.assertEqual(response.status_code, 202)
|
||||||
|
self.assertEqual(response.content, "")
|
||||||
|
|
||||||
|
def test_disassociate_host(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-disassociate-host-req',
|
||||||
|
{})
|
||||||
|
self.assertEqual(response.status_code, 202)
|
||||||
|
self.assertEqual(response.content, "")
|
||||||
|
|
||||||
|
def test_disassociate_project(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-disassociate-project-req',
|
||||||
|
{})
|
||||||
|
self.assertEqual(response.status_code, 202)
|
||||||
|
self.assertEqual(response.content, "")
|
||||||
|
|
||||||
|
def test_associate_host(self):
|
||||||
|
response = self._do_post('os-networks/1/action',
|
||||||
|
'network-associate-host-req',
|
||||||
|
{"host": "testHost"})
|
||||||
|
self.assertEqual(response.status_code, 202)
|
||||||
|
self.assertEqual(response.content, "")
|
|
@ -103,6 +103,7 @@ nova.api.v3.extensions =
|
||||||
multinic = nova.api.openstack.compute.plugins.v3.multinic:Multinic
|
multinic = nova.api.openstack.compute.plugins.v3.multinic:Multinic
|
||||||
multiple_create = nova.api.openstack.compute.plugins.v3.multiple_create:MultipleCreate
|
multiple_create = nova.api.openstack.compute.plugins.v3.multiple_create:MultipleCreate
|
||||||
networks = nova.api.openstack.compute.plugins.v3.networks:Networks
|
networks = nova.api.openstack.compute.plugins.v3.networks:Networks
|
||||||
|
networks_associate = nova.api.openstack.compute.plugins.v3.networks_associate:NetworksAssociate
|
||||||
pause_server = nova.api.openstack.compute.plugins.v3.pause_server:PauseServer
|
pause_server = nova.api.openstack.compute.plugins.v3.pause_server:PauseServer
|
||||||
pci = nova.api.openstack.compute.plugins.v3.pci:Pci
|
pci = nova.api.openstack.compute.plugins.v3.pci:Pci
|
||||||
quota_sets = nova.api.openstack.compute.plugins.v3.quota_sets:QuotaSets
|
quota_sets = nova.api.openstack.compute.plugins.v3.quota_sets:QuotaSets
|
||||||
|
|
Loading…
Reference in New Issue