Implement If-Match support for Neutron resources

Neutron API supports using If-Match HTTP header to do compare-and-swap
updates and deletes of several resources [1]. This feature is based on
revision_number property.

This commit implements that by adding the if_revision argument to
supported update_* and delete_* resources.

[1] https://docs.openstack.org/api-ref/network/v2/?expanded=list-routers-detail#revisions

Change-Id: I5c4ff42796cc860c0a99a431cac84bb75a2d9236
This commit is contained in:
Michał Dulko 2020-02-26 14:07:19 +01:00
parent cc71171962
commit c2f2ffdd9f
18 changed files with 290 additions and 83 deletions

View File

@ -196,7 +196,7 @@ class Server(resource.Resource, metadata.MetadataMixin, resource.TagMixin):
vm_state = resource.Body('OS-EXT-STS:vm_state')
def _prepare_request(self, requires_id=True, prepend_key=True,
base_path=None):
base_path=None, **kwargs):
request = super(Server, self)._prepare_request(requires_id=requires_id,
prepend_key=prepend_key,
base_path=base_path)

View File

@ -35,7 +35,7 @@ class User(resource.Resource):
password = resource.Body('password')
def _prepare_request(self, requires_id=True, prepend_key=True,
base_path=None):
base_path=None, **kwargs):
"""Prepare a request for the database service's create call
User.create calls require the resources_key.

View File

@ -121,6 +121,11 @@ class ConflictException(HttpException):
pass
class PreconditionFailedException(HttpException):
"""HTTP 412 Precondition Failed."""
pass
class MethodNotSupported(SDKException):
"""The resource does not support this operation type."""
def __init__(self, resource, method):
@ -192,6 +197,8 @@ def raise_from_response(response, error_message=None):
cls = NotFoundException
elif response.status_code == 400:
cls = BadRequestException
elif response.status_code == 412:
cls = PreconditionFailedException
else:
cls = HttpException

View File

@ -285,7 +285,7 @@ class Image(resource.Resource, resource.TagMixin, _download.DownloadMixin):
session.post(url, json=json, headers=headers)
def _prepare_request(self, requires_id=None, prepend_key=False,
patch=False, base_path=None):
patch=False, base_path=None, **kwargs):
request = super(Image, self)._prepare_request(requires_id=requires_id,
prepend_key=prepend_key,
patch=patch,

View File

@ -42,7 +42,7 @@ class Quota(resource.Resource):
project_id = resource.Body('project_id', alternate_id=True)
def _prepare_request(self, requires_id=True,
base_path=None, prepend_key=False):
base_path=None, prepend_key=False, **kwargs):
_request = super(Quota, self)._prepare_request(requires_id,
prepend_key,
base_path=base_path)

View File

@ -0,0 +1,27 @@
# 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 openstack import resource
class NetworkResource(resource.Resource):
#: Revision number of the resource. *Type: int*
revision_number = resource.Body('revision_number', type=int)
def _prepare_request(self, requires_id=None, prepend_key=False,
patch=False, base_path=None, params=None,
if_revision=None, **kwargs):
req = super(NetworkResource, self)._prepare_request(
requires_id=requires_id, prepend_key=prepend_key, patch=patch,
base_path=base_path, params=params)
if if_revision is not None:
req.headers['If-Match'] = "revision_number=%d" % if_revision
return req

View File

@ -60,6 +60,26 @@ from openstack import proxy
class Proxy(proxy.Proxy):
@proxy._check_resource(strict=False)
def _update(self, resource_type, value, base_path=None,
if_revision=None, **attrs):
res = self._get_resource(resource_type, value, **attrs)
return res.commit(self, base_path=base_path, if_revision=if_revision)
@proxy._check_resource(strict=False)
def _delete(self, resource_type, value, ignore_missing=True,
if_revision=None, **attrs):
res = self._get_resource(resource_type, value, **attrs)
try:
rv = res.delete(self, if_revision=if_revision)
except exceptions.ResourceNotFound:
if ignore_missing:
return None
raise
return rv
def create_address_scope(self, **attrs):
"""Create a new address scope from attributes
@ -497,7 +517,7 @@ class Proxy(proxy.Proxy):
"""
return self._create(_floating_ip.FloatingIP, **attrs)
def delete_ip(self, floating_ip, ignore_missing=True):
def delete_ip(self, floating_ip, ignore_missing=True, if_revision=None):
"""Delete a floating ip
:param floating_ip: The value can be either the ID of a floating ip
@ -508,11 +528,13 @@ class Proxy(proxy.Proxy):
raised when the floating ip does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent ip.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:returns: ``None``
"""
self._delete(_floating_ip.FloatingIP, floating_ip,
ignore_missing=ignore_missing)
ignore_missing=ignore_missing, if_revision=if_revision)
def find_available_ip(self):
"""Find an available IP
@ -577,19 +599,22 @@ class Proxy(proxy.Proxy):
"""
return self._list(_floating_ip.FloatingIP, **query)
def update_ip(self, floating_ip, **attrs):
def update_ip(self, floating_ip, if_revision=None, **attrs):
"""Update a ip
:param floating_ip: Either the id of a ip or a
:class:`~openstack.network.v2.floating_ip.FloatingIP`
instance.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:param dict attrs: The attributes to update on the ip represented
by ``value``.
:returns: The updated ip
:rtype: :class:`~openstack.network.v2.floating_ip.FloatingIP`
"""
return self._update(_floating_ip.FloatingIP, floating_ip, **attrs)
return self._update(_floating_ip.FloatingIP, floating_ip,
if_revision=if_revision, **attrs)
def create_port_forwarding(self, **attrs):
"""Create a new floating ip port forwarding from attributes
@ -1203,7 +1228,7 @@ class Proxy(proxy.Proxy):
"""
return self._create(_network.Network, **attrs)
def delete_network(self, network, ignore_missing=True):
def delete_network(self, network, ignore_missing=True, if_revision=None):
"""Delete a network
:param network:
@ -1214,10 +1239,13 @@ class Proxy(proxy.Proxy):
raised when the network does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent network.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:returns: ``None``
"""
self._delete(_network.Network, network, ignore_missing=ignore_missing)
self._delete(_network.Network, network, ignore_missing=ignore_missing,
if_revision=if_revision)
def find_network(self, name_or_id, ignore_missing=True, **args):
"""Find a single network
@ -1276,18 +1304,21 @@ class Proxy(proxy.Proxy):
"""
return self._list(_network.Network, **query)
def update_network(self, network, **attrs):
def update_network(self, network, if_revision=None, **attrs):
"""Update a network
:param network: Either the id of a network or an instance of type
:class:`~openstack.network.v2.network.Network`.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:param dict attrs: The attributes to update on the network represented
by ``network``.
:returns: The updated network
:rtype: :class:`~openstack.network.v2.network.Network`
"""
return self._update(_network.Network, network, **attrs)
return self._update(_network.Network, network, if_revision=if_revision,
**attrs)
def find_network_ip_availability(self, name_or_id, ignore_missing=True,
**args):
@ -1699,7 +1730,7 @@ class Proxy(proxy.Proxy):
"""
return self._bulk_create(_port.Port, data)
def delete_port(self, port, ignore_missing=True):
def delete_port(self, port, ignore_missing=True, if_revision=None):
"""Delete a port
:param port: The value can be either the ID of a port or a
@ -1709,10 +1740,13 @@ class Proxy(proxy.Proxy):
raised when the port does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent port.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:returns: ``None``
"""
self._delete(_port.Port, port, ignore_missing=ignore_missing)
self._delete(_port.Port, port, ignore_missing=ignore_missing,
if_revision=if_revision)
def find_port(self, name_or_id, ignore_missing=True, **args):
"""Find a single port
@ -1766,18 +1800,21 @@ class Proxy(proxy.Proxy):
"""
return self._list(_port.Port, **query)
def update_port(self, port, **attrs):
def update_port(self, port, if_revision=None, **attrs):
"""Update a port
:param port: Either the id of a port or a
:class:`~openstack.network.v2.port.Port` instance.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:param dict attrs: The attributes to update on the port represented
by ``port``.
:returns: The updated port
:rtype: :class:`~openstack.network.v2.port.Port`
"""
return self._update(_port.Port, port, **attrs)
return self._update(_port.Port, port, if_revision=if_revision,
**attrs)
def add_ip_to_port(self, port, ip):
ip.port_id = port.id
@ -2482,7 +2519,7 @@ class Proxy(proxy.Proxy):
"""
return self._create(_router.Router, **attrs)
def delete_router(self, router, ignore_missing=True):
def delete_router(self, router, ignore_missing=True, if_revision=None):
"""Delete a router
:param router: The value can be either the ID of a router or a
@ -2492,10 +2529,13 @@ class Proxy(proxy.Proxy):
raised when the router does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent router.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:returns: ``None``
"""
self._delete(_router.Router, router, ignore_missing=ignore_missing)
self._delete(_router.Router, router, ignore_missing=ignore_missing,
if_revision=if_revision)
def find_router(self, name_or_id, ignore_missing=True, **args):
"""Find a single router
@ -2546,18 +2586,21 @@ class Proxy(proxy.Proxy):
"""
return self._list(_router.Router, **query)
def update_router(self, router, **attrs):
def update_router(self, router, if_revision=None, **attrs):
"""Update a router
:param router: Either the id of a router or a
:class:`~openstack.network.v2.router.Router` instance.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:param dict attrs: The attributes to update on the router represented
by ``router``.
:returns: The updated router
:rtype: :class:`~openstack.network.v2.router.Router`
"""
return self._update(_router.Router, router, **attrs)
return self._update(_router.Router, router, if_revision=if_revision,
**attrs)
def add_interface_to_router(self, router, subnet_id=None, port_id=None):
"""Add Interface to a router
@ -3048,7 +3091,8 @@ class Proxy(proxy.Proxy):
"""
return self._create(_security_group.SecurityGroup, **attrs)
def delete_security_group(self, security_group, ignore_missing=True):
def delete_security_group(self, security_group, ignore_missing=True,
if_revision=None):
"""Delete a security group
:param security_group:
@ -3060,11 +3104,13 @@ class Proxy(proxy.Proxy):
raised when the security group does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent security group.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:returns: ``None``
"""
self._delete(_security_group.SecurityGroup, security_group,
ignore_missing=ignore_missing)
ignore_missing=ignore_missing, if_revision=if_revision)
def find_security_group(self, name_or_id, ignore_missing=True, **args):
"""Find a single security group
@ -3113,12 +3159,14 @@ class Proxy(proxy.Proxy):
"""
return self._list(_security_group.SecurityGroup, **query)
def update_security_group(self, security_group, **attrs):
def update_security_group(self, security_group, if_revision=None, **attrs):
"""Update a security group
:param security_group: Either the id of a security group or a
:class:`~openstack.network.v2.security_group.SecurityGroup`
instance.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:param dict attrs: The attributes to update on the security group
represented by ``security_group``.
@ -3126,7 +3174,7 @@ class Proxy(proxy.Proxy):
:rtype: :class:`~openstack.network.v2.security_group.SecurityGroup`
"""
return self._update(_security_group.SecurityGroup, security_group,
**attrs)
if_revision=if_revision, **attrs)
def create_security_group_rule(self, **attrs):
"""Create a new security group rule from attributes
@ -3143,7 +3191,7 @@ class Proxy(proxy.Proxy):
return self._create(_security_group_rule.SecurityGroupRule, **attrs)
def delete_security_group_rule(self, security_group_rule,
ignore_missing=True):
ignore_missing=True, if_revision=None):
"""Delete a security group rule
:param security_group_rule:
@ -3155,11 +3203,14 @@ class Proxy(proxy.Proxy):
raised when the security group rule does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent security group rule.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:returns: ``None``
"""
self._delete(_security_group_rule.SecurityGroupRule,
security_group_rule, ignore_missing=ignore_missing)
security_group_rule, ignore_missing=ignore_missing,
if_revision=if_revision)
def find_security_group_rule(self, name_or_id, ignore_missing=True,
**args):
@ -3424,7 +3475,7 @@ class Proxy(proxy.Proxy):
"""
return self._create(_subnet.Subnet, **attrs)
def delete_subnet(self, subnet, ignore_missing=True):
def delete_subnet(self, subnet, ignore_missing=True, if_revision=None):
"""Delete a subnet
:param subnet: The value can be either the ID of a subnet or a
@ -3434,10 +3485,13 @@ class Proxy(proxy.Proxy):
raised when the subnet does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent subnet.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:returns: ``None``
"""
self._delete(_subnet.Subnet, subnet, ignore_missing=ignore_missing)
self._delete(_subnet.Subnet, subnet, ignore_missing=ignore_missing,
if_revision=if_revision)
def find_subnet(self, name_or_id, ignore_missing=True, **args):
"""Find a single subnet
@ -3491,18 +3545,21 @@ class Proxy(proxy.Proxy):
"""
return self._list(_subnet.Subnet, **query)
def update_subnet(self, subnet, **attrs):
def update_subnet(self, subnet, if_revision=None, **attrs):
"""Update a subnet
:param subnet: Either the id of a subnet or a
:class:`~openstack.network.v2.subnet.Subnet` instance.
:param int if_revision: Revision to put in If-Match header of update
request to perform compare-and-swap update.
:param dict attrs: The attributes to update on the subnet represented
by ``subnet``.
:returns: The updated subnet
:rtype: :class:`~openstack.network.v2.subnet.Subnet`
"""
return self._update(_subnet.Subnet, subnet, **attrs)
return self._update(_subnet.Subnet, subnet, if_revision=if_revision,
**attrs)
def create_subnet_pool(self, **attrs):
"""Create a new subnet pool from attributes

