@@ -1,28 +1,26 @@ | |||
.. -*- rst -*- | |||
.. NOTE(sdague): These APIs are deprecated so do not update this | |||
file even body, example or parameters are not complete. | |||
===================================== | |||
====================================== | |||
Networks (os-networks) (DEPRECATED) | |||
===================================== | |||
====================================== | |||
.. warning:: The networks API was designed to work with | |||
``nova-network``. Some features are proxied to | |||
``neutron`` when appropriate, but as with all translation | |||
proxies, this is far from perfect compatibility. These | |||
APIs should be avoided in new applications in favor of | |||
using ``neutron`` directly. These will fail with a 404 | |||
starting from microversion 2.36. | |||
See: `Relevant Network APIs | |||
<https://docs.openstack.org/api-ref/network/v2/#networks>`__. | |||
.. warning:: | |||
This API was designed to work with ``nova-network`` which was deprecated in | |||
the 14.0.0 (Newton) release and removed in the 21.0.0 (Ussuri) release. Some | |||
features are proxied to the Network service (neutron) when appropriate, but | |||
as with all translation proxies, this is far from perfect compatibility. | |||
These APIs should be avoided in new applications in favor of `using | |||
neutron directly`__. These will fail with a 404 starting from microversion | |||
2.36. They were removed in the 21.0.0 (Ussuri) release. | |||
__ https://docs.openstack.org/api-ref/network/v2/#networks | |||
Creates, lists, shows information for, and deletes networks. | |||
Adds network to a project, disassociates a network from a project, and | |||
disassociates a project from a network. | |||
Associates host with and disassociates host from a network. | |||
List Networks | |||
@@ -60,7 +58,8 @@ these permissions through the ``policy.json`` file. | |||
Normal response codes: 200 | |||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), conflict(409), NotImplemented(501) | |||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), | |||
conflict(409), gone(410), notImplemented(501) | |||
Request | |||
------- | |||
@@ -91,7 +90,8 @@ this operation. Cloud providers can change these permissions through the | |||
Normal response codes: 202 | |||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), NotImplemented(501) | |||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), | |||
gone(410), notImplemented(501) | |||
Request | |||
------- | |||
@@ -146,7 +146,8 @@ these permissions through the ``policy.json`` file. | |||
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 | |||
------- | |||
@@ -160,15 +161,11 @@ Response | |||
There is no body content for the response of a successful DELETE query. | |||
Associate Host (DEPRECATED) | |||
=========================== | |||
Associate Host | |||
============== | |||
.. rest_method:: POST /os-networks/{network_id}/action | |||
.. warning:: | |||
This API is only available with ``nova-network`` which is | |||
deprecated. It should be avoided in any new applications. | |||
Associates a network with a host. | |||
Specify the ``associate_host`` action in the request body. | |||
@@ -179,7 +176,8 @@ permissions through the ``policy.json`` file. | |||
Normal response codes: 202 | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), | |||
gone(410), notImplemented(501) | |||
Request | |||
------- | |||
@@ -199,15 +197,11 @@ Response | |||
There is no body content for the response of a successful POST query. | |||
Disassociate Network (DEPRECATED) | |||
================================= | |||
Disassociate Network | |||
==================== | |||
.. rest_method:: POST /os-networks/{network_id}/action | |||
.. warning:: | |||
This API is only available with ``nova-network`` which is | |||
deprecated. It should be avoided in any new applications. | |||
Disassociates a network from a project. You can then reuse the network. | |||
Specify the ``disassociate`` action in the request body. | |||
@@ -218,7 +212,8 @@ these permissions through the ``policy.json`` file. | |||
Normal response codes: 202 | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), | |||
gone(410), notImplemented(501) | |||
Request | |||
------- | |||
@@ -237,15 +232,11 @@ Response | |||
There is no body content for the response of a successful POST query. | |||
Disassociate Host (DEPRECATED) | |||
============================== | |||
Disassociate Host | |||
================= | |||
.. rest_method:: POST /os-networks/{network_id}/action | |||
.. warning:: | |||
This API is only available with ``nova-network`` which is | |||
deprecated. It should be avoided in any new applications. | |||
Disassociates a host from a network. | |||
Specify the ``disassociate_host`` action in the request body. | |||
@@ -256,7 +247,8 @@ these permissions through the ``policy.json`` file. | |||
Normal response codes: 202 | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), | |||
gone(410), notImplemented(501) | |||
Request | |||
------- | |||
@@ -276,15 +268,11 @@ Response | |||
There is no body content for the response of a successful POST query. | |||
Disassociate Project (DEPRECATED) | |||
================================= | |||
Disassociate Project | |||
==================== | |||
.. rest_method:: POST /os-networks/{network_id}/action | |||
.. warning:: | |||
This API is only available with ``nova-network`` which is | |||
deprecated. It should be avoided in any new applications. | |||
Disassociates a project from a network. | |||
Specify the ``disassociate_project`` action in the request body. | |||
@@ -295,7 +283,8 @@ these permissions through the ``policy.json`` file. | |||
Normal response codes: 202 | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501) | |||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), | |||
gone(410), notImplemented(501) | |||
Request | |||
------- | |||
@@ -1,36 +1,36 @@ | |||
{ | |||
"network": { | |||
"bridge": "br100", | |||
"bridge_interface": "eth0", | |||
"broadcast": "10.0.0.7", | |||
"cidr": "10.0.0.0/29", | |||
"bridge": null, | |||
"bridge_interface": null, | |||
"broadcast": null, | |||
"cidr": null, | |||
"cidr_v6": null, | |||
"created_at": "2011-08-15T06:19:19.387525", | |||
"deleted": false, | |||
"created_at": null, | |||
"deleted": null, | |||
"deleted_at": null, | |||
"dhcp_server": "10.0.0.1", | |||
"dhcp_start": "10.0.0.3", | |||
"dhcp_server": null, | |||
"dhcp_start": null, | |||
"dns1": null, | |||
"dns2": null, | |||
"enable_dhcp": true, | |||
"gateway": "10.0.0.1", | |||
"enable_dhcp": null, | |||
"gateway": null, | |||
"gateway_v6": null, | |||
"host": "nsokolov-desktop", | |||
"host": null, | |||
"id": "20c8acc0-f747-4d71-a389-46d078ebf047", | |||
"injected": false, | |||
"label": "mynet_0", | |||
"injected": null, | |||
"label": "private", | |||
"mtu": null, | |||
"multi_host": false, | |||
"netmask": "255.255.255.248", | |||
"multi_host": null, | |||
"netmask": null, | |||
"netmask_v6": null, | |||
"priority": null, | |||
"project_id": "6133f8b603924f45bc0c9e21f6df12fa", | |||
"project_id": null, | |||
"rxtx_base": null, | |||
"share_address": false, | |||
"updated_at": "2011-08-16T09:26:13.048257", | |||
"vlan": 100, | |||
"vpn_private_address": "10.0.0.2", | |||
"vpn_public_address": "127.0.0.1", | |||
"vpn_public_port": 1000 | |||
"share_address": null, | |||
"updated_at": null, | |||
"vlan": null, | |||
"vpn_private_address": null, | |||
"vpn_public_address": null, | |||
"vpn_public_port": null | |||
} | |||
} | |||
} |
@@ -1,72 +1,38 @@ | |||
{ | |||
"networks": [ | |||
{ | |||
"bridge": "br100", | |||
"bridge_interface": "eth0", | |||
"broadcast": "10.0.0.7", | |||
"cidr": "10.0.0.0/29", | |||
"bridge": null, | |||
"bridge_interface": null, | |||
"broadcast": null, | |||
"cidr": null, | |||
"cidr_v6": null, | |||
"created_at": "2011-08-15T06:19:19.387525", | |||
"deleted": false, | |||
"created_at": null, | |||
"deleted": null, | |||
"deleted_at": null, | |||
"dhcp_server": "10.0.0.1", | |||
"dhcp_start": "10.0.0.3", | |||
"dhcp_server": null, | |||
"dhcp_start": null, | |||
"dns1": null, | |||
"dns2": null, | |||
"enable_dhcp": true, | |||
"gateway": "10.0.0.1", | |||
"gateway_v6": null, | |||
"host": "nsokolov-desktop", | |||
"id": "20c8acc0-f747-4d71-a389-46d078ebf047", | |||
"injected": false, | |||
"label": "mynet_0", | |||
"mtu": null, | |||
"multi_host": false, | |||
"netmask": "255.255.255.248", | |||
"netmask_v6": null, | |||
"priority": null, | |||
"project_id": "6133f8b603924f45bc0c9e21f6df12fa", | |||
"rxtx_base": null, | |||
"share_address": false, | |||
"updated_at": "2011-08-16T09:26:13.048257", | |||
"vlan": 100, | |||
"vpn_private_address": "10.0.0.2", | |||
"vpn_public_address": "127.0.0.1", | |||
"vpn_public_port": 1000 | |||
}, | |||
{ | |||
"bridge": "br101", | |||
"bridge_interface": "eth0", | |||
"broadcast": "10.0.0.15", | |||
"cidr": "10.0.0.10/29", | |||
"cidr_v6": null, | |||
"created_at": "2011-08-15T06:19:19.885495", | |||
"deleted": false, | |||
"deleted_at": null, | |||
"dhcp_server": "10.0.0.9", | |||
"dhcp_start": "10.0.0.11", | |||
"dns1": null, | |||
"dns2": null, | |||
"enable_dhcp": true, | |||
"gateway": "10.0.0.9", | |||
"enable_dhcp": null, | |||
"gateway": null, | |||
"gateway_v6": null, | |||
"host": null, | |||
"id": "20c8acc0-f747-4d71-a389-46d078ebf000", | |||
"injected": false, | |||
"label": "mynet_1", | |||
"id": "20c8acc0-f747-4d71-a389-46d078ebf047", | |||
"injected": null, | |||
"label": "private", | |||
"mtu": null, | |||
"multi_host": false, | |||
"netmask": "255.255.255.248", | |||
"multi_host": null, | |||
"netmask": null, | |||
"netmask_v6": null, | |||
"priority": null, | |||
"project_id": null, | |||
"rxtx_base": null, | |||
"share_address": false, | |||
"share_address": null, | |||
"updated_at": null, | |||
"vlan": 101, | |||
"vpn_private_address": "10.0.0.10", | |||
"vlan": null, | |||
"vpn_private_address": null, | |||
"vpn_public_address": null, | |||
"vpn_public_port": 1001 | |||
"vpn_public_port": null | |||
} | |||
] | |||
} | |||
} |
@@ -14,15 +14,11 @@ | |||
# License for the specific language governing permissions and limitations | |||
# under the License. | |||
import netaddr | |||
from webob import exc | |||
from nova.api.openstack.api_version_request \ | |||
import MAX_PROXY_API_SUPPORT_VERSION | |||
from nova.api.openstack import common | |||
from nova.api.openstack.compute.schemas import networks as schema | |||
from nova.api.openstack import wsgi | |||
from nova.api import validation | |||
from nova import exception | |||
from nova.i18n import _ | |||
from nova import network | |||
@@ -92,22 +88,6 @@ class NetworkController(wsgi.Controller): | |||
result = [network_dict(context, net_ref) for net_ref in networks] | |||
return {'networks': result} | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.response(202) | |||
@wsgi.expected_errors((404, 501)) | |||
@wsgi.action("disassociate") | |||
def _disassociate_host_and_project(self, req, id, body): | |||
context = req.environ['nova.context'] | |||
context.can(net_policies.BASE_POLICY_NAME) | |||
try: | |||
self.network_api.associate(context, id, host=None, project=None) | |||
except exception.NetworkNotFound: | |||
msg = _("Network not found") | |||
raise exc.HTTPNotFound(explanation=msg) | |||
except NotImplementedError: | |||
common.raise_feature_not_supported() | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.expected_errors(404) | |||
def show(self, req, id): | |||
@@ -121,63 +101,19 @@ class NetworkController(wsgi.Controller): | |||
raise exc.HTTPNotFound(explanation=msg) | |||
return {'network': network_dict(context, network)} | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.response(202) | |||
@wsgi.expected_errors((404, 409)) | |||
def delete(self, req, id): | |||
context = req.environ['nova.context'] | |||
context.can(net_policies.BASE_POLICY_NAME) | |||
@wsgi.expected_errors(410) | |||
@wsgi.action("disassociate") | |||
def _disassociate_host_and_project(self, req, id, body): | |||
raise exc.HTTPGone() | |||
try: | |||
self.network_api.delete(context, id) | |||
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.expected_errors(410) | |||
def delete(self, req, id): | |||
raise exc.HTTPGone() | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.expected_errors((400, 409, 501)) | |||
@validation.schema(schema.create) | |||
@wsgi.expected_errors(410) | |||
def create(self, req, body): | |||
context = req.environ['nova.context'] | |||
context.can(net_policies.BASE_POLICY_NAME) | |||
params = body["network"] | |||
cidr = params.get("cidr") or params.get("cidr_v6") | |||
raise exc.HTTPGone() | |||
params["num_networks"] = 1 | |||
params["network_size"] = netaddr.IPNetwork(cidr).size | |||
try: | |||
network = self.network_api.create(context, **params)[0] | |||
except (exception.InvalidCidr, | |||
exception.InvalidIntValue, | |||
exception.InvalidAddress, | |||
exception.NetworkNotCreated) as ex: | |||
raise exc.HTTPBadRequest(explanation=ex.format_message) | |||
except (exception.CidrConflict, | |||
exception.DuplicateVlan) as ex: | |||
raise exc.HTTPConflict(explanation=ex.format_message()) | |||
return {"network": network_dict(context, network)} | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.response(202) | |||
@wsgi.expected_errors((400, 501)) | |||
@validation.schema(schema.add_network_to_project) | |||
@wsgi.expected_errors(410) | |||
def add(self, req, body): | |||
context = req.environ['nova.context'] | |||
context.can(net_policies.BASE_POLICY_NAME) | |||
network_id = body['id'] | |||
project_id = context.project_id | |||
try: | |||
self.network_api.add_network_to_project( | |||
context, project_id, network_id) | |||
except NotImplementedError: | |||
common.raise_feature_not_supported() | |||
except (exception.NoMoreNetworks, | |||
exception.NetworkNotFoundForUUID) as e: | |||
raise exc.HTTPBadRequest(explanation=e.format_message()) | |||
raise exc.HTTPGone() |
@@ -12,70 +12,23 @@ | |||
from webob import exc | |||
from nova.api.openstack.api_version_request \ | |||
import MAX_PROXY_API_SUPPORT_VERSION | |||
from nova.api.openstack import common | |||
from nova.api.openstack.compute.schemas import networks_associate | |||
from nova.api.openstack import wsgi | |||
from nova.api import validation | |||
from nova import exception | |||
from nova.i18n import _ | |||
from nova import network | |||
from nova.policies import networks_associate as na_policies | |||
class NetworkAssociateActionController(wsgi.Controller): | |||
"""Network Association API Controller.""" | |||
def __init__(self, network_api=None): | |||
super(NetworkAssociateActionController, self).__init__() | |||
# TODO(stephenfin): 'network_api' is only being passed for use by tests | |||
self.network_api = network_api or network.API() | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.action("disassociate_host") | |||
@wsgi.response(202) | |||
@wsgi.expected_errors((404, 501)) | |||
@wsgi.expected_errors(410) | |||
def _disassociate_host_only(self, req, id, body): | |||
context = req.environ['nova.context'] | |||
context.can(na_policies.BASE_POLICY_NAME) | |||
try: | |||
self.network_api.associate(context, id, host=None) | |||
except exception.NetworkNotFound: | |||
msg = _("Network not found") | |||
raise exc.HTTPNotFound(explanation=msg) | |||
except NotImplementedError: | |||
common.raise_feature_not_supported() | |||
raise exc.HTTPGone() | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.action("disassociate_project") | |||
@wsgi.response(202) | |||
@wsgi.expected_errors((404, 501)) | |||
@wsgi.expected_errors(410) | |||
def _disassociate_project_only(self, req, id, body): | |||
context = req.environ['nova.context'] | |||
context.can(na_policies.BASE_POLICY_NAME) | |||
try: | |||
self.network_api.associate(context, id, project=None) | |||
except exception.NetworkNotFound: | |||
msg = _("Network not found") | |||
raise exc.HTTPNotFound(explanation=msg) | |||
except NotImplementedError: | |||
common.raise_feature_not_supported() | |||
raise exc.HTTPGone() | |||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) | |||
@wsgi.action("associate_host") | |||
@wsgi.response(202) | |||
@wsgi.expected_errors((404, 501)) | |||
@validation.schema(networks_associate.associate_host) | |||
@wsgi.expected_errors(410) | |||
def _associate_host(self, req, id, body): | |||
context = req.environ['nova.context'] | |||
context.can(na_policies.BASE_POLICY_NAME) | |||
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: | |||
common.raise_feature_not_supported() | |||
raise exc.HTTPGone() |
@@ -435,6 +435,12 @@ API endpoints as below:: | |||
21.0.0 (Ussuri) release. On deployments newer than this, the APIs will | |||
return HTTP 410 (Gone) regadless of the requested microversion. | |||
.. versionchanged:: 21.0.0 | |||
The ``os-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 | |||
---- | |||
@@ -1,77 +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, | |||
'project_id': parameter_types.project_id, | |||
'multi_host': parameter_types.boolean, | |||
'gateway': parameter_types.ipv4, | |||
'gateway_v6': parameter_types.ipv6, | |||
'bridge': { | |||
'type': 'string', | |||
}, | |||
'bridge_interface': { | |||
'type': 'string', | |||
}, | |||
# NOTE: In _extract_subnets(), dns1, dns2 dhcp_server are | |||
# used only for IPv4, not IPv6. | |||
'dns1': parameter_types.ipv4, | |||
'dns2': parameter_types.ipv4, | |||
'dhcp_server': parameter_types.ipv4, | |||
'fixed_cidr': parameter_types.cidr, | |||
'allowed_start': parameter_types.ip_address, | |||
'allowed_end': parameter_types.ip_address, | |||
'enable_dhcp': parameter_types.boolean, | |||
'share_address': parameter_types.boolean, | |||
'mtu': parameter_types.positive_integer_with_empty_str, | |||
'vlan': parameter_types.positive_integer_with_empty_str, | |||
'vlan_start': parameter_types.positive_integer_with_empty_str, | |||
'vpn_start': { | |||
'type': 'string', | |||
}, | |||
}, | |||
'required': ['label'], | |||
'oneOf': [ | |||
{'required': ['cidr']}, | |||
{'required': ['cidr_v6']} | |||
], | |||
'additionalProperties': False, | |||
}, | |||
}, | |||
'required': ['network'], | |||
'additionalProperties': False, | |||
} | |||
add_network_to_project = { | |||
'type': 'object', | |||
'properties': { | |||
'id': {'type': ['string', 'null']} | |||
}, | |||
'required': ['id'], | |||
'additionalProperties': False | |||
} |
@@ -1,24 +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 | |||
associate_host = { | |||
'type': 'object', | |||
'properties': { | |||
'associate_host': parameter_types.hostname | |||
}, | |||
'required': ['associate_host'], | |||
'additionalProperties': False | |||
} |
@@ -47,7 +47,6 @@ from nova.policies import migrate_server | |||
from nova.policies import migrations | |||
from nova.policies import multinic | |||
from nova.policies import networks | |||
from nova.policies import networks_associate | |||
from nova.policies import pause_server | |||
from nova.policies import quota_class_sets | |||
from nova.policies import quota_sets | |||
@@ -109,7 +108,6 @@ def list_rules(): | |||
migrations.list_rules(), | |||
multinic.list_rules(), | |||
networks.list_rules(), | |||
networks_associate.list_rules(), | |||
pause_server.list_rules(), | |||
quota_class_sets.list_rules(), | |||
quota_sets.list_rules(), | |||
@@ -18,36 +18,10 @@ from oslo_policy import policy | |||
from nova.policies import base | |||
BASE_POLICY_NAME = 'os_compute_api:os-networks' | |||
POLICY_ROOT = 'os_compute_api:os-networks:%s' | |||
networks_policies = [ | |||
policy.DocumentedRuleDefault( | |||
BASE_POLICY_NAME, | |||
base.RULE_ADMIN_API, | |||
"""Create and delete a network, add and disassociate a network | |||
from a project. | |||
These APIs are only available with nova-network which is deprecated.""", | |||
[ | |||
{ | |||
'method': 'POST', | |||
'path': '/os-networks' | |||
}, | |||
{ | |||
'method': 'POST', | |||
'path': '/os-networks/add' | |||
}, | |||
{ | |||
'method': 'DELETE', | |||
'path': '/os-networks/{network_id}' | |||
}, | |||
{ | |||
'method': 'POST', | |||
'path': '/os-networks/{network_id}/action (disassociate)' | |||
} | |||
]), | |||
policy.DocumentedRuleDefault( | |||
POLICY_ROOT % 'view', | |||
base.RULE_ADMIN_OR_OWNER, | |||
@@ -1,50 +0,0 @@ | |||
# Copyright 2016 Cloudbase Solutions Srl | |||
# 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 oslo_policy import policy | |||
from nova.policies import base | |||
BASE_POLICY_NAME = 'os_compute_api:os-networks-associate' | |||
networks_associate_policies = [ | |||
policy.DocumentedRuleDefault( | |||
BASE_POLICY_NAME, | |||
base.RULE_ADMIN_API, | |||
"""Associate or disassociate a network from a host or project. | |||
These APIs are only available with nova-network which is deprecated.""", | |||
[ | |||
{ | |||
'method': 'POST', | |||
'path': '/os-networks/{network_id}/action (disassociate_host)' | |||
}, | |||
{ | |||
'method': 'POST', | |||
'path': '/os-networks/{network_id}/action' | |||
' (disassociate_project)' | |||
}, | |||
{ | |||
'method': 'POST', | |||
'path': '/os-networks/{network_id}/action (associate_host)' | |||
} | |||
]), | |||
] | |||
def list_rules(): | |||
return networks_associate_policies |
@@ -1,3 +0,0 @@ | |||
{ | |||
"associate_host": "%(host)s" | |||
} |
@@ -1,3 +0,0 @@ | |||
{ | |||
"disassociate_host": null | |||
} |
@@ -1,3 +0,0 @@ | |||
{ | |||
"disassociate_project": null | |||
} |
@@ -1,3 +0,0 @@ | |||
{ | |||
"disassociate": null | |||
} |
@@ -1,3 +0,0 @@ | |||
{ | |||
"id": "1" | |||
} |
@@ -1,12 +0,0 @@ | |||
{ | |||
"network": { | |||
"label": "new net 111", | |||
"cidr": "10.20.105.0/24", | |||
"mtu": 9000, | |||
"dhcp_server": "10.20.105.2", | |||
"enable_dhcp": false, | |||
"share_address": true, | |||
"allowed_start": "10.20.105.10", | |||
"allowed_end": "10.20.105.200" | |||
} | |||
} |
@@ -1,36 +0,0 @@ | |||
{ | |||
"network": { | |||
"bridge": null, | |||
"vpn_public_port": null, | |||
"dhcp_start": "%(ip)s", | |||
"bridge_interface": null, | |||
"updated_at": null, | |||
"id": "%(id)s", | |||
"cidr_v6": null, | |||
"deleted_at": null, | |||
"gateway": "%(ip)s", | |||
"rxtx_base": null, | |||
"label": "new net 111", | |||
"priority": null, | |||
"project_id": null, | |||
"vpn_private_address": null, | |||
"deleted": null, | |||
"vlan": null, | |||
"broadcast": "%(ip)s", | |||
"netmask": "%(ip)s", | |||
"injected": null, | |||
"cidr": "10.20.105.0/24", | |||
"vpn_public_address": null, | |||
"multi_host": null, | |||
"dns2": null, | |||
"created_at": null, | |||
"host": null, | |||
"gateway_v6": null, | |||
"netmask_v6": null, | |||
"dns1": null, | |||
"mtu": 9000, | |||
"dhcp_server": "10.20.105.2", | |||
"enable_dhcp": false, | |||
"share_address": true | |||
} | |||
} |
@@ -1,37 +1,37 @@ | |||
{ | |||
"network": | |||
{ | |||
"bridge": "br100", | |||
"bridge_interface": "eth0", | |||
"broadcast": "%(ip)s", | |||
"cidr": "10.0.0.0/29", | |||
"bridge": null, | |||
"bridge_interface": null, | |||
"broadcast": null, | |||
"cidr": null, | |||
"cidr_v6": null, | |||
"created_at": "%(strtime)s", | |||
"deleted": false, | |||
"created_at": null, | |||
"deleted": null, | |||
"deleted_at": null, | |||
"dhcp_start": "%(ip)s", | |||
"dhcp_start": null, | |||
"dns1": null, | |||
"dns2": null, | |||
"gateway": "%(ip)s", | |||
"gateway": null, | |||
"gateway_v6": null, | |||
"host": "nsokolov-desktop", | |||
"host": null, | |||
"id": "%(id)s", | |||
"injected": false, | |||
"label": "mynet_0", | |||
"multi_host": false, | |||
"netmask": "%(ip)s", | |||
"injected": null, | |||
"label": "private", | |||
"multi_host": null, | |||
"netmask": null, | |||
"netmask_v6": null, | |||
"priority": null, | |||
"project_id": "6133f8b603924f45bc0c9e21f6df12fa", | |||
"project_id": null, | |||
"rxtx_base": null, | |||
"updated_at": "%(strtime)s", | |||
"vlan": 100, | |||
"vpn_private_address": "%(ip)s", | |||
"vpn_public_address": "%(ip)s", | |||
"vpn_public_port": 1000, | |||
"updated_at": null, | |||
"vlan": null, | |||
"vpn_private_address": null, | |||
"vpn_public_address": null, | |||
"vpn_public_port": null, | |||
"mtu": null, | |||
"dhcp_server": "%(ip)s", | |||
"enable_dhcp": true, | |||
"share_address": false | |||
"dhcp_server": null, | |||
"enable_dhcp": null, | |||
"share_address": null | |||
} | |||
} |
@@ -1,72 +1,38 @@ | |||
{ | |||
"networks": [ | |||
{ | |||
"bridge": "br100", | |||
"bridge_interface": "eth0", | |||
"broadcast": "%(ip)s", | |||
"cidr": "10.0.0.0/29", | |||
"bridge": null, | |||
"bridge_interface": null, | |||
"broadcast": null, | |||
"cidr": null, | |||
"cidr_v6": null, | |||
"created_at": "%(strtime)s", | |||
"deleted": false, | |||
"created_at": null, | |||
"deleted": null, | |||
"deleted_at": null, | |||
"dhcp_start": "%(ip)s", | |||
"dhcp_start": null, | |||
"dns1": null, | |||
"dns2": null, | |||
"gateway": "%(ip)s", | |||
"gateway_v6": null, | |||
"host": "nsokolov-desktop", | |||
"id": "%(id)s", | |||
"injected": false, | |||
"label": "mynet_0", | |||
"multi_host": false, | |||
"netmask": "%(ip)s", | |||
"netmask_v6": null, | |||
"priority": null, | |||
"project_id": "6133f8b603924f45bc0c9e21f6df12fa", | |||
"rxtx_base": null, | |||
"updated_at": "%(strtime)s", | |||
"vlan": 100, | |||
"vpn_private_address": "%(ip)s", | |||
"vpn_public_address": "%(ip)s", | |||
"vpn_public_port": 1000, | |||
"mtu": null, | |||
"dhcp_server": "%(ip)s", | |||
"enable_dhcp": true, | |||
"share_address": false | |||
}, | |||
{ | |||
"bridge": "br101", | |||
"bridge_interface": "eth0", | |||
"broadcast": "%(ip)s", | |||
"cidr": "10.0.0.10/29", | |||
"cidr_v6": null, | |||
"created_at": "%(strtime)s", | |||
"deleted": false, | |||
"deleted_at": null, | |||
"dhcp_start": "%(ip)s", | |||
"dns1": null, | |||
"dns2": null, | |||
"gateway": "%(ip)s", | |||
"gateway": null, | |||
"gateway_v6": null, | |||
"host": null, | |||
"id": "%(id)s", | |||
"injected": false, | |||
"label": "mynet_1", | |||
"multi_host": false, | |||
"netmask": "%(ip)s", | |||
"injected": null, | |||
"label": "private", | |||
"multi_host": null, | |||
"netmask": null, | |||
"netmask_v6": null, | |||
"priority": null, | |||
"project_id": null, | |||
"rxtx_base": null, | |||
"updated_at": null, | |||
"vlan": 101, | |||
"vpn_private_address": "%(ip)s", | |||
"vlan": null, | |||
"vpn_private_address": null, | |||
"vpn_public_address": null, | |||
"vpn_public_port": 1001, | |||
"vpn_public_port": null, | |||
"mtu": null, | |||
"dhcp_server": "%(ip)s", | |||
"enable_dhcp": true, | |||
"share_address": false | |||
"dhcp_server": null, | |||
"enable_dhcp": null, | |||
"share_address": null | |||
} | |||
] | |||
} |
@@ -16,91 +16,38 @@ | |||
import mock | |||
from nova import exception | |||
from nova.tests import fixtures as nova_fixtures | |||
from nova.tests.functional.api_sample_tests import api_sample_base | |||
from nova.tests.unit.api.openstack.compute import test_networks | |||
def _fixtures_passthrough(method_name): | |||
# This compensates for how fixtures 3.x handles the signatures of | |||
# MonkeyPatched functions vs fixtures < 3.x. In fixtures 3 if a bound | |||
# method is patched in for a bound method then both objects will be passed | |||
# in when called. This means the patch method should have the signature of | |||
# (self, targetself, *args, **kwargs). However that will not work for | |||
# fixtures < 3. This method captures self from the call point and discards | |||
# it since it's not needed. | |||
fake_network_api = test_networks.FakeNetworkAPI() | |||
method = getattr(fake_network_api, method_name) | |||
def call(self, *args, **kwargs): | |||
# self is the nova.network.api.API object that has been patched | |||
# method is bound to FakeNetworkAPI so that will be passed in as self | |||
return method(*args, **kwargs) | |||
return call | |||
# TODO(stephenfin): Remove the parts of this test that are nova-network only | |||
class NetworksJsonTests(api_sample_base.ApiSampleTestBaseV21): | |||
USE_NEUTRON = False # partially nova-net only | |||
ADMIN_API = True | |||
sample_dir = "os-networks" | |||
def setUp(self): | |||
super(NetworksJsonTests, self).setUp() | |||
self.stub_out("nova.network.api.API.get_all", | |||
_fixtures_passthrough('get_all')) | |||
self.stub_out("nova.network.api.API.get", | |||
_fixtures_passthrough('get')) | |||
self.stub_out("nova.network.api.API.associate", | |||
_fixtures_passthrough('associate')) | |||
self.stub_out("nova.network.api.API.delete", | |||
_fixtures_passthrough('delete')) | |||
self.stub_out("nova.network.api.API.create", | |||
_fixtures_passthrough('create')) | |||
self.stub_out("nova.network.api.API.add_network_to_project", | |||
_fixtures_passthrough('add_network_to_project')) | |||
sample_dir = 'os-networks' | |||
# TODO(stephenfin): Rework this to work with neutron | |||
def test_network_list(self): | |||
response = self._do_get('os-networks') | |||
self._verify_response('networks-list-resp', {}, response, 200) | |||
# TODO(stephenfin): Rework this to work with neutron | |||
def test_network_show(self): | |||
uuid = test_networks.FAKE_NETWORKS[0]['uuid'] | |||
uuid = nova_fixtures.NeutronFixture.network_1['id'] | |||
response = self._do_get('os-networks/%s' % uuid) | |||
self._verify_response('network-show-resp', {}, response, 200) | |||
# TODO(stephenfin): Rework this to work with neutron | |||
@mock.patch('nova.network.api.API.get', side_effect=exception.Unauthorized) | |||
@mock.patch('nova.network.neutronv2.api.API.get', | |||
side_effect=exception.Unauthorized) | |||
def test_network_show_token_expired(self, mock_get): | |||
uuid = test_networks.FAKE_NETWORKS[0]['uuid'] | |||
uuid = nova_fixtures.NeutronFixture.network_1['id'] | |||
response = self._do_get('os-networks/%s' % uuid) | |||
self.assertEqual(401, response.status_code) | |||
# TODO(stephenfin): Remove this API since it's nova-network only | |||
@mock.patch('nova.network.api.API.create', | |||
side_effect=exception.Forbidden) | |||
def test_network_create_forbidden(self, mock_create): | |||
response = self._do_post("os-networks", | |||
'network-create-req', {}) | |||
self.assertEqual(403, response.status_code) | |||
# TODO(stephenfin): Remove this API since it's nova-network only | |||
def test_network_create(self): | |||
response = self._do_post("os-networks", | |||
'network-create-req', {}) | |||
self._verify_response('network-create-resp', {}, response, 200) | |||
self.api.api_post('os-networks', {}, | |||
check_response_status=[410]) | |||
# TODO(stephenfin): Remove this API since it's nova-network only | |||
def test_network_add(self): | |||
response = self._do_post("os-networks/add", | |||
'network-add-req', {}) | |||
self.assertEqual(202, response.status_code) | |||
self.assertEqual("", response.text) | |||
self.api.api_post('os-networks/add', {}, | |||
check_response_status=[410]) | |||
# TODO(stephenfin): Remove this API since it's nova-network only | |||
def test_network_delete(self): | |||
response = self._do_delete('os-networks/always_delete') | |||
self.assertEqual(202, response.status_code) | |||
self.assertEqual("", response.text) | |||
self.api.api_delete('os-networks/always-delete', | |||
check_response_status=[410]) |
@@ -16,48 +16,25 @@ | |||
from nova.tests.functional.api_sample_tests import api_sample_base | |||
# TODO(stephenfin): Remove this API since it's nova-network only | |||
class NetworksAssociateJsonTests(api_sample_base.ApiSampleTestBaseV21): | |||
USE_NEUTRON = False # nova-net only | |||
ADMIN_API = True | |||
sample_dir = "os-networks-associate" | |||
_sentinel = object() | |||
def setUp(self): | |||
super(NetworksAssociateJsonTests, self).setUp() | |||
def fake_associate(self, context, network_id, | |||
host=NetworksAssociateJsonTests._sentinel, | |||
project=NetworksAssociateJsonTests._sentinel): | |||
return True | |||
self.stub_out("nova.network.api.API.associate", fake_associate) | |||
def test_disassociate(self): | |||
response = self._do_post('os-networks/1/action', | |||
'network-disassociate-req', | |||
{}) | |||
self.assertEqual(202, response.status_code) | |||
self.assertEqual("", response.text) | |||
self.api.api_post('os-networks/1/action', | |||
{'disassociate': None}, | |||
check_response_status=[410]) | |||
def test_disassociate_host(self): | |||
response = self._do_post('os-networks/1/action', | |||
'network-disassociate-host-req', | |||
{}) | |||
self.assertEqual(202, response.status_code) | |||
self.assertEqual("", response.text) | |||
self.api.api_post('os-networks/1/action', | |||
{'disassociate_host': None}, | |||
check_response_status=[410]) | |||
def test_disassociate_project(self): | |||
response = self._do_post('os-networks/1/action', | |||
'network-disassociate-project-req', | |||
{}) | |||
self.assertEqual(202, response.status_code) | |||
self.assertEqual("", response.text) | |||
self.api.api_post('os-networks/1/action', | |||
{'disassociate_project': None}, | |||
check_response_status=[410]) | |||
def test_associate_host(self): | |||
response = self._do_post('os-networks/1/action', | |||
'network-associate-host-req', | |||
{"host": "testHost"}) | |||
self.assertEqual(202, response.status_code) | |||
self.assertEqual("", response.text) | |||
self.api.api_post('os-networks/1/action', | |||
{'associate_host': 'foo'}, | |||
check_response_status=[410]) |
@@ -16,21 +16,15 @@ | |||
import copy | |||
import datetime | |||
import math | |||
import iso8601 | |||
import mock | |||
import netaddr | |||
from oslo_config import cfg | |||
from oslo_utils.fixture import uuidsentinel as uuids | |||
import webob | |||
from nova.api.openstack.compute import networks as networks_v21 | |||
from nova.api.openstack.compute import networks_associate \ | |||
as networks_associate_v21 | |||
import nova.context | |||
from nova import exception | |||
from nova.network import manager | |||
from nova.network.neutronv2 import api as neutron | |||
from nova import objects | |||
from nova import test | |||
@@ -121,48 +115,6 @@ class FakeNetworkAPI(object): | |||
def __init__(self): | |||
self.networks = copy.deepcopy(FAKE_NETWORKS) | |||
def delete(self, context, network_id): | |||
if network_id == 'always_delete': | |||
return True | |||
if network_id == -1: | |||
raise exception.NetworkInUse(network_id=network_id) | |||
for i, network in enumerate(self.networks): | |||
if network['id'] == network_id: | |||
del self.networks[0] | |||
return True | |||
raise exception.NetworkNotFoundForUUID(uuid=network_id) | |||
def disassociate(self, context, network_uuid): | |||
for network in self.networks: | |||
if network.get('uuid') == network_uuid: | |||
network['project_id'] = None | |||
return True | |||
raise exception.NetworkNotFound(network_id=network_uuid) | |||
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(network_id=network_uuid) | |||
def add_network_to_project(self, context, | |||
project_id, network_uuid=None): | |||
if network_uuid: | |||
for network in self.networks: | |||
if network.get('project_id', None) is None: | |||
network['project_id'] = project_id | |||
return | |||
return | |||
for network in self.networks: | |||
if network.get('uuid') == network_uuid: | |||
network['project_id'] = project_id | |||
return | |||
def get_all(self, context): | |||
return self._fake_db_network_get_all(context, project_only=True) | |||
@@ -199,146 +151,13 @@ class FakeNetworkAPI(object): | |||
network) | |||
raise exception.NetworkNotFound(network_id=network_id) | |||
def create(self, context, **kwargs): | |||
subnet_bits = int(math.ceil(math.log(kwargs.get( | |||
'network_size', CONF.network_size), 2))) | |||
fixed_net_v4 = netaddr.IPNetwork(kwargs['cidr']) | |||
prefixlen_v4 = 32 - subnet_bits | |||
subnets_v4 = list(fixed_net_v4.subnet( | |||
prefixlen_v4, | |||
count=kwargs.get('num_networks', CONF.num_networks))) | |||
new_networks = [] | |||
new_id = max((net['id'] for net in self.networks)) | |||
for index, subnet_v4 in enumerate(subnets_v4): | |||
new_id += 1 | |||
net = {'id': new_id, 'uuid': uuids.fake} | |||
net['cidr'] = str(subnet_v4) | |||
net['netmask'] = str(subnet_v4.netmask) | |||
net['gateway'] = kwargs.get('gateway') or str(subnet_v4[1]) | |||
net['broadcast'] = str(subnet_v4.broadcast) | |||
net['dhcp_start'] = str(subnet_v4[2]) | |||
for key in FAKE_NETWORKS[0]: | |||
net.setdefault(key, kwargs.get(key)) | |||
new_networks.append(net) | |||
self.networks += new_networks | |||
return new_networks | |||
# NOTE(vish): tests that network create Exceptions actually return | |||
# the proper error responses | |||
class NetworkCreateExceptionsTestV21(test.TestCase): | |||
validation_error = exception.ValidationError | |||
class PassthroughAPI(object): | |||
def __init__(self): | |||
self.network_manager = manager.FlatDHCPManager() | |||
def create(self, *args, **kwargs): | |||
if kwargs['label'] == 'fail_NetworkNotCreated': | |||
raise exception.NetworkNotCreated(req='fake_fail') | |||
return self.network_manager.create_networks(*args, **kwargs) | |||
def setUp(self): | |||
super(NetworkCreateExceptionsTestV21, self).setUp() | |||
self._setup() | |||
fakes.stub_out_networking(self) | |||
self.new_network = copy.deepcopy(NEW_NETWORK) | |||
def _setup(self): | |||
self.req = fakes.HTTPRequest.blank('') | |||
self.controller = networks_v21.NetworkController(self.PassthroughAPI()) | |||
def test_network_create_bad_vlan(self): | |||
self.new_network['network']['vlan_start'] = 'foo' | |||
self.assertRaises(self.validation_error, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_no_cidr(self): | |||
del self.new_network['network']['cidr'] | |||
self.assertRaises(self.validation_error, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_no_label(self): | |||
del self.new_network['network']['label'] | |||
self.assertRaises(self.validation_error, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_label_too_long(self): | |||
self.new_network['network']['label'] = "x" * 256 | |||
self.assertRaises(self.validation_error, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_invalid_fixed_cidr(self): | |||
self.new_network['network']['fixed_cidr'] = 'foo' | |||
self.assertRaises(self.validation_error, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_invalid_start(self): | |||
self.new_network['network']['allowed_start'] = 'foo' | |||
self.assertRaises(self.validation_error, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_bad_cidr(self): | |||
self.new_network['network']['cidr'] = '128.0.0.0/900' | |||
self.assertRaises(self.validation_error, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_handle_network_not_created(self): | |||
self.new_network['network']['label'] = 'fail_NetworkNotCreated' | |||
self.assertRaises(webob.exc.HTTPBadRequest, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
@mock.patch.object(objects.NetworkList, 'get_all') | |||
def test_network_create_cidr_conflict(self, mock_get_all): | |||
def fake_get_all(context): | |||
ret = objects.NetworkList(context=context, objects=[]) | |||
net = objects.Network(cidr='10.0.0.0/23') | |||
ret.objects.append(net) | |||
return ret | |||
mock_get_all.side_effect = fake_get_all | |||
self.new_network['network']['cidr'] = '10.0.0.0/24' | |||
self.assertRaises(webob.exc.HTTPConflict, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
def test_network_create_vlan_conflict(self): | |||
@staticmethod | |||
def get_all(context): | |||
ret = objects.NetworkList(context=context, objects=[]) | |||
net = objects.Network(cidr='10.0.0.0/24', vlan=100) | |||
ret.objects.append(net) | |||
return ret | |||
def fake_create(context): | |||
raise exception.DuplicateVlan(vlan=100) | |||
self.stub_out('nova.objects.NetworkList.get_all', get_all) | |||
self.stub_out('nova.objects.Network.create', fake_create) | |||
self.new_network['network']['cidr'] = '20.0.0.0/24' | |||
self.assertRaises(webob.exc.HTTPConflict, | |||
self.controller.create, self.req, | |||
body=self.new_network) | |||
class NetworksTestV21(test.NoDBTestCase): | |||
validation_error = exception.ValidationError | |||
def setUp(self): | |||
super(NetworksTestV21, self).setUp() | |||
# TODO(stephenfin): Consider using then NeutronFixture here | |||
self.fake_network_api = FakeNetworkAPI() | |||
self._setup() | |||
fakes.stub_out_networking(self) | |||
@@ -370,18 +189,6 @@ class NetworksTestV21(test.NoDBTestCase): | |||
res_dict = self.controller.index(self.non_admin_req) | |||
self.assertEqual(res_dict, {'networks': []}) | |||
project_id = self.req.environ["nova.context"].project_id | |||
cxt = self.req.environ["nova.context"] | |||
uuid = FAKE_NETWORKS[0]['uuid'] | |||
self.fake_network_api.associate(context=cxt, | |||
network_uuid=uuid, | |||
project=project_id) | |||
res_dict = self.controller.index(self.non_admin_req) | |||
expected = [copy.deepcopy(FAKE_USER_NETWORKS[0])] | |||
for network in expected: | |||
self.network_uuid_to_id(network) | |||
self.assertEqual({'networks': expected}, res_dict) | |||
def test_network_list_all_as_admin(self): | |||
res_dict = self.controller.index(self.admin_req) | |||
expected = copy.deepcopy(FAKE_NETWORKS) | |||
@@ -389,19 +196,6 @@ class NetworksTestV21(test.NoDBTestCase): | |||
self.network_uuid_to_id(network) | |||
self.assertEqual({'networks': expected}, res_dict) | |||
def test_network_disassociate(self): | |||
uuid = FAKE_NETWORKS[0]['uuid'] | |||
disassociate = self.controller._disassociate_host_and_project | |||
res = disassociate(self.req, uuid, {'disassociate': None}) | |||
self._check_status(res, disassociate, 202) | |||
self.assertIsNone(self.fake_network_api.networks[0]['project_id']) | |||
self.assertIsNone(self.fake_network_api.networks[0]['host']) | |||
def test_network_disassociate_not_found(self): | |||
self.assertRaises(webob.exc.HTTPNotFound, | |||
self.controller._disassociate_host_and_project, | |||
self.req, 100, {'disassociate': None}) | |||
def test_network_get_as_user(self): | |||
uuid = FAKE_USER_NETWORKS[0]['uuid'] | |||
res_dict = self.controller.show(self.non_admin_req, uuid) | |||
@@ -420,200 +214,6 @@ class NetworksTestV21(test.NoDBTestCase): | |||
self.assertRaises(webob.exc.HTTPNotFound, | |||
self.controller.show, self.req, 100) | |||
def test_network_delete(self): | |||
delete_method = self.controller.delete | |||
res = delete_method(self.req, 1) | |||
self._check_status(res, delete_method, 202) | |||
def test_network_delete_not_found(self): | |||
self.assertRaises(webob.exc.HTTPNotFound, | |||
self.controller.delete, self.req, 100) | |||
def test_network_delete_in_use(self): | |||
self.assertRaises(webob.exc.HTTPConflict, | |||
self.controller.delete, self.req, -1) | |||
def test_network_add(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
add = self.controller.add | |||
res = add(self.req, body={'id': uuid}) | |||
self._check_status(res, add, 202) | |||
res_dict = self.controller.show(self.admin_req, | |||
uuid) | |||
self.assertEqual(res_dict['network']['project_id'], | |||
fakes.FAKE_PROJECT_ID) | |||
@mock.patch('nova.tests.unit.api.openstack.compute.test_networks.' | |||
'FakeNetworkAPI.add_network_to_project', | |||
side_effect=exception.NoMoreNetworks) | |||
def test_network_add_no_more_networks_fail(self, mock_add): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.add, | |||
self.req, body={'id': uuid}) | |||
@mock.patch('nova.tests.unit.api.openstack.compute.test_networks.' | |||
'FakeNetworkAPI.add_network_to_project', | |||
side_effect=exception. | |||
NetworkNotFoundForUUID(uuid=fakes.FAKE_PROJECT_ID)) | |||
def test_network_add_network_not_found_networks_fail(self, mock_add): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.add, | |||
self.req, body={'id': uuid}) | |||
def test_network_add_network_without_body(self): | |||
self.assertRaises(self.validation_error, self.controller.add, | |||
self.req, | |||
body=None) | |||
def test_network_add_network_with_invalid_id(self): | |||
self.assertRaises(exception.ValidationError, self.controller.add, | |||
self.req, | |||
body={'id': 123}) | |||
def test_network_add_network_with_extra_arg(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(exception.ValidationError, self.controller.add, | |||
self.req, | |||
body={'id': uuid, | |||
'extra_arg': 123}) | |||
def test_network_add_network_with_none_id(self): | |||
add = self.controller.add | |||
res = add(self.req, body={'id': None}) | |||
self._check_status(res, add, 202) | |||
def test_network_create(self): | |||
res_dict = self.controller.create(self.req, body=self.new_network) | |||
self.assertIn('network', res_dict) | |||
uuid = res_dict['network']['id'] | |||
res_dict = self.controller.show(self.req, uuid) | |||
self.assertTrue(res_dict['network']['label']. | |||
startswith(NEW_NETWORK['network']['label'])) | |||
def test_network_create_large(self): | |||
self.new_network['network']['cidr'] = '128.0.0.0/4' | |||
res_dict = self.controller.create(self.req, body=self.new_network) | |||
self.assertEqual(res_dict['network']['cidr'], | |||
self.new_network['network']['cidr']) | |||
def test_network_neutron_disassociate_not_implemented(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(webob.exc.HTTPNotImplemented, | |||
self.neutron_ctrl._disassociate_host_and_project, | |||
self.req, uuid, {'disassociate': None}) | |||
class NetworksAssociateTestV21(test.NoDBTestCase): | |||
def setUp(self): | |||
super(NetworksAssociateTestV21, self).setUp() | |||
self.fake_network_api = FakeNetworkAPI() | |||
self._setup() | |||
fakes.stub_out_networking(self) | |||
self.admin_req = fakes.HTTPRequest.blank('', use_admin_context=True) | |||
def _setup(self): | |||
self.controller = networks_v21.NetworkController(self.fake_network_api) | |||
self.associate_controller = networks_associate_v21\ | |||
.NetworkAssociateActionController(self.fake_network_api) | |||
self.neutron_assoc_ctrl = ( | |||
networks_associate_v21.NetworkAssociateActionController( | |||
neutron.API())) | |||
self.req = fakes.HTTPRequest.blank('') | |||
def _check_status(self, res, method, code): | |||
self.assertEqual(method.wsgi_code, code) | |||
def test_network_disassociate_host_only(self): | |||
uuid = FAKE_NETWORKS[0]['uuid'] | |||
disassociate = self.associate_controller._disassociate_host_only | |||
res = disassociate( | |||
self.req, uuid, {'disassociate_host': None}) | |||
self._check_status(res, | |||
disassociate, | |||
202) | |||
self.assertIsNotNone(self.fake_network_api.networks[0]['project_id']) | |||
self.assertIsNone(self.fake_network_api.networks[0]['host']) | |||
def test_network_disassociate_project_only(self): | |||
uuid = FAKE_NETWORKS[0]['uuid'] | |||
disassociate = self.associate_controller._disassociate_project_only | |||
res = disassociate(self.req, uuid, {'disassociate_project': None}) | |||
self._check_status(res, disassociate, 202) | |||
self.assertIsNone(self.fake_network_api.networks[0]['project_id']) | |||
self.assertIsNotNone(self.fake_network_api.networks[0]['host']) | |||
def test_network_disassociate_project_network_delete(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
disassociate = self.associate_controller._disassociate_project_only | |||
res = disassociate( | |||
self.req, uuid, {'disassociate_project': None}) | |||
self._check_status(res, disassociate, 202) | |||
self.assertIsNone(self.fake_network_api.networks[1]['project_id']) | |||
delete = self.controller.delete | |||
res = delete(self.req, 1) | |||
# NOTE: On v2.1 code, delete method doesn't return anything and | |||
# the status code is decorated on wsgi_code of the method. | |||
self.assertIsNone(res) | |||
self.assertEqual(202, delete.wsgi_code) | |||
def test_network_associate_project_delete_fail(self): | |||
uuid = FAKE_NETWORKS[0]['uuid'] | |||
req = fakes.HTTPRequest.blank( | |||
'/v2/%s/os-networks/%s/action' % (fakes.FAKE_PROJECT_ID, uuid)) | |||
self.assertRaises(webob.exc.HTTPConflict, | |||
self.controller.delete, req, -1) | |||
def test_network_associate_with_host(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
associate = self.associate_controller._associate_host | |||
res = associate(self.req, uuid, body={'associate_host': "TestHost"}) | |||
self._check_status(res, associate, 202) | |||
res_dict = self.controller.show(self.admin_req, uuid) | |||
self.assertEqual(res_dict['network']['host'], 'TestHost') | |||
def test_network_neutron_associate_not_implemented(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(webob.exc.HTTPNotImplemented, | |||
self.neutron_assoc_ctrl._associate_host, | |||
self.req, uuid, body={'associate_host': "TestHost"}) | |||
def _test_network_neutron_associate_host_validation_failed(self, body): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(exception.ValidationError, | |||
self.associate_controller._associate_host, | |||
self.req, uuid, body=body) | |||
def test_network_neutron_associate_host_non_string(self): | |||
self._test_network_neutron_associate_host_validation_failed( | |||
{'associate_host': 123}) | |||
def test_network_neutron_associate_host_empty_body(self): | |||
self._test_network_neutron_associate_host_validation_failed({}) | |||
def test_network_neutron_associate_bad_associate_host_key(self): | |||
self._test_network_neutron_associate_host_validation_failed( | |||
{'badassociate_host': "TestHost"}) | |||
def test_network_neutron_associate_host_extra_arg(self): | |||
self._test_network_neutron_associate_host_validation_failed( | |||
{'associate_host': "TestHost", | |||
'extra_arg': "extra_arg"}) | |||
def test_network_neutron_disassociate_project_not_implemented(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(webob.exc.HTTPNotImplemented, | |||
self.neutron_assoc_ctrl._disassociate_project_only, | |||
self.req, uuid, {'disassociate_project': None}) | |||
def test_network_neutron_disassociate_host_not_implemented(self): | |||
uuid = FAKE_NETWORKS[1]['uuid'] | |||
self.assertRaises(webob.exc.HTTPNotImplemented, | |||
self.neutron_assoc_ctrl._disassociate_host_only, | |||
self.req, uuid, {'disassociate_host': None}) | |||
class NetworksEnforcementV21(test.NoDBTestCase): | |||
@@ -642,90 +242,6 @@ class NetworksEnforcementV21(test.NoDBTestCase): | |||
"Policy doesn't allow %s to be performed." % rule_name, | |||
exc.format_message()) | |||
def test_create_policy_failed(self): | |||
rule_name = 'os_compute_api:os-networks' | |||
self.policy.set_rules({rule_name: "project:non_fake"}) | |||
exc = self.assertRaises( | |||
exception.PolicyNotAuthorized, | |||
self.controller.create, self.req, body=NEW_NETWORK) | |||
self.assertEqual( | |||
"Policy doesn't allow %s to be performed." % rule_name, | |||
exc.format_message()) | |||
def test_delete_policy_failed(self): | |||
rule_name = 'os_compute_api:os-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_add_policy_failed(self): | |||
rule_name = 'os_compute_api:os-networks' | |||
self.policy.set_rules({rule_name: "project:non_fake"}) | |||
exc = self.assertRaises( | |||
exception.PolicyNotAuthorized, | |||
self.controller.add, self.req, | |||
body={'id': fakes.FAKE_UUID}) | |||
self.assertEqual( | |||
"Policy doesn't allow %s to be performed." % rule_name, | |||
exc.format_message()) | |||
def test_disassociate_policy_failed(self): | |||
rule_name = 'os_compute_api:os-networks' | |||
self.policy.set_rules({rule_name: "project:non_fake"}) | |||
exc = self.assertRaises( | |||
exception.PolicyNotAuthorized, | |||
self.controller._disassociate_host_and_project, | |||
self.req, fakes.FAKE_UUID, body={'network': {}}) | |||
self.assertEqual( | |||
"Policy doesn't allow %s to be performed." % rule_name, | |||
exc.format_message()) | |||
class NetworksAssociateEnforcementV21(test.NoDBTestCase): | |||
def setUp(self): | |||
super(NetworksAssociateEnforcementV21, self).setUp() | |||
self.controller = (networks_associate_v21. | |||
NetworkAssociateActionController()) | |||
self.req = fakes.HTTPRequest.blank('') | |||
def test_disassociate_host_policy_failed(self): | |||
rule_name = 'os_compute_api:os-networks-associate' | |||
self.policy.set_rules({rule_name: "project:non_fake"}) | |||
exc = self.assertRaises( | |||
exception.PolicyNotAuthorized, | |||
self.controller._disassociate_host_only, | |||
self.req, fakes.FAKE_UUID, body={'disassociate_host': {}}) | |||
self.assertEqual( | |||
"Policy doesn't allow %s to be performed." % rule_name, | |||
exc.format_message()) | |||
def test_disassociate_project_only_policy_failed(self): | |||
rule_name = 'os_compute_api:os-networks-associate' | |||
self.policy.set_rules({rule_name: "project:non_fake"}) | |||
exc = self.assertRaises( | |||
exception.PolicyNotAuthorized, | |||
self.controller._disassociate_project_only, | |||
self.req, fakes.FAKE_UUID, body={'disassociate_project': {}}) | |||
self.assertEqual( | |||
"Policy doesn't allow %s to be performed." % rule_name, | |||
exc.format_message()) | |||
def test_disassociate_host_only_policy_failed(self): | |||
rule_name = 'os_compute_api:os-networks-associate' | |||
self.policy.set_rules({rule_name: "project:non_fake"}) | |||
exc = self.assertRaises( | |||
exception.PolicyNotAuthorized, | |||
self.controller._associate_host, | |||
self.req, fakes.FAKE_UUID, body={'associate_host': 'fake_host'}) | |||
self.assertEqual( | |||
"Policy doesn't allow %s to be performed." % rule_name, | |||
exc.format_message()) | |||
class NetworksDeprecationTest(test.NoDBTestCase): | |||
@@ -737,32 +253,5 @@ class NetworksDeprecationTest(test.NoDBTestCase): | |||
def test_all_api_return_not_found(self): | |||
self.assertRaises(exception.VersionNotFoundForAPIMethod, | |||
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.index, self.req) | |||
self.assertRaises(exception.VersionNotFoundForAPIMethod, | |||
self.controller._disassociate_host_and_project, self.req, {}) | |||
self.assertRaises(exception.VersionNotFoundForAPIMethod, | |||
self.controller.add, self.req, {}) | |||
self.assertRaises(exception.VersionNotFoundForAPIMethod, | |||
self.controller.create, self.req, {}) | |||
class NetworksAssociateDeprecationTest(test.NoDBTestCase): | |||
def setUp(self): | |||
super(NetworksAssociateDeprecationTest, self).setUp() | |||
self.controller = networks_associate_v21\ | |||
.NetworkAssociateActionController() | |||
self.req = fakes.HTTPRequest.blank('', version='2.36') | |||
def test_all_api_return_not_found(self): | |||
self.assertRaises(exception.VersionNotFoundForAPIMethod, | |||
self.controller._associate_host, self.req, fakes.FAKE_UUID, {}) | |||
self.assertRaises(exception.VersionNotFoundForAPIMethod, | |||
self.controller._disassociate_project_only, self.req, | |||
fakes.FAKE_UUID, {}) | |||
self.assertRaises(exception.VersionNotFoundForAPIMethod, | |||
self.controller._disassociate_host_only, self.req, | |||
fakes.FAKE_UUID, {}) |
@@ -57,9 +57,7 @@ policy_data = """ | |||
"os_compute_api:os-migrate-server:migrate": "", | |||
"os_compute_api:os-migrate-server:migrate_live": "", | |||
"os_compute_api:os-multinic": "", | |||
"os_compute_api:os-networks": "", | |||
"os_compute_api:os-networks:view": "", | |||
"os_compute_api:os-networks-associate": "", | |||
"os_compute_api:os-tenant-networks": "", | |||
"os_compute_api:os-pause-server:pause": "", | |||
"os_compute_api:os-pause-server:unpause": "", | |||
@@ -346,8 +346,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase): | |||
"os_compute_api:os-lock-server:unlock:unlock_override", | |||
"os_compute_api:os-migrate-server:migrate", | |||
"os_compute_api:os-migrate-server:migrate_live", | |||
"os_compute_api:os-networks", | |||
"os_compute_api:os-networks-associate", | |||
"os_compute_api:os-quota-sets:update", | |||
"os_compute_api:os-quota-sets:delete", | |||
"os_compute_api:os-server-diagnostics", | |||
@@ -10,7 +10,16 @@ upgrade: | |||
* ``POST /os-security-group-default-rules`` | |||
* ``GET /os-security-group-default-rules/{id}`` | |||
* ``DELETE /os-security-group-default-rules/{id}`` | |||
* ``POST /os-networks`` | |||
* ``DELETE /os-networks`` | |||
* ``POST /os-networks/add`` | |||
* ``POST /os-networks/{id} (associate_host)`` | |||
* ``POST /os-networks/{id} (disassociate)`` | |||
* ``POST /os-networks/{id} (disassociate_host)`` | |||
* ``POST /os-networks/{id} (disassociate_project)`` | |||
The following policies have also been removed. | |||
* ``os_compute_api:os-security-group-default-rules`` | |||
* ``os_compute_api:os-networks`` | |||
* ``os_compute_api:os-networks-associate`` |