Remove '/os-tenant-networks' REST API
Like '/os-networks', we can't remove this in its entirety due to the fact that some of these are proxy URLs that also work with neutron. These are retained but everything else is nuked. Note that this highlights a bug in the API, whereby a missing 'objects.Network.cidr' value results in a value of 'None' being output over the API. Clearly this is incorrect, but it's probably not worth fixing for this deprecated API. Change-Id: I31cb0891144bdd7945479bb6692b0a533de4c5d0 Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
parent
991d675675
commit
e5e477c42a
@ -1,10 +1,8 @@
|
|||||||
.. -*- rst -*-
|
.. -*- rst -*-
|
||||||
.. NOTE(gmann): These APIs are deprecated so do not update this
|
|
||||||
file even body, example or parameters are not complete.
|
|
||||||
|
|
||||||
===================================================
|
====================================================
|
||||||
Project networks (os-tenant-networks) (DEPRECATED)
|
Project networks (os-tenant-networks) (DEPRECATED)
|
||||||
===================================================
|
====================================================
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
@ -57,7 +55,8 @@ through the ``policy.json`` file.
|
|||||||
|
|
||||||
Normal response codes: 200
|
Normal response codes: 200
|
||||||
|
|
||||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409), serviceUnavailable(503)
|
Error response codes: badRequest(400), unauthorized(401), forbidden(403),
|
||||||
|
conflict(409), gone(410), serviceUnavailable(503)
|
||||||
|
|
||||||
**Example Create Project Network: JSON request**
|
**Example Create Project Network: JSON request**
|
||||||
|
|
||||||
@ -121,7 +120,8 @@ can change these permissions through the ``policy.json`` file.
|
|||||||
|
|
||||||
Normal response codes: 202
|
Normal response codes: 202
|
||||||
|
|
||||||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), conflict(409)
|
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404),
|
||||||
|
conflict(409), gone(410)
|
||||||
|
|
||||||
Request
|
Request
|
||||||
-------
|
-------
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
{
|
{
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
"cidr": "10.0.0.0/29",
|
"cidr": "None",
|
||||||
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
|
"id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6",
|
||||||
"label": "test_0"
|
"label": "private"
|
||||||
},
|
|
||||||
{
|
|
||||||
"cidr": "10.0.0.8/29",
|
|
||||||
"id": "616fb98f-46ca-475e-917e-2563e5a8cd20",
|
|
||||||
"label": "test_1"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -441,6 +441,12 @@ API endpoints as below::
|
|||||||
release. On deployments newer than this, some endpoints of the API will
|
release. On deployments newer than this, some endpoints of the API will
|
||||||
return HTTP 410 (Gone) regadless of the requested microversion.
|
return HTTP 410 (Gone) regadless of the requested microversion.
|
||||||
|
|
||||||
|
.. versionchanged:: 21.0.0
|
||||||
|
|
||||||
|
The ``os-tenant-networks`` API was partially removed in the 21.0.0 (Ussuri)
|
||||||
|
release. On deployments newer than this, some endpoints of the API will
|
||||||
|
return HTTP 410 (Gone) regadless of the requested microversion.
|
||||||
|
|
||||||
2.37
|
2.37
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
# Copyright 2015 NEC Corporation. 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.
|
|
||||||
|
|
||||||
from nova.api.validation import parameter_types
|
|
||||||
|
|
||||||
create = {
|
|
||||||
'type': 'object',
|
|
||||||
'properties': {
|
|
||||||
'network': {
|
|
||||||
'type': 'object',
|
|
||||||
'properties': {
|
|
||||||
'label': {
|
|
||||||
'type': 'string', 'maxLength': 255
|
|
||||||
},
|
|
||||||
'ipam': parameter_types.boolean,
|
|
||||||
'cidr': parameter_types.cidr,
|
|
||||||
'cidr_v6': parameter_types.cidr,
|
|
||||||
'vlan_start': parameter_types.positive_integer_with_empty_str,
|
|
||||||
'network_size':
|
|
||||||
parameter_types.positive_integer_with_empty_str,
|
|
||||||
'num_networks': parameter_types.positive_integer_with_empty_str
|
|
||||||
},
|
|
||||||
'required': ['label'],
|
|
||||||
'oneOf': [
|
|
||||||
{'required': ['cidr']},
|
|
||||||
{'required': ['cidr_v6']}
|
|
||||||
],
|
|
||||||
'additionalProperties': False,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'required': ['network'],
|
|
||||||
'additionalProperties': False,
|
|
||||||
}
|
|
@ -13,24 +13,17 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
import netaddr
|
|
||||||
import netaddr.core as netexc
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import six
|
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack.api_version_request \
|
from nova.api.openstack.api_version_request \
|
||||||
import MAX_PROXY_API_SUPPORT_VERSION
|
import MAX_PROXY_API_SUPPORT_VERSION
|
||||||
from nova.api.openstack.compute.schemas import tenant_networks as schema
|
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api import validation
|
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova import context as nova_context
|
from nova import context as nova_context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
import nova.network
|
import nova.network
|
||||||
from nova import objects
|
|
||||||
from nova.policies import tenant_networks as tn_policies
|
from nova.policies import tenant_networks as tn_policies
|
||||||
from nova import quota
|
from nova import quota
|
||||||
|
|
||||||
@ -97,86 +90,13 @@ class TenantNetworkController(wsgi.Controller):
|
|||||||
raise exc.HTTPNotFound(explanation=msg)
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
return {'network': network_dict(network)}
|
return {'network': network_dict(network)}
|
||||||
|
|
||||||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
@wsgi.expected_errors(410)
|
||||||
@wsgi.expected_errors((403, 404, 409))
|
|
||||||
@wsgi.response(202)
|
|
||||||
def delete(self, req, id):
|
def delete(self, req, id):
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
context.can(tn_policies.BASE_POLICY_NAME)
|
|
||||||
|
|
||||||
try:
|
@wsgi.expected_errors(410)
|
||||||
self.network_api.disassociate(context, id)
|
|
||||||
self.network_api.delete(context, id)
|
|
||||||
except exception.PolicyNotAuthorized as e:
|
|
||||||
raise exc.HTTPForbidden(explanation=six.text_type(e))
|
|
||||||
except exception.NetworkInUse as e:
|
|
||||||
raise exc.HTTPConflict(explanation=e.format_message())
|
|
||||||
except exception.NetworkNotFound:
|
|
||||||
msg = _("Network not found")
|
|
||||||
raise exc.HTTPNotFound(explanation=msg)
|
|
||||||
|
|
||||||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
|
||||||
@wsgi.expected_errors((400, 403, 409, 503))
|
|
||||||
@validation.schema(schema.create)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
context = req.environ["nova.context"]
|
raise exc.HTTPGone()
|
||||||
context.can(tn_policies.BASE_POLICY_NAME)
|
|
||||||
|
|
||||||
network = body["network"]
|
|
||||||
keys = ["cidr", "cidr_v6", "ipam", "vlan_start", "network_size",
|
|
||||||
"num_networks"]
|
|
||||||
kwargs = {k: network.get(k) for k in keys}
|
|
||||||
|
|
||||||
label = network["label"]
|
|
||||||
|
|
||||||
if kwargs["cidr"]:
|
|
||||||
try:
|
|
||||||
net = netaddr.IPNetwork(kwargs["cidr"])
|
|
||||||
if net.size < 4:
|
|
||||||
msg = _("Requested network does not contain "
|
|
||||||
"enough (2+) usable hosts")
|
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
|
||||||
except netexc.AddrConversionError:
|
|
||||||
msg = _("Address could not be converted.")
|
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if CONF.enable_network_quota:
|
|
||||||
objects.Quotas.check_deltas(context, {'networks': 1},
|
|
||||||
context.project_id)
|
|
||||||
except exception.OverQuota:
|
|
||||||
msg = _("Quota exceeded, too many networks.")
|
|
||||||
raise exc.HTTPForbidden(explanation=msg)
|
|
||||||
|
|
||||||
kwargs['project_id'] = context.project_id
|
|
||||||
|
|
||||||
try:
|
|
||||||
networks = self.network_api.create(context,
|
|
||||||
label=label, **kwargs)
|
|
||||||
except exception.PolicyNotAuthorized as e:
|
|
||||||
raise exc.HTTPForbidden(explanation=six.text_type(e))
|
|
||||||
except exception.CidrConflict as e:
|
|
||||||
raise exc.HTTPConflict(explanation=e.format_message())
|
|
||||||
except Exception:
|
|
||||||
msg = _("Create networks failed")
|
|
||||||
LOG.exception(msg, extra=network)
|
|
||||||
raise exc.HTTPServiceUnavailable(explanation=msg)
|
|
||||||
|
|
||||||
# NOTE(melwitt): We recheck the quota after creating the object to
|
|
||||||
# prevent users from allocating more resources than their allowed quota
|
|
||||||
# in the event of a race. This is configurable because it can be
|
|
||||||
# expensive if strict quota limits are not required in a deployment.
|
|
||||||
if CONF.quota.recheck_quota and CONF.enable_network_quota:
|
|
||||||
try:
|
|
||||||
objects.Quotas.check_deltas(context, {'networks': 0},
|
|
||||||
context.project_id)
|
|
||||||
except exception.OverQuota:
|
|
||||||
self.network_api.delete(context,
|
|
||||||
network_dict(networks[0])['id'])
|
|
||||||
msg = _("Quota exceeded, too many networks.")
|
|
||||||
raise exc.HTTPForbidden(explanation=msg)
|
|
||||||
|
|
||||||
return {"network": network_dict(networks[0])}
|
|
||||||
|
|
||||||
|
|
||||||
def _network_count(context, project_id):
|
def _network_count(context, project_id):
|
||||||
|
@ -250,13 +250,6 @@ non_negative_integer = {
|
|||||||
'pattern': '^[0-9]*$', 'minimum': 0, 'minLength': 1
|
'pattern': '^[0-9]*$', 'minimum': 0, 'minLength': 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# This only be used by nova-network specific APIs. It will be removed when
|
|
||||||
# those API removed.
|
|
||||||
positive_integer_with_empty_str = {
|
|
||||||
'type': ['integer', 'string'],
|
|
||||||
'pattern': '^[0-9]*$', 'minimum': 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname = {
|
hostname = {
|
||||||
'type': 'string', 'minLength': 1, 'maxLength': 255,
|
'type': 'string', 'minLength': 1, 'maxLength': 255,
|
||||||
# NOTE: 'host' is defined in "services" table, and that
|
# NOTE: 'host' is defined in "services" table, and that
|
||||||
|
@ -34,22 +34,10 @@ deprecated.""",
|
|||||||
'method': 'GET',
|
'method': 'GET',
|
||||||
'path': '/os-tenant-networks'
|
'path': '/os-tenant-networks'
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
'method': 'POST',
|
|
||||||
'path': '/os-tenant-networks'
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
'method': 'GET',
|
'method': 'GET',
|
||||||
'path': '/os-tenant-networks/{network_id}'
|
'path': '/os-tenant-networks/{network_id}'
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
'method': 'DELETE',
|
|
||||||
'path': '/os-tenant-networks/{network_id}'
|
|
||||||
}
|
|
||||||
|
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
{
|
{
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
"cidr": "10.0.0.0/29",
|
"cidr": "None",
|
||||||
"id": "%(id)s",
|
"id": "%(uuid)s",
|
||||||
"label": "test_0"
|
"label": "private"
|
||||||
},
|
|
||||||
{
|
|
||||||
"cidr": "10.0.0.8/29",
|
|
||||||
"id": "%(id)s",
|
|
||||||
"label": "test_1"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"network": {
|
|
||||||
"label": "public",
|
|
||||||
"cidr": "172.0.0.0/24",
|
|
||||||
"vlan_start": 1,
|
|
||||||
"num_networks": 1,
|
|
||||||
"network_size": 255
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"network": {
|
|
||||||
"cidr": "172.0.0.0/24",
|
|
||||||
"id": "%(id)s",
|
|
||||||
"label": "public"
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,52 +13,24 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
|
||||||
|
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.tests import fixtures as nova_fixtures
|
|
||||||
from nova.tests.functional.api_sample_tests import api_sample_base
|
from nova.tests.functional.api_sample_tests import api_sample_base
|
||||||
|
|
||||||
CONF = nova.conf.CONF
|
CONF = nova.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
# TODO(stephenfin): Remove the parts of this test that are nova-network only
|
|
||||||
class TenantNetworksJsonTests(api_sample_base.ApiSampleTestBaseV21):
|
class TenantNetworksJsonTests(api_sample_base.ApiSampleTestBaseV21):
|
||||||
USE_NEUTRON = False # partially nova-net only
|
|
||||||
ADMIN_API = True
|
ADMIN_API = True
|
||||||
sample_dir = "os-tenant-networks"
|
sample_dir = "os-tenant-networks"
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TenantNetworksJsonTests, self).setUp()
|
|
||||||
CONF.set_override("enable_network_quota", True)
|
|
||||||
self.useFixture(nova_fixtures.RegisterNetworkQuota())
|
|
||||||
|
|
||||||
def fake(*args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.stub_out("nova.quota.QUOTAS.reserve", fake)
|
|
||||||
self.stub_out("nova.quota.QUOTAS.commit", fake)
|
|
||||||
self.stub_out("nova.quota.QUOTAS.rollback", fake)
|
|
||||||
self.stub_out("nova.quota.QuotaEngine.reserve", fake)
|
|
||||||
self.stub_out("nova.quota.QuotaEngine.commit", fake)
|
|
||||||
self.stub_out("nova.quota.QuotaEngine.rollback", fake)
|
|
||||||
|
|
||||||
# TODO(stephenfin): Rework this to work with neutron
|
|
||||||
def test_list_networks(self):
|
def test_list_networks(self):
|
||||||
response = self._do_get('os-tenant-networks')
|
response = self._do_get('os-tenant-networks')
|
||||||
self._verify_response('networks-list-res', {}, response, 200)
|
self._verify_response('networks-list-res', {}, response, 200)
|
||||||
|
|
||||||
# TODO(stephenfin): Remove this API since it's nova-network only
|
|
||||||
def test_create_network(self):
|
def test_create_network(self):
|
||||||
response = self._do_post('os-tenant-networks', "networks-post-req", {})
|
self.api.api_post('os-tenant-networks', {},
|
||||||
self._verify_response('networks-post-res', {}, response, 200)
|
check_response_status=[410])
|
||||||
|
|
||||||
# TODO(stephenfin): Remove this API since it's nova-network only
|
|
||||||
def test_delete_network(self):
|
def test_delete_network(self):
|
||||||
response = self._do_post('os-tenant-networks', "networks-post-req", {})
|
self.api.api_delete('os-tenant-networks/1',
|
||||||
net = jsonutils.loads(response.content)
|
check_response_status=[410])
|
||||||
response = self._do_delete('os-tenant-networks/%s' %
|
|
||||||
net["network"]["id"])
|
|
||||||
self.assertEqual(202, response.status_code)
|
|
||||||
self.assertEqual("", response.text)
|
|
||||||
|
@ -64,15 +64,14 @@ def fake_network_api_get_all(context):
|
|||||||
class TenantNetworksTestV21(test.NoDBTestCase):
|
class TenantNetworksTestV21(test.NoDBTestCase):
|
||||||
ctrlr = networks_v21.TenantNetworkController
|
ctrlr = networks_v21.TenantNetworkController
|
||||||
validation_error = exception.ValidationError
|
validation_error = exception.ValidationError
|
||||||
use_neutron = False
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
# TODO(stephenfin): We should probably use NeutronFixture here
|
||||||
super(TenantNetworksTestV21, self).setUp()
|
super(TenantNetworksTestV21, self).setUp()
|
||||||
# os-tenant-networks only supports Neutron when listing networks or
|
# os-tenant-networks only supports Neutron when listing networks or
|
||||||
# showing details about a network, create and delete operations
|
# showing details about a network, create and delete operations
|
||||||
# result in a 503 and 500 response, respectively.
|
# result in a 503 and 500 response, respectively.
|
||||||
self.flags(enable_network_quota=True,
|
self.flags(enable_network_quota=True)
|
||||||
use_neutron=self.use_neutron)
|
|
||||||
self.useFixture(nova_fixtures.RegisterNetworkQuota())
|
self.useFixture(nova_fixtures.RegisterNetworkQuota())
|
||||||
self.controller = self.ctrlr()
|
self.controller = self.ctrlr()
|
||||||
self.req = fakes.HTTPRequest.blank('')
|
self.req = fakes.HTTPRequest.blank('')
|
||||||
@ -83,63 +82,6 @@ class TenantNetworksTestV21(test.NoDBTestCase):
|
|||||||
CONF.set_override("use_neutron_default_nets", self.original_value,
|
CONF.set_override("use_neutron_default_nets", self.original_value,
|
||||||
group='api')
|
group='api')
|
||||||
|
|
||||||
def _fake_network_api_create(self, context, **kwargs):
|
|
||||||
self.assertEqual(context.project_id, kwargs['project_id'])
|
|
||||||
return NETWORKS
|
|
||||||
|
|
||||||
@mock.patch('nova.network.api.API.disassociate')
|
|
||||||
@mock.patch('nova.network.api.API.delete')
|
|
||||||
def _test_network_delete_exception(self, delete_ex, disassociate_ex, expex,
|
|
||||||
delete_mock, disassociate_mock):
|
|
||||||
ctxt = self.req.environ['nova.context']
|
|
||||||
|
|
||||||
if delete_mock:
|
|
||||||
delete_mock.side_effect = delete_ex
|
|
||||||
if disassociate_ex:
|
|
||||||
disassociate_mock.side_effect = disassociate_ex
|
|
||||||
|
|
||||||
if self.use_neutron:
|
|
||||||
expex = webob.exc.HTTPInternalServerError
|
|
||||||
self.assertRaises(expex, self.controller.delete, self.req, 1)
|
|
||||||
|
|
||||||
if not self.use_neutron:
|
|
||||||
disassociate_mock.assert_called_once_with(ctxt, 1)
|
|
||||||
if not disassociate_ex:
|
|
||||||
delete_mock.assert_called_once_with(ctxt, 1)
|
|
||||||
|
|
||||||
def test_network_delete_exception_network_not_found(self):
|
|
||||||
ex = exception.NetworkNotFound(network_id=1)
|
|
||||||
expex = webob.exc.HTTPNotFound
|
|
||||||
self._test_network_delete_exception(None, ex, expex)
|
|
||||||
|
|
||||||
def test_network_delete_exception_policy_failed(self):
|
|
||||||
ex = exception.PolicyNotAuthorized(action='dummy')
|
|
||||||
expex = webob.exc.HTTPForbidden
|
|
||||||
self._test_network_delete_exception(ex, None, expex)
|
|
||||||
|
|
||||||
def test_network_delete_exception_network_in_use(self):
|
|
||||||
ex = exception.NetworkInUse(network_id=1)
|
|
||||||
expex = webob.exc.HTTPConflict
|
|
||||||
self._test_network_delete_exception(ex, None, expex)
|
|
||||||
|
|
||||||
@mock.patch('nova.network.api.API.delete')
|
|
||||||
@mock.patch('nova.network.api.API.disassociate')
|
|
||||||
def test_network_delete(self, disassociate_mock, delete_mock):
|
|
||||||
ctxt = self.req.environ['nova.context']
|
|
||||||
|
|
||||||
delete_method = self.controller.delete
|
|
||||||
res = delete_method(self.req, 1)
|
|
||||||
# NOTE: on v2.1, http status code is set as wsgi_code of API
|
|
||||||
# method instead of status_int in a response object.
|
|
||||||
if isinstance(self.controller, networks_v21.TenantNetworkController):
|
|
||||||
status_int = delete_method.wsgi_code
|
|
||||||
else:
|
|
||||||
status_int = res.status_int
|
|
||||||
self.assertEqual(202, status_int)
|
|
||||||
|
|
||||||
disassociate_mock.assert_called_once_with(ctxt, 1)
|
|
||||||
delete_mock.assert_called_once_with(ctxt, 1)
|
|
||||||
|
|
||||||
def test_network_show(self):
|
def test_network_show(self):
|
||||||
with mock.patch.object(self.controller.network_api, 'get',
|
with mock.patch.object(self.controller.network_api, 'get',
|
||||||
return_value=NETWORKS[0]):
|
return_value=NETWORKS[0]):
|
||||||
@ -173,155 +115,6 @@ class TenantNetworksTestV21(test.NoDBTestCase):
|
|||||||
def test_network_index_without_default_net(self):
|
def test_network_index_without_default_net(self):
|
||||||
self._test_network_index(default_net=False)
|
self._test_network_index(default_net=False)
|
||||||
|
|
||||||
@mock.patch('nova.objects.Quotas.check_deltas')
|
|
||||||
@mock.patch('nova.network.api.API.create')
|
|
||||||
def test_network_create(self, create_mock, check_mock):
|
|
||||||
create_mock.side_effect = self._fake_network_api_create
|
|
||||||
|
|
||||||
body = copy.deepcopy(NETWORKS[0])
|
|
||||||
del body['id']
|
|
||||||
body = {'network': body}
|
|
||||||
res = self.controller.create(self.req, body=body)
|
|
||||||
|
|
||||||
self.assertEqual(NETWORKS[0], res['network'])
|
|
||||||
|
|
||||||
@mock.patch('nova.objects.Quotas.check_deltas')
|
|
||||||
@mock.patch('nova.network.api.API.delete')
|
|
||||||
@mock.patch('nova.network.api.API.create')
|
|
||||||
def test_network_create_quota_error_during_recheck(self, create_mock,
|
|
||||||
delete_mock,
|
|
||||||
check_mock):
|
|
||||||
create_mock.side_effect = self._fake_network_api_create
|
|
||||||
ctxt = self.req.environ['nova.context']
|
|
||||||
|
|
||||||
# Simulate a race where the first check passes and the recheck fails.
|
|
||||||
check_mock.side_effect = [None, exception.OverQuota(overs='networks')]
|
|
||||||
|
|
||||||
body = copy.deepcopy(NETWORKS[0])
|
|
||||||
del body['id']
|
|
||||||
body = {'network': body}
|
|
||||||
self.assertRaises(webob.exc.HTTPForbidden,
|
|
||||||
self.controller.create, self.req, body=body)
|
|
||||||
|
|
||||||
self.assertEqual(2, check_mock.call_count)
|
|
||||||
call1 = mock.call(ctxt, {'networks': 1}, ctxt.project_id)
|
|
||||||
call2 = mock.call(ctxt, {'networks': 0}, ctxt.project_id)
|
|
||||||
check_mock.assert_has_calls([call1, call2])
|
|
||||||
|
|
||||||
# Verify we removed the network that was added after the first quota
|
|
||||||
# check passed.
|
|
||||||
delete_mock.assert_called_once_with(ctxt, NETWORKS[0]['id'])
|
|
||||||
|
|
||||||
@mock.patch('nova.objects.Quotas.check_deltas')
|
|
||||||
@mock.patch('nova.network.api.API.create')
|
|
||||||
def test_network_create_no_quota_recheck(self, create_mock, check_mock):
|
|
||||||
create_mock.side_effect = self._fake_network_api_create
|
|
||||||
ctxt = self.req.environ['nova.context']
|
|
||||||
# Disable recheck_quota.
|
|
||||||
self.flags(recheck_quota=False, group='quota')
|
|
||||||
|
|
||||||
body = copy.deepcopy(NETWORKS[0])
|
|
||||||
del body['id']
|
|
||||||
body = {'network': body}
|
|
||||||
self.controller.create(self.req, body=body)
|
|
||||||
|
|
||||||
# check_deltas should have been called only once.
|
|
||||||
check_mock.assert_called_once_with(ctxt, {'networks': 1},
|
|
||||||
ctxt.project_id)
|
|
||||||
|
|
||||||
@mock.patch('nova.objects.Quotas.check_deltas')
|
|
||||||
def test_network_create_quota_error(self, check_mock):
|
|
||||||
ctxt = self.req.environ['nova.context']
|
|
||||||
|
|
||||||
check_mock.side_effect = exception.OverQuota(overs='networks')
|
|
||||||
body = {'network': {"cidr": "10.20.105.0/24",
|
|
||||||
"label": "new net 1"}}
|
|
||||||
self.assertRaises(webob.exc.HTTPForbidden,
|
|
||||||
self.controller.create, self.req, body=body)
|
|
||||||
check_mock.assert_called_once_with(ctxt, {'networks': 1},
|
|
||||||
ctxt.project_id)
|
|
||||||
|
|
||||||
@mock.patch('nova.objects.Quotas.check_deltas')
|
|
||||||
@mock.patch('nova.network.api.API.create')
|
|
||||||
def _test_network_create_exception(self, ex, expex, create_mock,
|
|
||||||
check_mock):
|
|
||||||
ctxt = self.req.environ['nova.context']
|
|
||||||
|
|
||||||
create_mock.side_effect = ex
|
|
||||||
body = {'network': {"cidr": "10.20.105.0/24",
|
|
||||||
"label": "new net 1"}}
|
|
||||||
if self.use_neutron:
|
|
||||||
expex = webob.exc.HTTPServiceUnavailable
|
|
||||||
self.assertRaises(expex, self.controller.create, self.req, body=body)
|
|
||||||
check_mock.assert_called_once_with(ctxt, {'networks': 1},
|
|
||||||
ctxt.project_id)
|
|
||||||
|
|
||||||
def test_network_create_exception_policy_failed(self):
|
|
||||||
ex = exception.PolicyNotAuthorized(action='dummy')
|
|
||||||
expex = webob.exc.HTTPForbidden
|
|
||||||
self._test_network_create_exception(ex, expex)
|
|
||||||
|
|
||||||
def test_network_create_exception_conflictcidr(self):
|
|
||||||
ex = exception.CidrConflict(cidr='dummy', other='dummy')
|
|
||||||
expex = webob.exc.HTTPConflict
|
|
||||||
self._test_network_create_exception(ex, expex)
|
|
||||||
|
|
||||||
def test_network_create_exception_service_unavailable(self):
|
|
||||||
ex = Exception
|
|
||||||
expex = webob.exc.HTTPServiceUnavailable
|
|
||||||
self._test_network_create_exception(ex, expex)
|
|
||||||
|
|
||||||
def test_network_create_empty_body(self):
|
|
||||||
self.assertRaises(exception.ValidationError,
|
|
||||||
self.controller.create, self.req, body={})
|
|
||||||
|
|
||||||
def test_network_create_without_cidr(self):
|
|
||||||
body = {'network': {"label": "new net 1"}}
|
|
||||||
self.assertRaises(self.validation_error,
|
|
||||||
self.controller.create, self.req, body=body)
|
|
||||||
|
|
||||||
def test_network_create_bad_format_cidr(self):
|
|
||||||
body = {'network': {"cidr": "123",
|
|
||||||
"label": "new net 1"}}
|
|
||||||
self.assertRaises(self.validation_error,
|
|
||||||
self.controller.create, self.req, body=body)
|
|
||||||
|
|
||||||
def test_network_create_empty_network(self):
|
|
||||||
body = {'network': {}}
|
|
||||||
self.assertRaises(self.validation_error,
|
|
||||||
self.controller.create, self.req, body=body)
|
|
||||||
|
|
||||||
def test_network_create_without_label(self):
|
|
||||||
body = {'network': {"cidr": "10.20.105.0/24"}}
|
|
||||||
self.assertRaises(self.validation_error,
|
|
||||||
self.controller.create, self.req, body=body)
|
|
||||||
|
|
||||||
|
|
||||||
class TenantNeutronNetworksTestV21(TenantNetworksTestV21):
|
|
||||||
use_neutron = True
|
|
||||||
|
|
||||||
def test_network_create(self):
|
|
||||||
self.assertRaises(
|
|
||||||
webob.exc.HTTPServiceUnavailable,
|
|
||||||
super(TenantNeutronNetworksTestV21, self).test_network_create)
|
|
||||||
|
|
||||||
def test_network_create_quota_error_during_recheck(self):
|
|
||||||
self.assertRaises(
|
|
||||||
webob.exc.HTTPServiceUnavailable,
|
|
||||||
super(TenantNeutronNetworksTestV21, self)
|
|
||||||
.test_network_create_quota_error_during_recheck)
|
|
||||||
|
|
||||||
def test_network_create_no_quota_recheck(self):
|
|
||||||
self.assertRaises(
|
|
||||||
webob.exc.HTTPServiceUnavailable,
|
|
||||||
super(TenantNeutronNetworksTestV21, self)
|
|
||||||
.test_network_create_no_quota_recheck)
|
|
||||||
|
|
||||||
def test_network_delete(self):
|
|
||||||
self.assertRaises(
|
|
||||||
webob.exc.HTTPInternalServerError,
|
|
||||||
super(TenantNeutronNetworksTestV21, self).test_network_delete)
|
|
||||||
|
|
||||||
|
|
||||||
class TenantNetworksEnforcementV21(test.NoDBTestCase):
|
class TenantNetworksEnforcementV21(test.NoDBTestCase):
|
||||||
|
|
||||||
@ -330,18 +123,6 @@ class TenantNetworksEnforcementV21(test.NoDBTestCase):
|
|||||||
self.controller = networks_v21.TenantNetworkController()
|
self.controller = networks_v21.TenantNetworkController()
|
||||||
self.req = fakes.HTTPRequest.blank('')
|
self.req = fakes.HTTPRequest.blank('')
|
||||||
|
|
||||||
def test_create_policy_failed(self):
|
|
||||||
rule_name = 'os_compute_api:os-tenant-networks'
|
|
||||||
self.policy.set_rules({rule_name: "project:non_fake"})
|
|
||||||
exc = self.assertRaises(
|
|
||||||
exception.PolicyNotAuthorized,
|
|
||||||
self.controller.create,
|
|
||||||
self.req, body={'network': {'label': 'test',
|
|
||||||
'cidr': '10.0.0.0/32'}})
|
|
||||||
self.assertEqual(
|
|
||||||
"Policy doesn't allow %s to be performed." % rule_name,
|
|
||||||
exc.format_message())
|
|
||||||
|
|
||||||
def test_index_policy_failed(self):
|
def test_index_policy_failed(self):
|
||||||
rule_name = 'os_compute_api:os-tenant-networks'
|
rule_name = 'os_compute_api:os-tenant-networks'
|
||||||
self.policy.set_rules({rule_name: "project:non_fake"})
|
self.policy.set_rules({rule_name: "project:non_fake"})
|
||||||
@ -353,17 +134,6 @@ class TenantNetworksEnforcementV21(test.NoDBTestCase):
|
|||||||
"Policy doesn't allow %s to be performed." % rule_name,
|
"Policy doesn't allow %s to be performed." % rule_name,
|
||||||
exc.format_message())
|
exc.format_message())
|
||||||
|
|
||||||
def test_delete_policy_failed(self):
|
|
||||||
rule_name = 'os_compute_api:os-tenant-networks'
|
|
||||||
self.policy.set_rules({rule_name: "project:non_fake"})
|
|
||||||
exc = self.assertRaises(
|
|
||||||
exception.PolicyNotAuthorized,
|
|
||||||
self.controller.delete,
|
|
||||||
self.req, fakes.FAKE_UUID)
|
|
||||||
self.assertEqual(
|
|
||||||
"Policy doesn't allow %s to be performed." % rule_name,
|
|
||||||
exc.format_message())
|
|
||||||
|
|
||||||
def test_show_policy_failed(self):
|
def test_show_policy_failed(self):
|
||||||
rule_name = 'os_compute_api:os-tenant-networks'
|
rule_name = 'os_compute_api:os-tenant-networks'
|
||||||
self.policy.set_rules({rule_name: "project:non_fake"})
|
self.policy.set_rules({rule_name: "project:non_fake"})
|
||||||
@ -388,7 +158,3 @@ class TenantNetworksDeprecationTest(test.NoDBTestCase):
|
|||||||
self.controller.index, self.req)
|
self.controller.index, self.req)
|
||||||
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
||||||
self.controller.show, self.req, fakes.FAKE_UUID)
|
self.controller.show, self.req, fakes.FAKE_UUID)
|
||||||
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
|
||||||
self.controller.delete, self.req, fakes.FAKE_UUID)
|
|
||||||
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
|
||||||
self.controller.create, self.req, {})
|
|
||||||
|
@ -17,6 +17,8 @@ upgrade:
|
|||||||
* ``POST /os-networks/{id} (disassociate)``
|
* ``POST /os-networks/{id} (disassociate)``
|
||||||
* ``POST /os-networks/{id} (disassociate_host)``
|
* ``POST /os-networks/{id} (disassociate_host)``
|
||||||
* ``POST /os-networks/{id} (disassociate_project)``
|
* ``POST /os-networks/{id} (disassociate_project)``
|
||||||
|
* ``POST /os-tenant-networks``
|
||||||
|
* ``DELETE /os-tenant-networks``
|
||||||
|
|
||||||
The following policies have also been removed.
|
The following policies have also been removed.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user