View File

@ -10,10 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack.network.v2 import _base
from openstack import resource
class FloatingIP(resource.Resource, resource.TagMixin):
class FloatingIP(_base.NetworkResource, resource.TagMixin):
name_attribute = "floating_ip_address"
resource_name = "floating ip"
resource_key = 'floatingip'
@ -68,8 +69,6 @@ class FloatingIP(resource.Resource, resource.TagMixin):
qos_policy_id = resource.Body('qos_policy_id')
#: The ID of the project this floating IP is associated with.
project_id = resource.Body('tenant_id')
#: Revision number of the floating IP. *Type: int*
revision_number = resource.Body('revision_number', type=int)
#: The ID of an associated router.
router_id = resource.Body('router_id')
#: The floating IP status. Value is ``ACTIVE`` or ``DOWN``.

View File

@ -10,10 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack.network.v2 import _base
from openstack import resource
class Network(resource.Resource, resource.TagMixin):
class Network(_base.NetworkResource, resource.TagMixin):
resource_key = 'network'
resources_key = 'networks'
base_path = '/networks'
@ -97,8 +98,6 @@ class Network(resource.Resource, resource.TagMixin):
provider_segmentation_id = resource.Body('provider:segmentation_id')
#: The ID of the QoS policy attached to the port.
qos_policy_id = resource.Body('qos_policy_id')
#: Revision number of the network. *Type: int*
revision_number = resource.Body('revision_number', type=int)
#: A list of provider segment objects.
#: Available for multiple provider extensions.
segments = resource.Body('segments')

