diff --git a/openstack/compute/v2/keypair.py b/openstack/compute/v2/keypair.py index 51109632c..1db894f81 100644 --- a/openstack/compute/v2/keypair.py +++ b/openstack/compute/v2/keypair.py @@ -51,7 +51,7 @@ class Keypair(resource.Resource): return super(Keypair, self)._consume_attrs(mapping, attrs) @classmethod - def list(cls, session, paginated=False): + def list(cls, session, paginated=False, base_path=None): resp = session.get(cls.base_path, headers={"Accept": "application/json"}) resp = resp.json() diff --git a/openstack/compute/v2/limits.py b/openstack/compute/v2/limits.py index b08b29219..0f019897f 100644 --- a/openstack/compute/v2/limits.py +++ b/openstack/compute/v2/limits.py @@ -76,7 +76,8 @@ class Limits(resource.Resource): absolute = resource.Body("absolute", type=AbsoluteLimits) rate = resource.Body("rate", type=list, list_type=RateLimit) - def fetch(self, session, requires_id=False, error_message=None): + def fetch(self, session, requires_id=False, error_message=None, + base_path=None): """Get the Limits resource. :param session: The session to use for making this request. @@ -88,4 +89,5 @@ class Limits(resource.Resource): # TODO(mordred) We shouldn't have to subclass just to declare # requires_id = False. return super(Limits, self).fetch( - session=session, requires_id=False, error_message=error_message) + session=session, requires_id=False, error_message=error_message, + base_path=base_path) diff --git a/openstack/compute/v2/server.py b/openstack/compute/v2/server.py index 11294fb62..a9db33f9a 100644 --- a/openstack/compute/v2/server.py +++ b/openstack/compute/v2/server.py @@ -138,7 +138,8 @@ class Server(resource.Resource, metadata.MetadataMixin, resource.TagMixin): #: only. instance_name = resource.Body('OS-EXT-SRV-ATTR:instance_name') - def _prepare_request(self, requires_id=True, prepend_key=True): + def _prepare_request(self, requires_id=True, prepend_key=True, + base_path=None): request = super(Server, self)._prepare_request(requires_id=requires_id, prepend_key=prepend_key) diff --git a/openstack/compute/v2/server_ip.py b/openstack/compute/v2/server_ip.py index bc868fb6c..d3b9dd236 100644 --- a/openstack/compute/v2/server_ip.py +++ b/openstack/compute/v2/server_ip.py @@ -33,7 +33,7 @@ class ServerIP(resource.Resource): @classmethod def list(cls, session, paginated=False, server_id=None, - network_label=None, **params): + network_label=None, base_path=None, **params): url = cls.base_path % {"server_id": server_id} if network_label is not None: diff --git a/openstack/database/v1/user.py b/openstack/database/v1/user.py index c3a0d3d6e..a18f0b898 100644 --- a/openstack/database/v1/user.py +++ b/openstack/database/v1/user.py @@ -34,7 +34,8 @@ class User(resource.Resource): #: The password of the user password = resource.Body('password') - def _prepare_request(self, requires_id=True, prepend_key=True): + def _prepare_request(self, requires_id=True, prepend_key=True, + base_path=None): """Prepare a request for the database service's create call User.create calls require the resources_key. diff --git a/openstack/identity/v2/extension.py b/openstack/identity/v2/extension.py index 153d6df91..5f95a499d 100644 --- a/openstack/identity/v2/extension.py +++ b/openstack/identity/v2/extension.py @@ -43,7 +43,7 @@ class Extension(resource.Resource): updated_at = resource.Body('updated') @classmethod - def list(cls, session, paginated=False, **params): + def list(cls, session, paginated=False, base_path=None, **params): resp = session.get(cls.base_path, params=params) resp = resp.json() diff --git a/openstack/identity/version.py b/openstack/identity/version.py index 3ad27ee86..710328055 100644 --- a/openstack/identity/version.py +++ b/openstack/identity/version.py @@ -27,7 +27,7 @@ class Version(resource.Resource): updated = resource.Body('updated') @classmethod - def list(cls, session, paginated=False, **params): + def list(cls, session, paginated=False, base_path=None, **params): resp = session.get(cls.base_path, params=params) resp = resp.json() diff --git a/openstack/image/v2/image.py b/openstack/image/v2/image.py index 7889bc1d2..0c121b11d 100644 --- a/openstack/image/v2/image.py +++ b/openstack/image/v2/image.py @@ -278,10 +278,11 @@ class Image(resource.Resource, resource.TagMixin): return resp.content def _prepare_request(self, requires_id=None, prepend_key=False, - patch=False): + patch=False, base_path=None): request = super(Image, self)._prepare_request(requires_id=requires_id, prepend_key=prepend_key, - patch=patch) + patch=patch, + base_path=base_path) if patch: headers = { 'Content-Type': 'application/openstack-images-v2.1-json-patch', diff --git a/openstack/key_manager/v1/secret.py b/openstack/key_manager/v1/secret.py index 4e2be6c46..7cb8c23f9 100644 --- a/openstack/key_manager/v1/secret.py +++ b/openstack/key_manager/v1/secret.py @@ -77,8 +77,10 @@ class Secret(resource.Resource): #: (required if payload is encoded) payload_content_encoding = resource.Body('payload_content_encoding') - def fetch(self, session, requires_id=True, error_message=None): - request = self._prepare_request(requires_id=requires_id) + def fetch(self, session, requires_id=True, + base_path=None, error_message=None): + request = self._prepare_request(requires_id=requires_id, + base_path=base_path) response = session.get(request.url).json() diff --git a/openstack/load_balancer/v2/quota.py b/openstack/load_balancer/v2/quota.py index 77799f7d1..a14f62812 100644 --- a/openstack/load_balancer/v2/quota.py +++ b/openstack/load_balancer/v2/quota.py @@ -41,9 +41,11 @@ class Quota(resource.Resource): #: The ID of the project this quota is associated with. project_id = resource.Body('project_id', alternate_id=True) - def _prepare_request(self, requires_id=True, prepend_key=False): + def _prepare_request(self, requires_id=True, + base_path=None, prepend_key=False): _request = super(Quota, self)._prepare_request(requires_id, - prepend_key) + prepend_key, + base_path=base_path) if self.resource_key in _request.body: _body = _request.body[self.resource_key] else: diff --git a/openstack/message/v2/claim.py b/openstack/message/v2/claim.py index f02e51748..ba9075338 100644 --- a/openstack/message/v2/claim.py +++ b/openstack/message/v2/claim.py @@ -61,9 +61,10 @@ class Claim(resource.Resource): # Extract claim ID from location self.id = self.location.split("claims/")[1] - def create(self, session, prepend_key=False): + def create(self, session, prepend_key=False, base_path=None): request = self._prepare_request(requires_id=False, - prepend_key=prepend_key) + prepend_key=prepend_key, + base_path=base_path) headers = { "Client-ID": self.client_id or str(uuid.uuid4()), "X-PROJECT-ID": self.project_id or session.get_project_id() @@ -80,8 +81,10 @@ class Claim(resource.Resource): return self - def fetch(self, session, requires_id=True, error_message=None): - request = self._prepare_request(requires_id=requires_id) + def fetch(self, session, requires_id=True, + base_path=None, error_message=None): + request = self._prepare_request(requires_id=requires_id, + base_path=base_path) headers = { "Client-ID": self.client_id or str(uuid.uuid4()), "X-PROJECT-ID": self.project_id or session.get_project_id() @@ -94,8 +97,10 @@ class Claim(resource.Resource): return self - def commit(self, session, prepend_key=False, has_body=False): - request = self._prepare_request(prepend_key=prepend_key) + def commit(self, session, prepend_key=False, has_body=False, + base_path=None): + request = self._prepare_request(prepend_key=prepend_key, + base_path=base_path) headers = { "Client-ID": self.client_id or str(uuid.uuid4()), "X-PROJECT-ID": self.project_id or session.get_project_id() diff --git a/openstack/message/v2/message.py b/openstack/message/v2/message.py index 5f7acb090..a8818ace8 100644 --- a/openstack/message/v2/message.py +++ b/openstack/message/v2/message.py @@ -67,7 +67,7 @@ class Message(resource.Resource): return response.json()['resources'] @classmethod - def list(cls, session, paginated=True, **params): + def list(cls, session, paginated=True, base_path=None, **params): """This method is a generator which yields message objects. This is almost the copy of list method of resource.Resource class. @@ -107,8 +107,10 @@ class Message(resource.Resource): query_params["limit"] = yielded query_params["marker"] = new_marker - def fetch(self, session, requires_id=True, error_message=None): - request = self._prepare_request(requires_id=requires_id) + def fetch(self, session, requires_id=True, + base_path=None, error_message=None): + request = self._prepare_request(requires_id=requires_id, + base_path=base_path) headers = { "Client-ID": self.client_id or str(uuid.uuid4()), "X-PROJECT-ID": self.project_id or session.get_project_id() diff --git a/openstack/message/v2/queue.py b/openstack/message/v2/queue.py index 7901f1959..7b65bb881 100644 --- a/openstack/message/v2/queue.py +++ b/openstack/message/v2/queue.py @@ -50,9 +50,10 @@ class Queue(resource.Resource): #: in case keystone auth is not enabled in Zaqar service. project_id = resource.Header("X-PROJECT-ID") - def create(self, session, prepend_key=True): + def create(self, session, prepend_key=True, base_path=None): request = self._prepare_request(requires_id=True, - prepend_key=prepend_key) + prepend_key=prepend_key, + base_path=None) headers = { "Client-ID": self.client_id or str(uuid.uuid4()), "X-PROJECT-ID": self.project_id or session.get_project_id() @@ -65,7 +66,7 @@ class Queue(resource.Resource): return self @classmethod - def list(cls, session, paginated=False, **params): + def list(cls, session, paginated=False, base_path=None, **params): """This method is a generator which yields queue objects. This is almost the copy of list method of resource.Resource class. @@ -105,8 +106,10 @@ class Queue(resource.Resource): query_params["limit"] = yielded query_params["marker"] = new_marker - def fetch(self, session, requires_id=True, error_message=None): - request = self._prepare_request(requires_id=requires_id) + def fetch(self, session, requires_id=True, + base_path=None, error_message=None): + request = self._prepare_request(requires_id=requires_id, + base_path=base_path) headers = { "Client-ID": self.client_id or str(uuid.uuid4()), "X-PROJECT-ID": self.project_id or session.get_project_id() diff --git a/openstack/message/v2/subscription.py b/openstack/message/v2/subscription.py index 7372951b8..b3960c0fd 100644 --- a/openstack/message/v2/subscription.py +++ b/openstack/message/v2/subscription.py @@ -58,7 +58,7 @@ class Subscription(resource.Resource): #: authentication is not enabled in Zaqar service. project_id = resource.Header("X-PROJECT-ID") - def create(self, session, prepend_key=True): + def create(self, session, prepend_key=True, base_path=None): request = self._prepare_request(requires_id=False, prepend_key=prepend_key) headers = { @@ -73,7 +73,7 @@ class Subscription(resource.Resource): return self @classmethod - def list(cls, session, paginated=True, **params): + def list(cls, session, paginated=True, base_path=None, **params): """This method is a generator which yields subscription objects. This is almost the copy of list method of resource.Resource class. @@ -113,8 +113,10 @@ class Subscription(resource.Resource): query_params["limit"] = yielded query_params["marker"] = new_marker - def fetch(self, session, requires_id=True, error_message=None): - request = self._prepare_request(requires_id=requires_id) + def fetch(self, session, requires_id=True, + base_path=None, error_message=None): + request = self._prepare_request(requires_id=requires_id, + base_path=base_path) headers = { "Client-ID": self.client_id or str(uuid.uuid4()), "X-PROJECT-ID": self.project_id or session.get_project_id() diff --git a/openstack/network/v2/quota.py b/openstack/network/v2/quota.py index eab5aa79b..87432718c 100644 --- a/openstack/network/v2/quota.py +++ b/openstack/network/v2/quota.py @@ -56,7 +56,8 @@ class Quota(resource.Resource): #: The maximum amount of security groups you can create. *Type: int* security_groups = resource.Body('security_group', type=int) - def _prepare_request(self, requires_id=True, prepend_key=False): + def _prepare_request(self, requires_id=True, prepend_key=False, + base_path=None): _request = super(Quota, self)._prepare_request(requires_id, prepend_key) if self.resource_key in _request.body: diff --git a/openstack/object_store/v1/container.py b/openstack/object_store/v1/container.py index d45bcd864..04c0d45e4 100644 --- a/openstack/object_store/v1/container.py +++ b/openstack/object_store/v1/container.py @@ -109,7 +109,7 @@ class Container(_base.BaseResource): kwargs.setdefault('name', name) return Container(_synchronized=True, **kwargs) - def create(self, session, prepend_key=True): + def create(self, session, prepend_key=True, base_path=None): """Create a remote resource based on this instance. :param session: The session to use for making this request. @@ -123,7 +123,7 @@ class Container(_base.BaseResource): :data:`Resource.allow_create` is not set to ``True``. """ request = self._prepare_request( - requires_id=True, prepend_key=prepend_key) + requires_id=True, prepend_key=prepend_key, base_path=base_path) response = session.put( request.url, json=request.body, headers=request.headers) diff --git a/openstack/object_store/v1/obj.py b/openstack/object_store/v1/obj.py index 41daa9d28..2ef16529f 100644 --- a/openstack/object_store/v1/obj.py +++ b/openstack/object_store/v1/obj.py @@ -285,8 +285,8 @@ class Object(_base.BaseResource): session, error_message=error_message, stream=True) return response.iter_content(chunk_size, decode_unicode=False) - def create(self, session): - request = self._prepare_request() + def create(self, session, base_path=None): + request = self._prepare_request(base_path=base_path) request.headers['Accept'] = '' response = session.put( diff --git a/openstack/orchestration/v1/software_config.py b/openstack/orchestration/v1/software_config.py index af59e8989..6107068d5 100644 --- a/openstack/orchestration/v1/software_config.py +++ b/openstack/orchestration/v1/software_config.py @@ -45,7 +45,8 @@ class SoftwareConfig(resource.Resource): #: produces. outputs = resource.Body('outputs') - def create(self, session): + def create(self, session, base_path=None): # This overrides the default behavior of resource creation because # heat doesn't accept resource_key in its request. - return super(SoftwareConfig, self).create(session, prepend_key=False) + return super(SoftwareConfig, self).create(session, prepend_key=False, + base_path=base_path) diff --git a/openstack/orchestration/v1/software_deployment.py b/openstack/orchestration/v1/software_deployment.py index 5ac670973..2c091b342 100644 --- a/openstack/orchestration/v1/software_deployment.py +++ b/openstack/orchestration/v1/software_deployment.py @@ -49,14 +49,14 @@ class SoftwareDeployment(resource.Resource): #: The date and time when the software deployment resource was created. updated_at = resource.Body('updated_time') - def create(self, session): + def create(self, session, base_path=None): # This overrides the default behavior of resource creation because # heat doesn't accept resource_key in its request. return super(SoftwareDeployment, self).create( - session, prepend_key=False) + session, prepend_key=False, base_path=base_path) - def commit(self, session): + def commit(self, session, base_path=None): # This overrides the default behavior of resource creation because # heat doesn't accept resource_key in its request. return super(SoftwareDeployment, self).commit( - session, prepend_key=False) + session, prepend_key=False, base_path=base_path) diff --git a/openstack/orchestration/v1/stack.py b/openstack/orchestration/v1/stack.py index 97bb6e3f6..c2e464293 100644 --- a/openstack/orchestration/v1/stack.py +++ b/openstack/orchestration/v1/stack.py @@ -74,16 +74,17 @@ class Stack(resource.Resource): #: The ID of the user project created for this stack. user_project_id = resource.Body('stack_user_project_id') - def create(self, session): + def create(self, session, base_path=None): # This overrides the default behavior of resource creation because # heat doesn't accept resource_key in its request. - return super(Stack, self).create(session, prepend_key=False) + return super(Stack, self).create(session, prepend_key=False, + base_path=base_path) - def commit(self, session): + def commit(self, session, base_path=None): # This overrides the default behavior of resource creation because # heat doesn't accept resource_key in its request. return super(Stack, self).commit(session, prepend_key=False, - has_body=False) + has_body=False, base_path=None) def _action(self, session, body): """Perform stack actions""" @@ -94,10 +95,12 @@ class Stack(resource.Resource): def check(self, session): return self._action(session, {'check': ''}) - def fetch(self, session, requires_id=True, error_message=None): + def fetch(self, session, requires_id=True, + base_path=None, error_message=None): stk = super(Stack, self).fetch( session, requires_id=requires_id, + base_path=base_path, error_message=error_message) if stk and stk.status in ['DELETE_COMPLETE', 'ADOPT_COMPLETE']: raise exceptions.ResourceNotFound( diff --git a/openstack/orchestration/v1/stack_files.py b/openstack/orchestration/v1/stack_files.py index faa4c942b..98b28aa37 100644 --- a/openstack/orchestration/v1/stack_files.py +++ b/openstack/orchestration/v1/stack_files.py @@ -34,9 +34,9 @@ class StackFiles(resource.Resource): # Backwards compat stack_id = id - def fetch(self, session): + def fetch(self, session, base_path=None): # The stack files response contains a map of filenames and file # contents. - request = self._prepare_request(requires_id=False) + request = self._prepare_request(requires_id=False, base_path=base_path) resp = session.get(request.url) return resp.json() diff --git a/openstack/proxy.py b/openstack/proxy.py index 94a43480f..339f5dc93 100644 --- a/openstack/proxy.py +++ b/openstack/proxy.py @@ -161,7 +161,7 @@ class Proxy(_adapter.OpenStackSDKAdapter): return rv @_check_resource(strict=False) - def _update(self, resource_type, value, **attrs): + def _update(self, resource_type, value, base_path=None, **attrs): """Update a resource :param resource_type: The type of resource to update. @@ -169,6 +169,9 @@ class Proxy(_adapter.OpenStackSDKAdapter): :param value: The resource to update. This must either be a :class:`~openstack.resource.Resource` or an id that corresponds to a resource. + :param str base_path: Base part of the URI for updating resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :param dict attrs: Attributes to be passed onto the :meth:`~openstack.resource.Resource.update` method to be updated. These should correspond @@ -180,13 +183,16 @@ class Proxy(_adapter.OpenStackSDKAdapter): :rtype: :class:`~openstack.resource.Resource` """ res = self._get_resource(resource_type, value, **attrs) - return res.commit(self) + return res.commit(self, base_path=base_path) - def _create(self, resource_type, **attrs): + def _create(self, resource_type, base_path=None, **attrs): """Create a resource from attributes :param resource_type: The type of resource to create. :type resource_type: :class:`~openstack.resource.Resource` + :param str base_path: Base part of the URI for creating resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :param path_args: A dict containing arguments for forming the request URL, if needed. :param dict attrs: Attributes to be passed onto the @@ -200,10 +206,11 @@ class Proxy(_adapter.OpenStackSDKAdapter): :rtype: :class:`~openstack.resource.Resource` """ res = resource_type.new(**attrs) - return res.create(self) + return res.create(self, base_path=base_path) @_check_resource(strict=False) - def _get(self, resource_type, value=None, requires_id=True, **attrs): + def _get(self, resource_type, value=None, requires_id=True, + base_path=None, **attrs): """Fetch a resource :param resource_type: The type of resource to get. @@ -211,6 +218,9 @@ class Proxy(_adapter.OpenStackSDKAdapter): :param value: The value to get. Can be either the ID of a resource or a :class:`~openstack.resource.Resource` subclass. + :param str base_path: Base part of the URI for fetching resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :param dict attrs: Attributes to be passed onto the :meth:`~openstack.resource.Resource.get` method. These should correspond @@ -224,11 +234,12 @@ class Proxy(_adapter.OpenStackSDKAdapter): res = self._get_resource(resource_type, value, **attrs) return res.fetch( - self, requires_id=requires_id, + self, requires_id=requires_id, base_path=base_path, error_message="No {resource_type} found for {value}".format( resource_type=resource_type.__name__, value=value)) - def _list(self, resource_type, value=None, paginated=False, **attrs): + def _list(self, resource_type, value=None, + paginated=False, base_path=None, **attrs): """List a resource :param resource_type: The type of resource to delete. This should @@ -241,6 +252,9 @@ class Proxy(_adapter.OpenStackSDKAdapter): to be returned in one response. When set to ``True``, the resource supports data being returned across multiple pages. + :param str base_path: Base part of the URI for listing resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :param dict attrs: Attributes to be passed onto the :meth:`~openstack.resource.Resource.list` method. These should correspond to either :class:`~openstack.resource.URI` values @@ -252,9 +266,10 @@ class Proxy(_adapter.OpenStackSDKAdapter): the ``resource_type``. """ res = self._get_resource(resource_type, value, **attrs) - return res.list(self, paginated=paginated, **attrs) + return res.list(self, paginated=paginated, + base_path=base_path, **attrs) - def _head(self, resource_type, value=None, **attrs): + def _head(self, resource_type, value=None, base_path=None, **attrs): """Retrieve a resource's header :param resource_type: The type of resource to retrieve. @@ -263,6 +278,9 @@ class Proxy(_adapter.OpenStackSDKAdapter): for. Can be either the ID of a resource, a :class:`~openstack.resource.Resource` subclass, or ``None``. + :param str base_path: Base part of the URI for heading resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :param dict attrs: Attributes to be passed onto the :meth:`~openstack.resource.Resource.head` method. These should correspond to @@ -272,4 +290,4 @@ class Proxy(_adapter.OpenStackSDKAdapter): :rtype: :class:`~openstack.resource.Resource` """ res = self._get_resource(resource_type, value, **attrs) - return res.head(self) + return res.head(self, base_path=base_path) diff --git a/openstack/resource.py b/openstack/resource.py index b1c5cc2af..a0313ce95 100644 --- a/openstack/resource.py +++ b/openstack/resource.py @@ -884,7 +884,7 @@ class Resource(dict): return body def _prepare_request(self, requires_id=None, prepend_key=False, - patch=False): + patch=False, base_path=None): """Prepare a request to be sent to the server Create operations don't require an ID, but all others do, @@ -912,7 +912,9 @@ class Resource(dict): else: headers[k] = str(v) - uri = self.base_path % self._uri.attributes + if base_path is None: + base_path = self.base_path + uri = base_path % self._uri.attributes if requires_id: if self.id is None: raise exceptions.InvalidRequest( @@ -1047,7 +1049,7 @@ class Resource(dict): return actual - def create(self, session, prepend_key=True): + def create(self, session, prepend_key=True, base_path=None): """Create a remote resource based on this instance. :param session: The session to use for making this request. @@ -1055,7 +1057,9 @@ class Resource(dict): :param prepend_key: A boolean indicating whether the resource_key should be prepended in a resource creation request. Default to True. - + :param str base_path: Base part of the URI for creating resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if :data:`Resource.allow_create` is not set to ``True``. @@ -1067,13 +1071,15 @@ class Resource(dict): microversion = self._get_microversion_for(session, 'create') if self.create_method == 'PUT': request = self._prepare_request(requires_id=True, - prepend_key=prepend_key) + prepend_key=prepend_key, + base_path=base_path) response = session.put(request.url, json=request.body, headers=request.headers, microversion=microversion) elif self.create_method == 'POST': request = self._prepare_request(requires_id=False, - prepend_key=prepend_key) + prepend_key=prepend_key, + base_path=base_path) response = session.post(request.url, json=request.body, headers=request.headers, microversion=microversion) @@ -1085,13 +1091,19 @@ class Resource(dict): self._translate_response(response) return self - def fetch(self, session, requires_id=True, error_message=None): + def fetch(self, session, requires_id=True, + base_path=None, error_message=None): """Get a remote resource based on this instance. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param boolean requires_id: A boolean indicating whether resource ID should be part of the requested URI. + :param str base_path: Base part of the URI for fetching resources, if + different from + :data:`~openstack.resource.Resource.base_path`. + :param str error_message: An Error message to be returned if + requested object does not exist. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if :data:`Resource.allow_fetch` is not set to ``True``. @@ -1101,7 +1113,8 @@ class Resource(dict): if not self.allow_fetch: raise exceptions.MethodNotSupported(self, "fetch") - request = self._prepare_request(requires_id=requires_id) + request = self._prepare_request(requires_id=requires_id, + base_path=base_path) session = self._get_session(session) microversion = self._get_microversion_for(session, 'fetch') response = session.get(request.url, microversion=microversion) @@ -1113,11 +1126,14 @@ class Resource(dict): self._translate_response(response, **kwargs) return self - def head(self, session): + def head(self, session, base_path=None): """Get headers from a remote resource based on this instance. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` + :param str base_path: Base part of the URI for fetching resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if @@ -1128,7 +1144,7 @@ class Resource(dict): if not self.allow_head: raise exceptions.MethodNotSupported(self, "head") - request = self._prepare_request() + request = self._prepare_request(base_path=base_path) session = self._get_session(session) microversion = self._get_microversion_for(session, 'fetch') @@ -1141,7 +1157,7 @@ class Resource(dict): return self def commit(self, session, prepend_key=True, has_body=True, - retry_on_conflict=None): + retry_on_conflict=None, base_path=None): """Commit the state of the instance to the remote resource. :param session: The session to use for making this request. @@ -1152,6 +1168,9 @@ class Resource(dict): :param bool retry_on_conflict: Whether to enable retries on HTTP CONFLICT (409). Value of ``None`` leaves the `Adapter` defaults. + :param str base_path: Base part of the URI for modifying resources, if + different from + :data:`~openstack.resource.Resource.base_path`. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if @@ -1173,7 +1192,9 @@ class Resource(dict): if self.commit_jsonpatch: kwargs['patch'] = True - request = self._prepare_request(prepend_key=prepend_key, **kwargs) + request = self._prepare_request(prepend_key=prepend_key, + base_path=base_path, + **kwargs) session = self._get_session(session) kwargs = {} diff --git a/openstack/tests/unit/message/v2/test_proxy.py b/openstack/tests/unit/message/v2/test_proxy.py index fd9fb839c..f9ea86f71 100644 --- a/openstack/tests/unit/message/v2/test_proxy.py +++ b/openstack/tests/unit/message/v2/test_proxy.py @@ -130,7 +130,8 @@ class TestMessageProxy(test_proxy_base.TestProxyBase): def test_subscription_create(self): self._verify("openstack.message.v2.subscription.Subscription.create", self.proxy.create_subscription, - method_args=["test_queue"]) + method_args=["test_queue"], + expected_kwargs={"base_path": None}) @mock.patch.object(proxy_base.Proxy, '_get_resource') def test_subscription_get(self, mock_get_resource): @@ -175,7 +176,8 @@ class TestMessageProxy(test_proxy_base.TestProxyBase): def test_claim_create(self): self._verify("openstack.message.v2.claim.Claim.create", self.proxy.create_claim, - method_args=["test_queue"]) + method_args=["test_queue"], + expected_kwargs={"base_path": None}) def test_claim_get(self): self._verify2("openstack.proxy.Proxy._get", diff --git a/openstack/tests/unit/orchestration/v1/test_stack.py b/openstack/tests/unit/orchestration/v1/test_stack.py index c25084096..967265ce0 100644 --- a/openstack/tests/unit/orchestration/v1/test_stack.py +++ b/openstack/tests/unit/orchestration/v1/test_stack.py @@ -93,7 +93,8 @@ class TestStack(base.TestCase): res = sot.create(sess) - mock_create.assert_called_once_with(sess, prepend_key=False) + mock_create.assert_called_once_with(sess, prepend_key=False, + base_path=None) self.assertEqual(mock_create.return_value, res) @mock.patch.object(resource.Resource, 'commit') @@ -104,7 +105,8 @@ class TestStack(base.TestCase): res = sot.commit(sess) mock_commit.assert_called_once_with(sess, prepend_key=False, - has_body=False) + has_body=False, + base_path=None) self.assertEqual(mock_commit.return_value, res) def test_check(self): diff --git a/openstack/tests/unit/test_proxy.py b/openstack/tests/unit/test_proxy.py index c03759d07..297a06a0a 100644 --- a/openstack/tests/unit/test_proxy.py +++ b/openstack/tests/unit/test_proxy.py @@ -254,13 +254,22 @@ class TestProxyUpdate(base.TestCase): self.assertEqual(rv, self.fake_result) self.res._update.assert_called_once_with(**self.attrs) - self.res.commit.assert_called_once_with(self.sot) + self.res.commit.assert_called_once_with(self.sot, base_path=None) + + def test_update_resource_override_base_path(self): + base_path = 'dummy' + rv = self.sot._update(UpdateableResource, self.res, + base_path=base_path, **self.attrs) + + self.assertEqual(rv, self.fake_result) + self.res._update.assert_called_once_with(**self.attrs) + self.res.commit.assert_called_once_with(self.sot, base_path=base_path) def test_update_id(self): rv = self.sot._update(UpdateableResource, self.fake_id, **self.attrs) self.assertEqual(rv, self.fake_result) - self.res.commit.assert_called_once_with(self.sot) + self.res.commit.assert_called_once_with(self.sot, base_path=None) class TestProxyCreate(base.TestCase): @@ -284,7 +293,18 @@ class TestProxyCreate(base.TestCase): self.assertEqual(rv, self.fake_result) CreateableResource.new.assert_called_once_with(**attrs) - self.res.create.assert_called_once_with(self.sot) + self.res.create.assert_called_once_with(self.sot, base_path=None) + + def test_create_attributes_override_base_path(self): + CreateableResource.new = mock.Mock(return_value=self.res) + + base_path = 'dummy' + attrs = {"x": 1, "y": 2, "z": 3} + rv = self.sot._create(CreateableResource, base_path=base_path, **attrs) + + self.assertEqual(rv, self.fake_result) + CreateableResource.new.assert_called_once_with(**attrs) + self.res.create.assert_called_once_with(self.sot, base_path=base_path) class TestProxyGet(base.TestCase): @@ -309,6 +329,7 @@ class TestProxyGet(base.TestCase): self.res.fetch.assert_called_with( self.sot, requires_id=True, + base_path=None, error_message=mock.ANY) self.assertEqual(rv, self.fake_result) @@ -318,7 +339,7 @@ class TestProxyGet(base.TestCase): self.res._update.assert_called_once_with(**args) self.res.fetch.assert_called_with( - self.sot, requires_id=True, + self.sot, requires_id=True, base_path=None, error_message=mock.ANY) self.assertEqual(rv, self.fake_result) @@ -327,7 +348,18 @@ class TestProxyGet(base.TestCase): RetrieveableResource.new.assert_called_with(id=self.fake_id) self.res.fetch.assert_called_with( - self.sot, requires_id=True, + self.sot, requires_id=True, base_path=None, + error_message=mock.ANY) + self.assertEqual(rv, self.fake_result) + + def test_get_base_path(self): + base_path = 'dummy' + rv = self.sot._get(RetrieveableResource, self.fake_id, + base_path=base_path) + + RetrieveableResource.new.assert_called_with(id=self.fake_id) + self.res.fetch.assert_called_with( + self.sot, requires_id=True, base_path=base_path, error_message=mock.ANY) self.assertEqual(rv, self.fake_result) @@ -354,12 +386,13 @@ class TestProxyList(base.TestCase): ListableResource.list = mock.Mock() ListableResource.list.return_value = self.fake_response - def _test_list(self, paginated): - rv = self.sot._list(ListableResource, paginated=paginated, **self.args) + def _test_list(self, paginated, base_path=None): + rv = self.sot._list(ListableResource, paginated=paginated, + base_path=base_path, **self.args) self.assertEqual(self.fake_response, rv) ListableResource.list.assert_called_once_with( - self.sot, paginated=paginated, **self.args) + self.sot, paginated=paginated, base_path=base_path, **self.args) def test_list_paginated(self): self._test_list(True) @@ -367,6 +400,9 @@ class TestProxyList(base.TestCase): def test_list_non_paginated(self): self._test_list(False) + def test_list_override_base_path(self): + self._test_list(False, base_path='dummy') + class TestProxyHead(base.TestCase): @@ -388,12 +424,19 @@ class TestProxyHead(base.TestCase): def test_head_resource(self): rv = self.sot._head(HeadableResource, self.res) - self.res.head.assert_called_with(self.sot) + self.res.head.assert_called_with(self.sot, base_path=None) + self.assertEqual(rv, self.fake_result) + + def test_head_resource_base_path(self): + base_path = 'dummy' + rv = self.sot._head(HeadableResource, self.res, base_path=base_path) + + self.res.head.assert_called_with(self.sot, base_path=base_path) self.assertEqual(rv, self.fake_result) def test_head_id(self): rv = self.sot._head(HeadableResource, self.fake_id) HeadableResource.new.assert_called_with(id=self.fake_id) - self.res.head.assert_called_with(self.sot) + self.res.head.assert_called_with(self.sot, base_path=None) self.assertEqual(rv, self.fake_result) diff --git a/openstack/tests/unit/test_proxy_base.py b/openstack/tests/unit/test_proxy_base.py index c2c0dab97..849597d6c 100644 --- a/openstack/tests/unit/test_proxy_base.py +++ b/openstack/tests/unit/test_proxy_base.py @@ -76,7 +76,17 @@ class TestProxyBase(base.TestCase): else: self.assertEqual(expected_result, test_method(*method_args, **method_kwargs)) - mocked.assert_called_with(*expected_args, **expected_kwargs) + # Check how the mock was called in detail + (called_args, called_kwargs) = mocked.call_args + self.assertEqual(list(called_args), expected_args) + base_path = expected_kwargs.get('base_path', None) + # NOTE(gtema): if base_path is not in epected_kwargs or empty + # exclude it from the comparison, since some methods might + # still invoke method with None value + if not base_path: + expected_kwargs.pop('base_path', None) + called_kwargs.pop('base_path', None) + self.assertDictEqual(called_kwargs, expected_kwargs) else: self.assertEqual(expected_result, test_method()) mocked.assert_called_with(test_method.__self__) @@ -87,7 +97,9 @@ class TestProxyBase(base.TestCase): the_kwargs = {"x": 1, "y": 2, "z": 3} method_kwargs = kwargs.pop("method_kwargs", the_kwargs) expected_args = [resource_type] - expected_kwargs = kwargs.pop("expected_kwargs", the_kwargs) + # Default the_kwargs should be copied, since we might need to extend it + expected_kwargs = kwargs.pop("expected_kwargs", the_kwargs.copy()) + expected_kwargs["base_path"] = kwargs.pop("base_path", None) self._verify2(mock_method, test_method, expected_result=expected_result, @@ -147,6 +159,7 @@ class TestProxyBase(base.TestCase): proxy._get(resource_type) res.fetch.assert_called_once_with( proxy, requires_id=True, + base_path=None, error_message=mock.ANY) def verify_head(self, test_method, resource_type, @@ -216,7 +229,8 @@ class TestProxyBase(base.TestCase): method_kwargs = kwargs.pop("method_kwargs", {}) method_kwargs.update({"x": 1, "y": 2, "z": 3}) expected_args = kwargs.pop("expected_args", ["resource_or_id"]) - expected_kwargs = method_kwargs.copy() + expected_kwargs = kwargs.pop("expected_kwargs", method_kwargs.copy()) + expected_kwargs["base_path"] = kwargs.pop("base_path", None) self._add_path_args_for_verify(path_args, method_args, expected_kwargs, value=value) diff --git a/openstack/tests/unit/test_resource.py b/openstack/tests/unit/test_resource.py index 8dafcc08f..f46e9ea68 100644 --- a/openstack/tests/unit/test_resource.py +++ b/openstack/tests/unit/test_resource.py @@ -1076,16 +1076,18 @@ class TestResourceActions(base.TestCase): self.session.get_endpoint_data.return_value = self.endpoint_data def _test_create(self, cls, requires_id=False, prepend_key=False, - microversion=None): + microversion=None, base_path=None): id = "id" if requires_id else None sot = cls(id=id) sot._prepare_request = mock.Mock(return_value=self.request) sot._translate_response = mock.Mock() - result = sot.create(self.session, prepend_key=prepend_key) + result = sot.create(self.session, prepend_key=prepend_key, + base_path=base_path) sot._prepare_request.assert_called_once_with( - requires_id=requires_id, prepend_key=prepend_key) + requires_id=requires_id, prepend_key=prepend_key, + base_path=base_path) if requires_id: self.session.put.assert_called_once_with( self.request.url, @@ -1130,10 +1132,21 @@ class TestResourceActions(base.TestCase): self._test_create(Test, requires_id=False, prepend_key=True) + def test_post_create_base_path(self): + class Test(resource.Resource): + service = self.service_name + base_path = self.base_path + allow_create = True + create_method = 'POST' + + self._test_create(Test, requires_id=False, prepend_key=True, + base_path='dummy') + def test_fetch(self): result = self.sot.fetch(self.session) - self.sot._prepare_request.assert_called_once_with(requires_id=True) + self.sot._prepare_request.assert_called_once_with( + requires_id=True, base_path=None) self.session.get.assert_called_once_with( self.request.url, microversion=None) @@ -1154,7 +1167,8 @@ class TestResourceActions(base.TestCase): result = sot.fetch(self.session) - sot._prepare_request.assert_called_once_with(requires_id=True) + sot._prepare_request.assert_called_once_with( + requires_id=True, base_path=None) self.session.get.assert_called_once_with( self.request.url, microversion='1.42') @@ -1165,7 +1179,20 @@ class TestResourceActions(base.TestCase): def test_fetch_not_requires_id(self): result = self.sot.fetch(self.session, False) - self.sot._prepare_request.assert_called_once_with(requires_id=False) + self.sot._prepare_request.assert_called_once_with( + requires_id=False, base_path=None) + self.session.get.assert_called_once_with( + self.request.url, microversion=None) + + self.sot._translate_response.assert_called_once_with(self.response) + self.assertEqual(result, self.sot) + + def test_fetch_base_path(self): + result = self.sot.fetch(self.session, False, base_path='dummy') + + self.sot._prepare_request.assert_called_once_with( + requires_id=False, + base_path='dummy') self.session.get.assert_called_once_with( self.request.url, microversion=None) @@ -1175,7 +1202,21 @@ class TestResourceActions(base.TestCase): def test_head(self): result = self.sot.head(self.session) - self.sot._prepare_request.assert_called_once_with() + self.sot._prepare_request.assert_called_once_with(base_path=None) + self.session.head.assert_called_once_with( + self.request.url, + headers={"Accept": ""}, + microversion=None) + + self.assertIsNone(self.sot.microversion) + self.sot._translate_response.assert_called_once_with( + self.response, has_body=False) + self.assertEqual(result, self.sot) + + def test_head_base_path(self): + result = self.sot.head(self.session, base_path='dummy') + + self.sot._prepare_request.assert_called_once_with(base_path='dummy') self.session.head.assert_called_once_with( self.request.url, headers={"Accept": ""}, @@ -1199,7 +1240,7 @@ class TestResourceActions(base.TestCase): result = sot.head(self.session) - sot._prepare_request.assert_called_once_with() + sot._prepare_request.assert_called_once_with(base_path=None) self.session.head.assert_called_once_with( self.request.url, headers={"Accept": ""}, @@ -1212,7 +1253,7 @@ class TestResourceActions(base.TestCase): def _test_commit(self, commit_method='PUT', prepend_key=True, has_body=True, microversion=None, - commit_args=None, expected_args=None): + commit_args=None, expected_args=None, base_path=None): self.sot.commit_method = commit_method # Need to make sot look dirty so we can attempt an update @@ -1220,10 +1261,11 @@ class TestResourceActions(base.TestCase): self.sot._body.dirty = mock.Mock(return_value={"x": "y"}) self.sot.commit(self.session, prepend_key=prepend_key, - has_body=has_body, **(commit_args or {})) + has_body=has_body, base_path=base_path, + **(commit_args or {})) self.sot._prepare_request.assert_called_once_with( - prepend_key=prepend_key) + prepend_key=prepend_key, base_path=base_path) if commit_method == 'PATCH': self.session.patch.assert_called_once_with( @@ -1252,6 +1294,10 @@ class TestResourceActions(base.TestCase): self._test_commit( commit_method='PATCH', prepend_key=False, has_body=False) + def test_commit_base_path(self): + self._test_commit(commit_method='PUT', prepend_key=True, has_body=True, + base_path='dummy') + def test_commit_patch_retry_on_conflict(self): self._test_commit( commit_method='PATCH', diff --git a/openstack/workflow/v2/execution.py b/openstack/workflow/v2/execution.py index 6a317841c..5e8787ba9 100644 --- a/openstack/workflow/v2/execution.py +++ b/openstack/workflow/v2/execution.py @@ -50,9 +50,10 @@ class Execution(resource.Resource): #: The time at which the Execution was updated updated_at = resource.Body("updated_at") - def create(self, session, prepend_key=True): + def create(self, session, prepend_key=True, base_path=None): request = self._prepare_request(requires_id=False, - prepend_key=prepend_key) + prepend_key=prepend_key, + base_path=base_path) request_body = request.body["execution"] response = session.post(request.url, diff --git a/openstack/workflow/v2/workflow.py b/openstack/workflow/v2/workflow.py index 6be931340..e196c19b4 100644 --- a/openstack/workflow/v2/workflow.py +++ b/openstack/workflow/v2/workflow.py @@ -46,9 +46,10 @@ class Workflow(resource.Resource): #: The time at which the workflow was created updated_at = resource.Body("updated_at") - def create(self, session, prepend_key=True): + def create(self, session, prepend_key=True, base_path=None): request = self._prepare_request(requires_id=False, - prepend_key=prepend_key) + prepend_key=prepend_key, + base_path=base_path) headers = { "Content-Type": 'text/plain'