Merge "Remove (most) '/os-networks' REST APIs"
This commit is contained in:
commit
991d675675
|
@ -1,28 +1,26 @@
|
||||||
.. -*- rst -*-
|
.. -*- 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)
|
Networks (os-networks) (DEPRECATED)
|
||||||
=====================================
|
======================================
|
||||||
|
|
||||||
.. warning:: The networks API was designed to work with
|
.. warning::
|
||||||
``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>`__.
|
|
||||||
|
|
||||||
|
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.
|
Creates, lists, shows information for, and deletes networks.
|
||||||
|
|
||||||
Adds network to a project, disassociates a network from a project, and
|
Adds network to a project, disassociates a network from a project, and
|
||||||
disassociates a project from a network.
|
disassociates a project from a network.
|
||||||
|
|
||||||
|
|
||||||
Associates host with and disassociates host from a network.
|
Associates host with and disassociates host from a network.
|
||||||
|
|
||||||
List Networks
|
List Networks
|
||||||
|
@ -60,7 +58,8 @@ these permissions through the ``policy.json`` file.
|
||||||
|
|
||||||
Normal response codes: 200
|
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
|
Request
|
||||||
-------
|
-------
|
||||||
|
@ -91,7 +90,8 @@ this operation. Cloud providers can change these permissions through the
|
||||||
|
|
||||||
Normal response codes: 202
|
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
|
Request
|
||||||
-------
|
-------
|
||||||
|
@ -146,7 +146,8 @@ 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
|
||||||
-------
|
-------
|
||||||
|
@ -160,15 +161,11 @@ Response
|
||||||
|
|
||||||
There is no body content for the response of a successful DELETE query.
|
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
|
.. 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.
|
Associates a network with a host.
|
||||||
|
|
||||||
Specify the ``associate_host`` action in the request body.
|
Specify the ``associate_host`` action in the request body.
|
||||||
|
@ -179,7 +176,8 @@ permissions through the ``policy.json`` file.
|
||||||
|
|
||||||
Normal response codes: 202
|
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
|
Request
|
||||||
-------
|
-------
|
||||||
|
@ -199,15 +197,11 @@ Response
|
||||||
|
|
||||||
There is no body content for the response of a successful POST query.
|
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
|
.. 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.
|
Disassociates a network from a project. You can then reuse the network.
|
||||||
|
|
||||||
Specify the ``disassociate`` action in the request body.
|
Specify the ``disassociate`` action in the request body.
|
||||||
|
@ -218,7 +212,8 @@ these permissions through the ``policy.json`` file.
|
||||||
|
|
||||||
Normal response codes: 202
|
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
|
Request
|
||||||
-------
|
-------
|
||||||
|
@ -237,15 +232,11 @@ Response
|
||||||
|
|
||||||
There is no body content for the response of a successful POST query.
|
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
|
.. 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.
|
Disassociates a host from a network.
|
||||||
|
|
||||||
Specify the ``disassociate_host`` action in the request body.
|
Specify the ``disassociate_host`` action in the request body.
|
||||||
|
@ -256,7 +247,8 @@ these permissions through the ``policy.json`` file.
|
||||||
|
|
||||||
Normal response codes: 202
|
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
|
Request
|
||||||
-------
|
-------
|
||||||
|
@ -276,15 +268,11 @@ Response
|
||||||
There is no body content for the response of a successful POST query.
|
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
|
.. 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.
|
Disassociates a project from a network.
|
||||||
|
|
||||||
Specify the ``disassociate_project`` action in the request body.
|
Specify the ``disassociate_project`` action in the request body.
|
||||||
|
@ -295,7 +283,8 @@ these permissions through the ``policy.json`` file.
|
||||||
|
|
||||||
Normal response codes: 202
|
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
|
Request
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
{
|
{
|
||||||
"network": {
|
"network": {
|
||||||
"bridge": "br100",
|
"bridge": null,
|
||||||
"bridge_interface": "eth0",
|
"bridge_interface": null,
|
||||||
"broadcast": "10.0.0.7",
|
"broadcast": null,
|
||||||
"cidr": "10.0.0.0/29",
|
"cidr": null,
|
||||||
"cidr_v6": null,
|
"cidr_v6": null,
|
||||||
"created_at": "2011-08-15T06:19:19.387525",
|
"created_at": null,
|
||||||
"deleted": false,
|
"deleted": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"dhcp_server": "10.0.0.1",
|
"dhcp_server": null,
|
||||||
"dhcp_start": "10.0.0.3",
|
"dhcp_start": null,
|
||||||
"dns1": null,
|
"dns1": null,
|
||||||
"dns2": null,
|
"dns2": null,
|
||||||
"enable_dhcp": true,
|
"enable_dhcp": null,
|
||||||
"gateway": "10.0.0.1",
|
"gateway": null,
|
||||||
"gateway_v6": null,
|
"gateway_v6": null,
|
||||||
"host": "nsokolov-desktop",
|
"host": null,
|
||||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
|
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||||
"injected": false,
|
"injected": null,
|
||||||
"label": "mynet_0",
|
"label": "private",
|
||||||
"mtu": null,
|
"mtu": null,
|
||||||
"multi_host": false,
|
"multi_host": null,
|
||||||
"netmask": "255.255.255.248",
|
"netmask": null,
|
||||||
"netmask_v6": null,
|
"netmask_v6": null,
|
||||||
"priority": null,
|
"priority": null,
|
||||||
"project_id": "6133f8b603924f45bc0c9e21f6df12fa",
|
"project_id": null,
|
||||||
"rxtx_base": null,
|
"rxtx_base": null,
|
||||||
"share_address": false,
|
"share_address": null,
|
||||||
"updated_at": "2011-08-16T09:26:13.048257",
|
"updated_at": null,
|
||||||
"vlan": 100,
|
"vlan": null,
|
||||||
"vpn_private_address": "10.0.0.2",
|
"vpn_private_address": null,
|
||||||
"vpn_public_address": "127.0.0.1",
|
"vpn_public_address": null,
|
||||||
"vpn_public_port": 1000
|
"vpn_public_port": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +1,38 @@
|
||||||
{
|
{
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
"bridge": "br100",
|
"bridge": null,
|
||||||
"bridge_interface": "eth0",
|
"bridge_interface": null,
|
||||||
"broadcast": "10.0.0.7",
|
"broadcast": null,
|
||||||
"cidr": "10.0.0.0/29",
|
"cidr": null,
|
||||||
"cidr_v6": null,
|
"cidr_v6": null,
|
||||||
"created_at": "2011-08-15T06:19:19.387525",
|
"created_at": null,
|
||||||
"deleted": false,
|
"deleted": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"dhcp_server": "10.0.0.1",
|
"dhcp_server": null,
|
||||||
"dhcp_start": "10.0.0.3",
|
"dhcp_start": null,
|
||||||
"dns1": null,
|
"dns1": null,
|
||||||
"dns2": null,
|
"dns2": null,
|
||||||
"enable_dhcp": true,
|
"enable_dhcp": null,
|
||||||
"gateway": "10.0.0.1",
|
"gateway": null,
|
||||||
"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",
|
|
||||||
"gateway_v6": null,
|
"gateway_v6": null,
|
||||||
"host": null,
|
"host": null,
|
||||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
|
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||||
"injected": false,
|
"injected": null,
|
||||||
"label": "mynet_1",
|
"label": "private",
|
||||||
"mtu": null,
|
"mtu": null,
|
||||||
"multi_host": false,
|
"multi_host": null,
|
||||||
"netmask": "255.255.255.248",
|
"netmask": null,
|
||||||
"netmask_v6": null,
|
"netmask_v6": null,
|
||||||
"priority": null,
|
"priority": null,
|
||||||
"project_id": null,
|
"project_id": null,
|
||||||
"rxtx_base": null,
|
"rxtx_base": null,
|
||||||
"share_address": false,
|
"share_address": null,
|
||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"vlan": 101,
|
"vlan": null,
|
||||||
"vpn_private_address": "10.0.0.10",
|
"vpn_private_address": null,
|
||||||
"vpn_public_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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import netaddr
|
|
||||||
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 import common
|
|
||||||
from nova.api.openstack.compute.schemas import networks as schema
|
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api import validation
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import network
|
from nova import network
|
||||||
|
@ -92,22 +88,6 @@ class NetworkController(wsgi.Controller):
|
||||||
result = [network_dict(context, net_ref) for net_ref in networks]
|
result = [network_dict(context, net_ref) for net_ref in networks]
|
||||||
return {'networks': result}
|
return {'networks': result}
|
||||||
|
|
||||||
@wsgi.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.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
||||||
@wsgi.expected_errors(404)
|
@wsgi.expected_errors(404)
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
|
@ -121,63 +101,19 @@ class NetworkController(wsgi.Controller):
|
||||||
raise exc.HTTPNotFound(explanation=msg)
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
return {'network': network_dict(context, network)}
|
return {'network': network_dict(context, network)}
|
||||||
|
|
||||||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
@wsgi.expected_errors(410)
|
||||||
@wsgi.response(202)
|
@wsgi.action("disassociate")
|
||||||
@wsgi.expected_errors((404, 409))
|
def _disassociate_host_and_project(self, req, id, body):
|
||||||
|
raise exc.HTTPGone()
|
||||||
|
|
||||||
|
@wsgi.expected_errors(410)
|
||||||
def delete(self, req, id):
|
def delete(self, req, id):
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
context.can(net_policies.BASE_POLICY_NAME)
|
|
||||||
|
|
||||||
try:
|
@wsgi.expected_errors(410)
|
||||||
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.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
|
||||||
@wsgi.expected_errors((400, 409, 501))
|
|
||||||
@validation.schema(schema.create)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
context.can(net_policies.BASE_POLICY_NAME)
|
|
||||||
|
|
||||||
params = body["network"]
|
@wsgi.expected_errors(410)
|
||||||
|
|
||||||
cidr = params.get("cidr") or params.get("cidr_v6")
|
|
||||||
|
|
||||||
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)
|
|
||||||
def add(self, req, body):
|
def add(self, req, body):
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
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())
|
|
||||||
|
|
|
@ -12,70 +12,23 @@
|
||||||
|
|
||||||
from webob import exc
|
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.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):
|
class NetworkAssociateActionController(wsgi.Controller):
|
||||||
"""Network Association API 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.action("disassociate_host")
|
||||||
@wsgi.response(202)
|
@wsgi.expected_errors(410)
|
||||||
@wsgi.expected_errors((404, 501))
|
|
||||||
def _disassociate_host_only(self, req, id, body):
|
def _disassociate_host_only(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
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()
|
|
||||||
|
|
||||||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
|
||||||
@wsgi.action("disassociate_project")
|
@wsgi.action("disassociate_project")
|
||||||
@wsgi.response(202)
|
@wsgi.expected_errors(410)
|
||||||
@wsgi.expected_errors((404, 501))
|
|
||||||
def _disassociate_project_only(self, req, id, body):
|
def _disassociate_project_only(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
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()
|
|
||||||
|
|
||||||
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
|
||||||
@wsgi.action("associate_host")
|
@wsgi.action("associate_host")
|
||||||
@wsgi.response(202)
|
@wsgi.expected_errors(410)
|
||||||
@wsgi.expected_errors((404, 501))
|
|
||||||
@validation.schema(networks_associate.associate_host)
|
|
||||||
def _associate_host(self, req, id, body):
|
def _associate_host(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
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()
|
|
||||||
|
|
|
@ -435,6 +435,12 @@ API endpoints as below::
|
||||||
21.0.0 (Ussuri) release. On deployments newer than this, the APIs will
|
21.0.0 (Ussuri) release. On deployments newer than this, the APIs 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-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,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 migrations
|
||||||
from nova.policies import multinic
|
from nova.policies import multinic
|
||||||
from nova.policies import networks
|
from nova.policies import networks
|
||||||
from nova.policies import networks_associate
|
|
||||||
from nova.policies import pause_server
|
from nova.policies import pause_server
|
||||||
from nova.policies import quota_class_sets
|
from nova.policies import quota_class_sets
|
||||||
from nova.policies import quota_sets
|
from nova.policies import quota_sets
|
||||||
|
@ -109,7 +108,6 @@ def list_rules():
|
||||||
migrations.list_rules(),
|
migrations.list_rules(),
|
||||||
multinic.list_rules(),
|
multinic.list_rules(),
|
||||||
networks.list_rules(),
|
networks.list_rules(),
|
||||||
networks_associate.list_rules(),
|
|
||||||
pause_server.list_rules(),
|
pause_server.list_rules(),
|
||||||
quota_class_sets.list_rules(),
|
quota_class_sets.list_rules(),
|
||||||
quota_sets.list_rules(),
|
quota_sets.list_rules(),
|
||||||
|
|
|
@ -18,36 +18,10 @@ from oslo_policy import policy
|
||||||
from nova.policies import base
|
from nova.policies import base
|
||||||
|
|
||||||
|
|
||||||
BASE_POLICY_NAME = 'os_compute_api:os-networks'
|
|
||||||
POLICY_ROOT = 'os_compute_api:os-networks:%s'
|
POLICY_ROOT = 'os_compute_api:os-networks:%s'
|
||||||
|
|
||||||
|
|
||||||
networks_policies = [
|
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.DocumentedRuleDefault(
|
||||||
POLICY_ROOT % 'view',
|
POLICY_ROOT % 'view',
|
||||||
base.RULE_ADMIN_OR_OWNER,
|
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":
|
"network":
|
||||||
{
|
{
|
||||||
"bridge": "br100",
|
"bridge": null,
|
||||||
"bridge_interface": "eth0",
|
"bridge_interface": null,
|
||||||
"broadcast": "%(ip)s",
|
"broadcast": null,
|
||||||
"cidr": "10.0.0.0/29",
|
"cidr": null,
|
||||||
"cidr_v6": null,
|
"cidr_v6": null,
|
||||||
"created_at": "%(strtime)s",
|
"created_at": null,
|
||||||
"deleted": false,
|
"deleted": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"dhcp_start": "%(ip)s",
|
"dhcp_start": null,
|
||||||
"dns1": null,
|
"dns1": null,
|
||||||
"dns2": null,
|
"dns2": null,
|
||||||
"gateway": "%(ip)s",
|
"gateway": null,
|
||||||
"gateway_v6": null,
|
"gateway_v6": null,
|
||||||
"host": "nsokolov-desktop",
|
"host": null,
|
||||||
"id": "%(id)s",
|
"id": "%(id)s",
|
||||||
"injected": false,
|
"injected": null,
|
||||||
"label": "mynet_0",
|
"label": "private",
|
||||||
"multi_host": false,
|
"multi_host": null,
|
||||||
"netmask": "%(ip)s",
|
"netmask": null,
|
||||||
"netmask_v6": null,
|
"netmask_v6": null,
|
||||||
"priority": null,
|
"priority": null,
|
||||||
"project_id": "6133f8b603924f45bc0c9e21f6df12fa",
|
"project_id": null,
|
||||||
"rxtx_base": null,
|
"rxtx_base": null,
|
||||||
"updated_at": "%(strtime)s",
|
"updated_at": null,
|
||||||
"vlan": 100,
|
"vlan": null,
|
||||||
"vpn_private_address": "%(ip)s",
|
"vpn_private_address": null,
|
||||||
"vpn_public_address": "%(ip)s",
|
"vpn_public_address": null,
|
||||||
"vpn_public_port": 1000,
|
"vpn_public_port": null,
|
||||||
"mtu": null,
|
"mtu": null,
|
||||||
"dhcp_server": "%(ip)s",
|
"dhcp_server": null,
|
||||||
"enable_dhcp": true,
|
"enable_dhcp": null,
|
||||||
"share_address": false
|
"share_address": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +1,38 @@
|
||||||
{
|
{
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
"bridge": "br100",
|
"bridge": null,
|
||||||
"bridge_interface": "eth0",
|
"bridge_interface": null,
|
||||||
"broadcast": "%(ip)s",
|
"broadcast": null,
|
||||||
"cidr": "10.0.0.0/29",
|
"cidr": null,
|
||||||
"cidr_v6": null,
|
"cidr_v6": null,
|
||||||
"created_at": "%(strtime)s",
|
"created_at": null,
|
||||||
"deleted": false,
|
"deleted": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"dhcp_start": "%(ip)s",
|
"dhcp_start": null,
|
||||||
"dns1": null,
|
"dns1": null,
|
||||||
"dns2": null,
|
"dns2": null,
|
||||||
"gateway": "%(ip)s",
|
"gateway": null,
|
||||||
"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_v6": null,
|
"gateway_v6": null,
|
||||||
"host": null,
|
"host": null,
|
||||||
"id": "%(id)s",
|
"id": "%(id)s",
|
||||||
"injected": false,
|
"injected": null,
|
||||||
"label": "mynet_1",
|
"label": "private",
|
||||||
"multi_host": false,
|
"multi_host": null,
|
||||||
"netmask": "%(ip)s",
|
"netmask": null,
|
||||||
"netmask_v6": null,
|
"netmask_v6": null,
|
||||||
"priority": null,
|
"priority": null,
|
||||||
"project_id": null,
|
"project_id": null,
|
||||||
"rxtx_base": null,
|
"rxtx_base": null,
|
||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"vlan": 101,
|
"vlan": null,
|
||||||
"vpn_private_address": "%(ip)s",
|
"vpn_private_address": null,
|
||||||
"vpn_public_address": null,
|
"vpn_public_address": null,
|
||||||
"vpn_public_port": 1001,
|
"vpn_public_port": null,
|
||||||
"mtu": null,
|
"mtu": null,
|
||||||
"dhcp_server": "%(ip)s",
|
"dhcp_server": null,
|
||||||
"enable_dhcp": true,
|
"enable_dhcp": null,
|
||||||
"share_address": false
|
"share_address": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,91 +16,38 @@
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from nova import exception
|
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.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):
|
class NetworksJsonTests(api_sample_base.ApiSampleTestBaseV21):
|
||||||
USE_NEUTRON = False # partially nova-net only
|
|
||||||
ADMIN_API = True
|
ADMIN_API = True
|
||||||
sample_dir = "os-networks"
|
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'))
|
|
||||||
|
|
||||||
# TODO(stephenfin): Rework this to work with neutron
|
|
||||||
def test_network_list(self):
|
def test_network_list(self):
|
||||||
response = self._do_get('os-networks')
|
response = self._do_get('os-networks')
|
||||||
self._verify_response('networks-list-resp', {}, response, 200)
|
self._verify_response('networks-list-resp', {}, response, 200)
|
||||||
|
|
||||||
# TODO(stephenfin): Rework this to work with neutron
|
|
||||||
def test_network_show(self):
|
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)
|
response = self._do_get('os-networks/%s' % uuid)
|
||||||
self._verify_response('network-show-resp', {}, response, 200)
|
self._verify_response('network-show-resp', {}, response, 200)
|
||||||
|
|
||||||
# TODO(stephenfin): Rework this to work with neutron
|
@mock.patch('nova.network.neutronv2.api.API.get',
|
||||||
@mock.patch('nova.network.api.API.get', side_effect=exception.Unauthorized)
|
side_effect=exception.Unauthorized)
|
||||||
def test_network_show_token_expired(self, mock_get):
|
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)
|
response = self._do_get('os-networks/%s' % uuid)
|
||||||
self.assertEqual(401, response.status_code)
|
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):
|
def test_network_create(self):
|
||||||
response = self._do_post("os-networks",
|
self.api.api_post('os-networks', {},
|
||||||
'network-create-req', {})
|
check_response_status=[410])
|
||||||
self._verify_response('network-create-resp', {}, response, 200)
|
|
||||||
|
|
||||||
# TODO(stephenfin): Remove this API since it's nova-network only
|
|
||||||
def test_network_add(self):
|
def test_network_add(self):
|
||||||
response = self._do_post("os-networks/add",
|
self.api.api_post('os-networks/add', {},
|
||||||
'network-add-req', {})
|
check_response_status=[410])
|
||||||
self.assertEqual(202, response.status_code)
|
|
||||||
self.assertEqual("", response.text)
|
|
||||||
|
|
||||||
# TODO(stephenfin): Remove this API since it's nova-network only
|
|
||||||
def test_network_delete(self):
|
def test_network_delete(self):
|
||||||
response = self._do_delete('os-networks/always_delete')
|
self.api.api_delete('os-networks/always-delete',
|
||||||
self.assertEqual(202, response.status_code)
|
check_response_status=[410])
|
||||||
self.assertEqual("", response.text)
|
|
||||||
|
|
|
@ -16,48 +16,25 @@
|
||||||
from nova.tests.functional.api_sample_tests import api_sample_base
|
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):
|
class NetworksAssociateJsonTests(api_sample_base.ApiSampleTestBaseV21):
|
||||||
USE_NEUTRON = False # nova-net only
|
|
||||||
ADMIN_API = True
|
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):
|
def test_disassociate(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
self.api.api_post('os-networks/1/action',
|
||||||
'network-disassociate-req',
|
{'disassociate': None},
|
||||||
{})
|
check_response_status=[410])
|
||||||
self.assertEqual(202, response.status_code)
|
|
||||||
self.assertEqual("", response.text)
|
|
||||||
|
|
||||||
def test_disassociate_host(self):
|
def test_disassociate_host(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
self.api.api_post('os-networks/1/action',
|
||||||
'network-disassociate-host-req',
|
{'disassociate_host': None},
|
||||||
{})
|
check_response_status=[410])
|
||||||
self.assertEqual(202, response.status_code)
|
|
||||||
self.assertEqual("", response.text)
|
|
||||||
|
|
||||||
def test_disassociate_project(self):
|
def test_disassociate_project(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
self.api.api_post('os-networks/1/action',
|
||||||
'network-disassociate-project-req',
|
{'disassociate_project': None},
|
||||||
{})
|
check_response_status=[410])
|
||||||
self.assertEqual(202, response.status_code)
|
|
||||||
self.assertEqual("", response.text)
|
|
||||||
|
|
||||||
def test_associate_host(self):
|
def test_associate_host(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
self.api.api_post('os-networks/1/action',
|
||||||
'network-associate-host-req',
|
{'associate_host': 'foo'},
|
||||||
{"host": "testHost"})
|
check_response_status=[410])
|
||||||
self.assertEqual(202, response.status_code)
|
|
||||||
self.assertEqual("", response.text)
|
|
||||||
|
|
|
@ -16,21 +16,15 @@
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import math
|
|
||||||
|
|
||||||
import iso8601
|
import iso8601
|
||||||
import mock
|
|
||||||
import netaddr
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils.fixture import uuidsentinel as uuids
|
from oslo_utils.fixture import uuidsentinel as uuids
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute import networks as networks_v21
|
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
|
import nova.context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.network import manager
|
|
||||||
from nova.network.neutronv2 import api as neutron
|
from nova.network.neutronv2 import api as neutron
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova import test
|
from nova import test
|
||||||
|
@ -121,48 +115,6 @@ class FakeNetworkAPI(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.networks = copy.deepcopy(FAKE_NETWORKS)
|
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):
|
def get_all(self, context):
|
||||||
return self._fake_db_network_get_all(context, project_only=True)
|
return self._fake_db_network_get_all(context, project_only=True)
|
||||||
|
|
||||||
|
@ -199,146 +151,13 @@ class FakeNetworkAPI(object):
|
||||||
network)
|
network)
|
||||||
raise exception.NetworkNotFound(network_id=network_id)
|
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):
|
class NetworksTestV21(test.NoDBTestCase):
|
||||||
validation_error = exception.ValidationError
|
validation_error = exception.ValidationError
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(NetworksTestV21, self).setUp()
|
super(NetworksTestV21, self).setUp()
|
||||||
|
# TODO(stephenfin): Consider using then NeutronFixture here
|
||||||
self.fake_network_api = FakeNetworkAPI()
|
self.fake_network_api = FakeNetworkAPI()
|
||||||
self._setup()
|
self._setup()
|
||||||
fakes.stub_out_networking(self)
|
fakes.stub_out_networking(self)
|
||||||
|
@ -370,18 +189,6 @@ class NetworksTestV21(test.NoDBTestCase):
|
||||||
res_dict = self.controller.index(self.non_admin_req)
|
res_dict = self.controller.index(self.non_admin_req)
|
||||||
self.assertEqual(res_dict, {'networks': []})
|
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):
|
def test_network_list_all_as_admin(self):
|
||||||
res_dict = self.controller.index(self.admin_req)
|
res_dict = self.controller.index(self.admin_req)
|
||||||
expected = copy.deepcopy(FAKE_NETWORKS)
|
expected = copy.deepcopy(FAKE_NETWORKS)
|
||||||
|
@ -389,19 +196,6 @@ class NetworksTestV21(test.NoDBTestCase):
|
||||||
self.network_uuid_to_id(network)
|
self.network_uuid_to_id(network)
|
||||||
self.assertEqual({'networks': expected}, res_dict)
|
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):
|
def test_network_get_as_user(self):
|
||||||
uuid = FAKE_USER_NETWORKS[0]['uuid']
|
uuid = FAKE_USER_NETWORKS[0]['uuid']
|
||||||
res_dict = self.controller.show(self.non_admin_req, 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.assertRaises(webob.exc.HTTPNotFound,
|
||||||
self.controller.show, self.req, 100)
|
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):
|
class NetworksEnforcementV21(test.NoDBTestCase):
|
||||||
|
|
||||||
|
@ -642,90 +242,6 @@ class NetworksEnforcementV21(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_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):
|
class NetworksDeprecationTest(test.NoDBTestCase):
|
||||||
|
|
||||||
|
@ -737,32 +253,5 @@ class NetworksDeprecationTest(test.NoDBTestCase):
|
||||||
def test_all_api_return_not_found(self):
|
def test_all_api_return_not_found(self):
|
||||||
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.assertRaises(exception.VersionNotFoundForAPIMethod,
|
||||||
self.controller.index, self.req)
|
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": "",
|
||||||
"os_compute_api:os-migrate-server:migrate_live": "",
|
"os_compute_api:os-migrate-server:migrate_live": "",
|
||||||
"os_compute_api:os-multinic": "",
|
"os_compute_api:os-multinic": "",
|
||||||
"os_compute_api:os-networks": "",
|
|
||||||
"os_compute_api:os-networks:view": "",
|
"os_compute_api:os-networks:view": "",
|
||||||
"os_compute_api:os-networks-associate": "",
|
|
||||||
"os_compute_api:os-tenant-networks": "",
|
"os_compute_api:os-tenant-networks": "",
|
||||||
"os_compute_api:os-pause-server:pause": "",
|
"os_compute_api:os-pause-server:pause": "",
|
||||||
"os_compute_api:os-pause-server:unpause": "",
|
"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-lock-server:unlock:unlock_override",
|
||||||
"os_compute_api:os-migrate-server:migrate",
|
"os_compute_api:os-migrate-server:migrate",
|
||||||
"os_compute_api:os-migrate-server:migrate_live",
|
"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:update",
|
||||||
"os_compute_api:os-quota-sets:delete",
|
"os_compute_api:os-quota-sets:delete",
|
||||||
"os_compute_api:os-server-diagnostics",
|
"os_compute_api:os-server-diagnostics",
|
||||||
|
|
|
@ -10,7 +10,16 @@ upgrade:
|
||||||
* ``POST /os-security-group-default-rules``
|
* ``POST /os-security-group-default-rules``
|
||||||
* ``GET /os-security-group-default-rules/{id}``
|
* ``GET /os-security-group-default-rules/{id}``
|
||||||
* ``DELETE /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.
|
The following policies have also been removed.
|
||||||
|
|
||||||
* ``os_compute_api:os-security-group-default-rules``
|
* ``os_compute_api:os-security-group-default-rules``
|
||||||
|
* ``os_compute_api:os-networks``
|
||||||
|
* ``os_compute_api:os-networks-associate``
|
||||||
|
|
Loading…
Reference in New Issue