View File

@ -10,10 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack.network.v2 import _base
from openstack import resource
class Port(resource.Resource, resource.TagMixin):
class Port(_base.NetworkResource, resource.TagMixin):
resource_key = 'port'
resources_key = 'ports'
base_path = '/ports'
@ -114,8 +115,6 @@ class Port(resource.Resource, resource.TagMixin):
# (i.e.: minimum-bandwidth) and traits (i.e.: vnic-type, physnet)
# requested by a port to Nova and Placement.
resource_request = resource.Body('resource_request', type=dict)
#: Revision number of the port. *Type: int*
revision_number = resource.Body('revision_number', type=int)
#: The IDs of any attached security groups.
#: *Type: list of strs of the security group IDs*
security_group_ids = resource.Body('security_groups', type=list)

View File

@ -57,7 +57,7 @@ class Quota(resource.Resource):
security_groups = resource.Body('security_group', type=int)
def _prepare_request(self, requires_id=True, prepend_key=False,
base_path=None):
base_path=None, **kwargs):
_request = super(Quota, self)._prepare_request(requires_id,
prepend_key)
if self.resource_key in _request.body:

View File

@ -11,11 +11,12 @@
# under the License.
from openstack import exceptions
from openstack.network.v2 import _base
from openstack import resource
from openstack import utils
class Router(resource.Resource, resource.TagMixin):
class Router(_base.NetworkResource, resource.TagMixin):
resource_key = 'router'
resources_key = 'routers'
base_path = '/routers'

View File

@ -10,10 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack.network.v2 import _base
from openstack import resource
class SecurityGroup(resource.Resource, resource.TagMixin):
class SecurityGroup(_base.NetworkResource, resource.TagMixin):
resource_key = 'security_group'
resources_key = 'security_groups'
base_path = '/security-groups'
@ -40,8 +41,6 @@ class SecurityGroup(resource.Resource, resource.TagMixin):
name = resource.Body('name')
#: The ID of the project this security group is associated with.
project_id = resource.Body('project_id')
#: Revision number of the security group. *Type: int*
revision_number = resource.Body('revision_number', type=int)
#: A list of
#: :class:`~openstack.network.v2.security_group_rule.SecurityGroupRule`
#: objects. *Type: list*

View File

@ -10,10 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack.network.v2 import _base
from openstack import resource
class SecurityGroupRule(resource.Resource, resource.TagMixin):
class SecurityGroupRule(_base.NetworkResource, resource.TagMixin):
resource_key = 'security_group_rule'
resources_key = 'security_group_rules'
base_path = '/security-group-rules'
@ -74,8 +75,6 @@ class SecurityGroupRule(resource.Resource, resource.TagMixin):
#: in the request body. This attribute matches the specified IP prefix
#: as the source IP address of the IP packet.
remote_ip_prefix = resource.Body('remote_ip_prefix')
#: Revision number of the security group rule. *Type: int*
revision_number = resource.Body('revision_number', type=int)
#: The security group ID to associate with this security group rule.
security_group_id = resource.Body('security_group_id')
#: The ID of the project this security group rule is associated with.

View File

@ -10,10 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack.network.v2 import _base
from openstack import resource
class Subnet(resource.Resource, resource.TagMixin):
class Subnet(_base.NetworkResource, resource.TagMixin):
resource_key = 'subnet'
resources_key = 'subnets'
base_path = '/subnets'
@ -75,8 +76,6 @@ class Subnet(resource.Resource, resource.TagMixin):
prefix_length = resource.Body('prefixlen')
#: The ID of the project this subnet is associated with.
project_id = resource.Body('tenant_id')
#: Revision number of the subnet. *Type: int*
revision_number = resource.Body('revision_number', type=int)
#: The ID of the segment this subnet is associated with.
segment_id = resource.Body('segment_id')
#: Service types for this subnet

View File

@ -1053,7 +1053,7 @@ class Resource(dict):
return body
def _prepare_request(self, requires_id=None, prepend_key=False,
patch=False, base_path=None, params=None):
patch=False, base_path=None, params=None, **kwargs):
"""Prepare a request to be sent to the server
Create operations don't require an ID, but all others do,
@ -1436,7 +1436,7 @@ class Resource(dict):
or self.allow_empty_commit)
def commit(self, session, prepend_key=True, has_body=True,
retry_on_conflict=None, base_path=None):
retry_on_conflict=None, base_path=None, **kwargs):
"""Commit the state of the instance to the remote resource.
:param session: The session to use for making this request.
@ -1450,6 +1450,8 @@ class Resource(dict):
:param str base_path: Base part of the URI for modifying resources, if
different from
:data:`~openstack.resource.Resource.base_path`.
:param dict kwargs: Parameters that will be passed to
_prepare_request()
:return: This :class:`Resource` instance.
:raises: :exc:`~openstack.exceptions.MethodNotSupported` if
@ -1467,7 +1469,6 @@ class Resource(dict):
# Avoid providing patch unconditionally to avoid breaking subclasses
# without it.
kwargs = {}
if self.commit_jsonpatch:
kwargs['patch'] = True
@ -1582,11 +1583,13 @@ class Resource(dict):
has_body=has_body,
retry_on_conflict=retry_on_conflict)
def delete(self, session, error_message=None):
def delete(self, session, error_message=None, **kwargs):
"""Delete the remote resource based on this instance.
:param session: The session to use for making this request.
:type session: :class:`~keystoneauth1.adapter.Adapter`
:param dict kwargs: Parameters that will be passed to
_prepare_request()
:return: This :class:`Resource` instance.
:raises: :exc:`~openstack.exceptions.MethodNotSupported` if
@ -1595,7 +1598,7 @@ class Resource(dict):
the resource was not found.
"""
response = self._raw_delete(session)
response = self._raw_delete(session, **kwargs)
kwargs = {}
if error_message:
kwargs['error_message'] = error_message
@ -1603,16 +1606,16 @@ class Resource(dict):
self._translate_response(response, has_body=False, **kwargs)
return self
def _raw_delete(self, session):
def _raw_delete(self, session, **kwargs):
if not self.allow_delete:
raise exceptions.MethodNotSupported(self, "delete")
request = self._prepare_request()
request = self._prepare_request(**kwargs)
session = self._get_session(session)
microversion = self._get_microversion_for(session, 'delete')
return session.delete(
request.url,
request.url, headers=request.headers,
microversion=microversion)
@classmethod

View File

@ -70,6 +70,25 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
super(TestNetworkProxy, self).setUp()
self.proxy = _proxy.Proxy(self.session)
def verify_update(self, test_method, resource_type, value=None,
mock_method="openstack.network.v2._proxy.Proxy._update",
expected_result="result", path_args=None, **kwargs):
super(TestNetworkProxy, self).verify_update(
test_method, resource_type, value=value, mock_method=mock_method,
expected_result=expected_result, path_args=path_args, **kwargs)
def verify_delete(self, test_method, resource_type, ignore,
input_path_args=None, expected_path_args=None,
method_kwargs=None, expected_args=None,
expected_kwargs=None,
mock_method="openstack.network.v2._proxy.Proxy._delete"):
super(TestNetworkProxy, self).verify_delete(
test_method, resource_type, ignore,
input_path_args=input_path_args,
expected_path_args=expected_path_args, method_kwargs=method_kwargs,
expected_args=expected_args, expected_kwargs=expected_kwargs,
mock_method=mock_method)
def test_address_scope_create_attrs(self):
self.verify_create(self.proxy.create_address_scope,
address_scope.AddressScope)
@ -143,11 +162,16 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_floating_ip_delete(self):
self.verify_delete(self.proxy.delete_ip, floating_ip.FloatingIP,
False)
False, expected_kwargs={'if_revision': None})
def test_floating_ip_delete_ignore(self):
self.verify_delete(self.proxy.delete_ip, floating_ip.FloatingIP,
True)
True, expected_kwargs={'if_revision': None})
def test_floating_ip_delete_if_revision(self):
self.verify_delete(self.proxy.delete_ip, floating_ip.FloatingIP,
True, method_kwargs={'if_revision': 42},
expected_kwargs={'if_revision': 42})
def test_floating_ip_find(self):
self.verify_find(self.proxy.find_ip, floating_ip.FloatingIP)
@ -159,7 +183,16 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_list(self.proxy.ips, floating_ip.FloatingIP)
def test_floating_ip_update(self):
self.verify_update(self.proxy.update_ip, floating_ip.FloatingIP)
self.verify_update(self.proxy.update_ip, floating_ip.FloatingIP,
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': None})
def test_floating_ip_update_if_revision(self):
self.verify_update(self.proxy.update_ip, floating_ip.FloatingIP,
method_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42},
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42})
def test_health_monitor_create_attrs(self):
self.verify_create(self.proxy.create_health_monitor,
@ -300,10 +333,17 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_create(self.proxy.create_network, network.Network)
def test_network_delete(self):
self.verify_delete(self.proxy.delete_network, network.Network, False)
self.verify_delete(self.proxy.delete_network, network.Network, False,
expected_kwargs={'if_revision': None})
def test_network_delete_ignore(self):
self.verify_delete(self.proxy.delete_network, network.Network, True)
self.verify_delete(self.proxy.delete_network, network.Network, True,
expected_kwargs={'if_revision': None})
def test_network_delete_if_revision(self):
self.verify_delete(self.proxy.delete_network, network.Network, True,
method_kwargs={'if_revision': 42},
expected_kwargs={'if_revision': 42})
def test_network_find(self):
self.verify_find(self.proxy.find_network, network.Network)
@ -324,7 +364,16 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_list(self.proxy.networks, network.Network)
def test_network_update(self):
self.verify_update(self.proxy.update_network, network.Network)
self.verify_update(self.proxy.update_network, network.Network,
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': None})
def test_network_update_if_revision(self):
self.verify_update(self.proxy.update_network, network.Network,
method_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42},
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42})
def test_flavor_create_attrs(self):
self.verify_create(self.proxy.create_flavor, flavor.Flavor)
@ -417,7 +466,7 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
expected_kwargs={"pool_id": "test_id"})
def test_pool_member_update(self):
self._verify2("openstack.proxy.Proxy._update",
self._verify2("openstack.network.v2._proxy.Proxy._update",
self.proxy.update_pool_member,
method_args=["MEMBER", "POOL"],
expected_args=[pool_member.PoolMember, "MEMBER"],
@ -448,10 +497,17 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_create(self.proxy.create_port, port.Port)
def test_port_delete(self):
self.verify_delete(self.proxy.delete_port, port.Port, False)
self.verify_delete(self.proxy.delete_port, port.Port, False,
expected_kwargs={'if_revision': None})
def test_port_delete_ignore(self):
self.verify_delete(self.proxy.delete_port, port.Port, True)
self.verify_delete(self.proxy.delete_port, port.Port, True,
expected_kwargs={'if_revision': None})
def test_port_delete_if_revision(self):
self.verify_delete(self.proxy.delete_port, port.Port, True,
method_kwargs={'if_revision': 42},
expected_kwargs={'if_revision': 42})
def test_port_find(self):
self.verify_find(self.proxy.find_port, port.Port)
@ -463,7 +519,16 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_list(self.proxy.ports, port.Port)
def test_port_update(self):
self.verify_update(self.proxy.update_port, port.Port)
self.verify_update(self.proxy.update_port, port.Port,
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': None})
def test_port_update_if_revision(self):
self.verify_update(self.proxy.update_port, port.Port,
method_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42},
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42})
@mock.patch('openstack.network.v2._proxy.Proxy._bulk_create')
def test_ports_create(self, bc):
@ -521,7 +586,7 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_qos_bandwidth_limit_rule_update(self):
policy = qos_policy.QoSPolicy.new(id=QOS_POLICY_ID)
self._verify2('openstack.proxy.Proxy._update',
self._verify2('openstack.network.v2._proxy.Proxy._update',
self.proxy.update_qos_bandwidth_limit_rule,
method_args=['rule_id', policy],
method_kwargs={'foo': 'bar'},
@ -578,7 +643,7 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_qos_dscp_marking_rule_update(self):
policy = qos_policy.QoSPolicy.new(id=QOS_POLICY_ID)
self._verify2('openstack.proxy.Proxy._update',
self._verify2('openstack.network.v2._proxy.Proxy._update',
self.proxy.update_qos_dscp_marking_rule,
method_args=['rule_id', policy],
method_kwargs={'foo': 'bar'},
@ -636,7 +701,7 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_qos_minimum_bandwidth_rule_update(self):
policy = qos_policy.QoSPolicy.new(id=QOS_POLICY_ID)
self._verify2('openstack.proxy.Proxy._update',
self._verify2('openstack.network.v2._proxy.Proxy._update',
self.proxy.update_qos_minimum_bandwidth_rule,
method_args=['rule_id', policy],
method_kwargs={'foo': 'bar'},
@ -749,10 +814,17 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_create(self.proxy.create_router, router.Router)
def test_router_delete(self):
self.verify_delete(self.proxy.delete_router, router.Router, False)
self.verify_delete(self.proxy.delete_router, router.Router, False,
expected_kwargs={'if_revision': None})
def test_router_delete_ignore(self):
self.verify_delete(self.proxy.delete_router, router.Router, True)
self.verify_delete(self.proxy.delete_router, router.Router, True,
expected_kwargs={'if_revision': None})
def test_router_delete_if_revision(self):
self.verify_delete(self.proxy.delete_router, router.Router, True,
method_kwargs={'if_revision': 42},
expected_kwargs={'if_revision': 42})
def test_router_find(self):
self.verify_find(self.proxy.find_router, router.Router)
@ -764,7 +836,16 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_list(self.proxy.routers, router.Router)
def test_router_update(self):
self.verify_update(self.proxy.update_router, router.Router)
self.verify_update(self.proxy.update_router, router.Router,
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': None})
def test_router_update_if_revision(self):
self.verify_update(self.proxy.update_router, router.Router,
method_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42},
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42})
@mock.patch.object(proxy_base.Proxy, '_get_resource')
@mock.patch.object(router.Router, 'add_interface')
@ -1010,11 +1091,19 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_security_group_delete(self):
self.verify_delete(self.proxy.delete_security_group,
security_group.SecurityGroup, False)
security_group.SecurityGroup, False,
expected_kwargs={'if_revision': None})
def test_security_group_delete_ignore(self):
self.verify_delete(self.proxy.delete_security_group,
security_group.SecurityGroup, True)
security_group.SecurityGroup, True,
expected_kwargs={'if_revision': None})
def test_security_group_delete_if_revision(self):
self.verify_delete(self.proxy.delete_security_group,
security_group.SecurityGroup, True,
method_kwargs={'if_revision': 42},
expected_kwargs={'if_revision': 42})
def test_security_group_find(self):
self.verify_find(self.proxy.find_security_group,
@ -1030,7 +1119,17 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_security_group_update(self):
self.verify_update(self.proxy.update_security_group,
security_group.SecurityGroup)
security_group.SecurityGroup,
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': None})
def test_security_group_update_if_revision(self):
self.verify_update(self.proxy.update_security_group,
security_group.SecurityGroup,
method_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42},
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': 42})
def test_security_group_rule_create_attrs(self):
self.verify_create(self.proxy.create_security_group_rule,
@ -1038,11 +1137,19 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_security_group_rule_delete(self):
self.verify_delete(self.proxy.delete_security_group_rule,
security_group_rule.SecurityGroupRule, False)
security_group_rule.SecurityGroupRule, False,
expected_kwargs={'if_revision': None})
def test_security_group_rule_delete_ignore(self):
self.verify_delete(self.proxy.delete_security_group_rule,
security_group_rule.SecurityGroupRule, True)
security_group_rule.SecurityGroupRule, True,
expected_kwargs={'if_revision': None})
def test_security_group_rule_delete_if_revision(self):
self.verify_delete(self.proxy.delete_security_group_rule,
security_group_rule.SecurityGroupRule, True,
method_kwargs={'if_revision': 42},
expected_kwargs={'if_revision': 42})
def test_security_group_rule_find(self):
self.verify_find(self.proxy.find_security_group_rule,
@ -1081,10 +1188,17 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_create(self.proxy.create_subnet, subnet.Subnet)
def test_subnet_delete(self):
self.verify_delete(self.proxy.delete_subnet, subnet.Subnet, False)
self.verify_delete(self.proxy.delete_subnet, subnet.Subnet, False,
expected_kwargs={'if_revision': None})
def test_subnet_delete_ignore(self):
self.verify_delete(self.proxy.delete_subnet, subnet.Subnet, True)
self.verify_delete(self.proxy.delete_subnet, subnet.Subnet, True,
expected_kwargs={'if_revision': None})
def test_subnet_delete_if_revision(self):
self.verify_delete(self.proxy.delete_subnet, subnet.Subnet, True,
method_kwargs={'if_revision': 42},
expected_kwargs={'if_revision': 42})
def test_subnet_find(self):
self.verify_find(self.proxy.find_subnet, subnet.Subnet)
@ -1096,7 +1210,9 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
self.verify_list(self.proxy.subnets, subnet.Subnet)
def test_subnet_update(self):
self.verify_update(self.proxy.update_subnet, subnet.Subnet)
self.verify_update(self.proxy.update_subnet, subnet.Subnet,
expected_kwargs={'x': 1, 'y': 2, 'z': 3,
'if_revision': None})
def test_subnet_pool_create_attrs(self):
self.verify_create(self.proxy.create_subnet_pool,
@ -1244,7 +1360,7 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
def test_update_floating_ip_port_forwarding(self):
fip = floating_ip.FloatingIP.new(id=FIP_ID)
self._verify2('openstack.proxy.Proxy._update',
self._verify2('openstack.network.v2._proxy.Proxy._update',
self.proxy.update_floating_ip_port_forwarding,
method_args=[fip, 'port_forwarding_id'],
method_kwargs={'foo': 'bar'},

View File

@ -1750,6 +1750,7 @@ class TestResourceActions(base.TestCase):
self.sot._prepare_request.assert_called_once_with()
self.session.delete.assert_called_once_with(
self.request.url,
headers='headers',
microversion=None)
self.sot._translate_response.assert_called_once_with(
@ -1772,6 +1773,7 @@ class TestResourceActions(base.TestCase):
sot._prepare_request.assert_called_once_with()
self.session.delete.assert_called_once_with(
self.request.url,
headers='headers',
microversion='1.42')
sot._translate_response.assert_called_once_with(