Add 'all_projects' support to proxy layers

A number of compute and block storage APIs support this parameter. Plumb
them through. This turns out to be a bigger change than expected as it
highlights a number of gaps in our documentation.

Change-Id: Id4049193ab2e6c173208692ed46fc5f9491da3dc
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2022-12-16 12:07:25 +00:00
parent 0c54b0993e
commit 6fee18fcd1
10 changed files with 448 additions and 113 deletions

View File

@ -37,7 +37,13 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
"""
return self._get(_snapshot.Snapshot, snapshot)
def find_snapshot(self, name_or_id, ignore_missing=True):
def find_snapshot(
self,
name_or_id,
ignore_missing=True,
*,
all_projects=False,
):
"""Find a single snapshot
:param snapshot: The name or ID a snapshot
@ -45,6 +51,9 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:class:`~openstack.exceptions.ResourceNotFound` will be raised
when the snapshot does not exist. When set to ``True``, None will
be returned when attempting to find a nonexistent resource.
:param bool all_projects: When set to ``True``, search for snapshot by
name across all projects. Note that this will likely result in
a higher chance of duplicates. Admin-only by default.
:returns: One :class:`~openstack.block_storage.v2.snapshot.Snapshot` or
None.
@ -53,13 +62,17 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
query = {}
if all_projects:
query['all_projects'] = True
return self._find(
_snapshot.Snapshot,
name_or_id,
ignore_missing=ignore_missing,
**query,
)
def snapshots(self, details=True, **query):
def snapshots(self, *, details=True, all_projects=False, **query):
"""Retrieve a generator of snapshots
:param bool details: When set to ``False``
@ -67,17 +80,20 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
objects will be returned. The default, ``True``, will cause
:class:`~openstack.block_storage.v2.snapshot.SnapshotDetail`
objects to be returned.
:param bool all_projects: When set to ``True``, list snapshots from all
projects. Admin-only by default.
:param kwargs query: Optional query parameters to be sent to limit
the snapshots being returned. Available parameters include:
* name: Name of the snapshot as a string.
* all_projects: Whether return the snapshots in all projects.
* volume_id: volume id of a snapshot.
* status: Value of the status of the snapshot so that you can
filter on "available" for example.
:returns: A generator of snapshot objects.
"""
if all_projects:
query['all_projects'] = True
base_path = '/snapshots/detail' if details else None
return self._list(_snapshot.Snapshot, base_path=base_path, **query)
@ -221,37 +237,59 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
"""
return self._get(_volume.Volume, volume)
def find_volume(self, name_or_id, ignore_missing=True):
def find_volume(
self,
name_or_id,
ignore_missing=True,
*,
all_projects=False,
):
"""Find a single volume
:param snapshot: The name or ID a volume
:param volume: The name or ID a volume
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised
when the volume does not exist.
:param bool all_projects: When set to ``True``, search for volume by
name across all projects. Note that this will likely result in
a higher chance of duplicates. Admin-only by default.
:returns: One :class:`~openstack.block_storage.v2.volume.Volume`
:returns: One :class:`~openstack.block_storage.v2.volume.Volume` or
None.
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_volume.Volume, name_or_id,
ignore_missing=ignore_missing)
query = {}
if all_projects:
query['all_projects'] = True
return self._find(
_volume.Volume,
name_or_id,
ignore_missing=ignore_missing,
**query,
)
def volumes(self, details=True, **query):
def volumes(self, *, details=True, all_projects=False, **query):
"""Retrieve a generator of volumes
:param bool details: When set to ``False`` no extended attributes
will be returned. The default, ``True``, will cause objects with
additional attributes to be returned.
:param bool all_projects: When set to ``True``, list volumes from all
projects. Admin-only by default.
:param kwargs query: Optional query parameters to be sent to limit
the volumes being returned. Available parameters include:
* name: Name of the volume as a string.
* all_projects: Whether return the volumes in all projects
* status: Value of the status of the volume so that you can filter
on "available" for example.
:returns: A generator of volume objects.
"""
if all_projects:
query['all_projects'] = True
base_path = '/volumes/detail' if details else None
return self._list(_volume.Volume, base_path=base_path, **query)

View File

@ -62,33 +62,53 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
"""
return self._get(_snapshot.Snapshot, snapshot)
def find_snapshot(self, name_or_id, ignore_missing=True):
def find_snapshot(
self,
name_or_id,
ignore_missing=True,
*,
all_projects=False,
):
"""Find a single snapshot
:param snapshot: The name or ID a snapshot
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised
when the snapshot does not exist.
when the snapshot does not exist. When set to ``True``, None will
be returned when attempting to find a nonexistent resource.
:param bool all_projects: When set to ``True``, search for snapshot by
name across all projects. Note that this will likely result in
a higher chance of duplicates. Admin-only by default.
:returns: One :class:`~openstack.block_storage.v3.snapshot.Snapshot`
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_snapshot.Snapshot, name_or_id,
ignore_missing=ignore_missing)
query = {}
if all_projects:
query['all_projects'] = True
return self._find(
_snapshot.Snapshot,
name_or_id,
ignore_missing=ignore_missing,
**query,
)
def snapshots(self, details=True, **query):
def snapshots(self, *, details=True, all_projects=False, **query):
"""Retrieve a generator of snapshots
:param bool details: When set to ``False`` :class:
`~openstack.block_storage.v3.snapshot.Snapshot`
objects will be returned. The default, ``True``, will cause
more attributes to be returned.
:param bool all_projects: When set to ``True``, list snapshots from all
projects. Admin-only by default.
:param kwargs query: Optional query parameters to be sent to limit
the snapshots being returned. Available parameters include:
* name: Name of the snapshot as a string.
* all_projects: Whether return the snapshots in all projects.
* project_id: Filter the snapshots by project.
* volume_id: volume id of a snapshot.
* status: Value of the status of the snapshot so that you can
@ -96,6 +116,8 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:returns: A generator of snapshot objects.
"""
if all_projects:
query['all_projects'] = True
base_path = '/snapshots/detail' if details else None
return self._list(_snapshot.Snapshot, base_path=base_path, **query)
@ -237,14 +259,19 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:param snapshot: The name or ID a volume type
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised
when the type does not exist.
when the volume type does not exist.
:returns: One :class:`~openstack.block_storage.v3.type.Type`
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_type.Type, name_or_id,
ignore_missing=ignore_missing)
return self._find(
_type.Type,
name_or_id,
ignore_missing=ignore_missing,
)
def types(self, **query):
"""Retrieve a generator of volume types
@ -469,38 +496,59 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
"""
return self._get(_volume.Volume, volume)
def find_volume(self, name_or_id, ignore_missing=True):
def find_volume(
self,
name_or_id,
ignore_missing=True,
*,
all_projects=False,
):
"""Find a single volume
:param snapshot: The name or ID a volume
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised
when the volume does not exist.
:param bool all_projects: When set to ``True``, search for volume by
name across all projects. Note that this will likely result in
a higher chance of duplicates. Admin-only by default.
:returns: One :class:`~openstack.block_storage.v3.volume.Volume`
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_volume.Volume, name_or_id,
ignore_missing=ignore_missing,
list_base_path='/volumes/detail')
query = {}
if all_projects:
query['all_projects'] = True
return self._find(
_volume.Volume,
name_or_id,
ignore_missing=ignore_missing,
list_base_path='/volumes/detail',
**query,
)
def volumes(self, details=True, **query):
def volumes(self, *, details=True, all_projects=False, **query):
"""Retrieve a generator of volumes
:param bool details: When set to ``False`` no extended attributes
will be returned. The default, ``True``, will cause objects with
additional attributes to be returned.
:param bool all_projects: When set to ``True``, list volumes from all
projects. Admin-only by default.
:param kwargs query: Optional query parameters to be sent to limit
the volumes being returned. Available parameters include:
* name: Name of the volume as a string.
* all_projects: Whether return the volumes in all projects
* status: Value of the status of the volume so that you can filter
on "available" for example.
:returns: A generator of volume objects.
"""
if all_projects:
query['all_projects'] = True
base_path = '/volumes/detail' if details else None
return self._list(_volume.Volume, base_path=base_path, **query)
@ -872,7 +920,7 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
return self._list(_stats.Pools, **query)
# ====== BACKUPS ======
def backups(self, details=True, **query):
def backups(self, *, details=True, **query):
"""Retrieve a generator of backups
:param bool details: When set to ``False``
@ -921,9 +969,14 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:returns: One :class:`~openstack.block_storage.v3.backup.Backup`
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_backup.Backup, name_or_id,
ignore_missing=ignore_missing)
return self._find(
_backup.Backup,
name_or_id,
ignore_missing=ignore_missing,
)
def create_backup(self, **attrs):
"""Create a new Backup from attributes with native API
@ -1032,11 +1085,16 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:returns: One :class:`~openstack.block_storage.v3.group.Group`
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(
_group.Group, name_or_id, ignore_missing=ignore_missing)
_group.Group,
name_or_id,
ignore_missing=ignore_missing,
)
def groups(self, details=True, **query):
def groups(self, *, details=True, **query):
"""Retrieve a generator of groups
:param bool details: When set to ``False``, no additional details will
@ -1155,12 +1213,16 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:returns: One :class:`~openstack.block_storage.v3.group_snapshot`
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(
_group_snapshot.GroupSnapshot, name_or_id,
ignore_missing=ignore_missing)
_group_snapshot.GroupSnapshot,
name_or_id,
ignore_missing=ignore_missing,
)
def group_snapshots(self, details=True, **query):
def group_snapshots(self, *, details=True, **query):
"""Retrieve a generator of group snapshots
:param bool details: When ``True``, returns
@ -1245,9 +1307,14 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:class:`~openstack.block_storage.v3.group_type.GroupType`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(
_group_type.GroupType, name_or_id, ignore_missing=ignore_missing)
_group_type.GroupType,
name_or_id,
ignore_missing=ignore_missing,
)
def group_types(self, **query):
"""Retrive a generator of group types

View File

@ -78,11 +78,19 @@ class Proxy(proxy.Proxy):
raised when the resource does not exist.
When set to ``True``, None will be returned when
attempting to find a nonexistent resource.
:returns: One :class:`~openstack.compute.v2.extension.Extension` or
None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(extension.Extension, name_or_id,
ignore_missing=ignore_missing)
return self._find(
extension.Extension,
name_or_id,
ignore_missing=ignore_missing,
)
def extensions(self):
"""Retrieve a generator of extensions
@ -94,8 +102,15 @@ class Proxy(proxy.Proxy):
# ========== Flavors ==========
def find_flavor(self, name_or_id, ignore_missing=True,
get_extra_specs=False, **query):
# TODO(stephenfin): Drop 'query' parameter or apply it consistently
def find_flavor(
self,
name_or_id,
ignore_missing=True,
*,
get_extra_specs=False,
**query,
):
"""Find a single flavor
:param name_or_id: The name or ID of a flavor.
@ -106,14 +121,21 @@ class Proxy(proxy.Proxy):
:param bool get_extra_specs: When set to ``True`` and extra_specs not
present in the response will invoke additional API call to fetch
extra_specs.
:param kwargs query: Optional query parameters to be sent to limit
the flavors being returned.
:returns: One :class:`~openstack.compute.v2.flavor.Flavor` or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
flavor = self._find(
_flavor.Flavor, name_or_id, ignore_missing=ignore_missing, **query)
_flavor.Flavor,
name_or_id,
ignore_missing=ignore_missing,
**query,
)
if flavor and get_extra_specs and not flavor.extra_specs:
flavor = flavor.fetch_extra_specs(self)
return flavor
@ -325,11 +347,19 @@ class Proxy(proxy.Proxy):
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource does not exist. When set to ``True``, None will be
returned when attempting to find a nonexistent resource.
:returns: One :class:`~openstack.compute.v2.aggregate.Aggregate`
or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_aggregate.Aggregate, name_or_id,
ignore_missing=ignore_missing)
return self._find(
_aggregate.Aggregate,
name_or_id,
ignore_missing=ignore_missing,
)
def create_aggregate(self, **attrs):
"""Create a new host aggregate from attributes
@ -463,15 +493,23 @@ class Proxy(proxy.Proxy):
raised when the resource does not exist.
When set to ``True``, None will be returned when
attempting to find a nonexistent resource.
:returns: One :class:`~openstack.compute.v2.image.Image` or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
warnings.warn(
'This API is a proxy to the image service and has been '
'deprecated; use the image service proxy API instead',
DeprecationWarning,
)
return self._find(_image.Image, name_or_id,
ignore_missing=ignore_missing)
return self._find(
_image.Image,
name_or_id,
ignore_missing=ignore_missing,
)
def get_image(self, image):
"""Get a single image
@ -615,7 +653,7 @@ class Proxy(proxy.Proxy):
attrs = {'user_id': user_id} if user_id else {}
return self._get(_keypair.Keypair, keypair, **attrs)
def find_keypair(self, name_or_id, ignore_missing=True, user_id=None):
def find_keypair(self, name_or_id, ignore_missing=True, *, user_id=None):
"""Find a single keypair
:param name_or_id: The name or ID of a keypair.
@ -626,11 +664,18 @@ class Proxy(proxy.Proxy):
:param str user_id: Optional user_id owning the keypair
:returns: One :class:`~openstack.compute.v2.keypair.Keypair` or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
attrs = {'user_id': user_id} if user_id else {}
return self._find(_keypair.Keypair, name_or_id,
ignore_missing=ignore_missing,
**attrs)
return self._find(
_keypair.Keypair,
name_or_id,
ignore_missing=ignore_missing,
**attrs,
)
def keypairs(self, **query):
"""Return a generator of keypairs
@ -690,20 +735,40 @@ class Proxy(proxy.Proxy):
else:
self._delete(_server.Server, server, ignore_missing=ignore_missing)
def find_server(self, name_or_id, ignore_missing=True):
def find_server(
self,
name_or_id,
ignore_missing=True,
*,
all_projects=False,
):
"""Find a single server
:param name_or_id: The name or ID of a server.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be
raised when the resource does not exist.
When set to ``True``, None will be returned when
attempting to find a nonexistent resource.
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource does not exist. When set to ``True``, None will be
returned when attempting to find a nonexistent resource.
:param bool all_projects: When set to ``True``, search for server
by name across all projects. Note that this will likely result in a
higher chance of duplicates. Admin-only by default.
:returns: One :class:`~openstack.compute.v2.server.Server` or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_server.Server, name_or_id,
ignore_missing=ignore_missing,
list_base_path='/servers/detail')
query = {}
if all_projects:
query['all_projects'] = True
return self._find(
_server.Server,
name_or_id,
ignore_missing=ignore_missing,
list_base_path='/servers/detail',
**query,
)
def get_server(self, server):
"""Get a single server
@ -723,6 +788,8 @@ class Proxy(proxy.Proxy):
:param bool details: When set to ``False``
instances with only basic data will be returned. The default,
``True``, will cause instances with full data to be returned.
:param bool all_projects: When set to ``True``, lists servers from all
projects. Admin-only by default.
:param kwargs query: Optional query parameters to be sent to limit
the servers being returned. Available parameters can be seen
under https://docs.openstack.org/api-ref/compute/#list-servers
@ -1374,21 +1441,40 @@ class Proxy(proxy.Proxy):
self._delete(_server_group.ServerGroup, server_group,
ignore_missing=ignore_missing)
def find_server_group(self, name_or_id, ignore_missing=True):
def find_server_group(
self,
name_or_id,
ignore_missing=True,
*,
all_projects=False,
):
"""Find a single server group
:param name_or_id: The name or ID of a server group.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be
raised when the resource does not exist.
When set to ``True``, None will be returned when
attempting to find a nonexistent resource.
:returns:
One :class:`~openstack.compute.v2.server_group.ServerGroup` object
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource does not exist. When set to ``True``, None will be
returned when attempting to find a nonexistent resource.
:param bool all_projects: When set to ``True``, search for server
groups by name across all projects. Note that this will likely
result in a higher chance of duplicates. Admin-only by default.
:returns: One :class:`~openstack.compute.v2.server_group.ServerGroup`
or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_server_group.ServerGroup, name_or_id,
ignore_missing=ignore_missing)
query = {}
if all_projects:
query['all_projects'] = True
return self._find(
_server_group.ServerGroup,
name_or_id,
ignore_missing=ignore_missing,
**query,
)
def get_server_group(self, server_group):
"""Get a single server group
@ -1404,21 +1490,25 @@ class Proxy(proxy.Proxy):
"""
return self._get(_server_group.ServerGroup, server_group)
def server_groups(self, **query):
def server_groups(self, *, all_projects=False, **query):
"""Return a generator of server groups
:param bool all_projects: When set to ``True``, lists servers groups
from all projects. Admin-only by default.
:param kwargs query: Optional query parameters to be sent to limit
the resources being returned.
:returns: A generator of ServerGroup objects
:rtype: :class:`~openstack.compute.v2.server_group.ServerGroup`
"""
if all_projects:
query['all_projects'] = True
return self._list(_server_group.ServerGroup, **query)
# ========== Hypervisors ==========
def hypervisors(self, details=False, **query):
"""Return a generator of hypervisor
"""Return a generator of hypervisors
:param bool details: When set to the default, ``False``,
:class:`~openstack.compute.v2.hypervisor.Hypervisor`
@ -1438,20 +1528,36 @@ class Proxy(proxy.Proxy):
pattern=query.pop('hypervisor_hostname_pattern'))
return self._list(_hypervisor.Hypervisor, base_path=base_path, **query)
def find_hypervisor(self, name_or_id, ignore_missing=True, details=True):
"""Find a hypervisor from name or id to get the corresponding info
def find_hypervisor(
self,
name_or_id,
ignore_missing=True,
*,
details=True,
):
"""Find a single hypervisor
:param name_or_id: The name or id of a hypervisor
:param name_or_id: The name or ID of a hypervisor
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource does not exist. When set to ``True``, None will be
returned when attempting to find a nonexistent resource.
:returns:
One: class:`~openstack.compute.v2.hypervisor.Hypervisor` object
:returns: One: class:`~openstack.compute.v2.hypervisor.Hypervisor`
or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
list_base_path = '/os-hypervisors/detail' if details else None
return self._find(_hypervisor.Hypervisor, name_or_id,
list_base_path=list_base_path,
ignore_missing=ignore_missing)
return self._find(
_hypervisor.Hypervisor,
name_or_id,
list_base_path=list_base_path,
ignore_missing=ignore_missing,
)
def get_hypervisor(self, hypervisor):
"""Get a single hypervisor
@ -1580,9 +1686,11 @@ class Proxy(proxy.Proxy):
attempting to find a nonexistent resource.
:param dict query: Additional attributes like 'host'
:returns:
One: class:`~openstack.compute.v2.hypervisor.Hypervisor` object
or None
:returns: One: class:`~openstack.compute.v2.service.Service` or None
:raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(
_service.Service,

View File

@ -2192,6 +2192,7 @@ class Resource(dict):
list_base_path=None,
*,
microversion=None,
all_projects=None,
**params,
):
"""Find a resource by its name or id.
@ -2220,10 +2221,13 @@ class Resource(dict):
is found and ignore_missing is ``False``.
"""
session = cls._get_session(session)
# Try to short-circuit by looking directly for a matching ID.
try:
match = cls.existing(
id=name_or_id, connection=session._get_connection(), **params
id=name_or_id,
connection=session._get_connection(),
**params,
)
return match.fetch(session, microversion=microversion, **params)
except (exceptions.NotFoundException, exceptions.BadRequestException):
@ -2234,6 +2238,12 @@ class Resource(dict):
if list_base_path:
params['base_path'] = list_base_path
# all_projects is a special case that is used by multiple services. We
# handle it here since it doesn't make sense to pass it to the .fetch
# call above
if all_projects is not None:
params['all_projects'] = all_projects
if (
'name' in cls._query_mapping._mapping.keys()
and 'name' not in params
@ -2248,6 +2258,7 @@ class Resource(dict):
if ignore_missing:
return None
raise exceptions.ResourceNotFound(
"No %s found for %s" % (cls.__name__, name_or_id)
)

View File

@ -23,8 +23,9 @@ from openstack.tests.unit import test_proxy_base
class TestVolumeProxy(test_proxy_base.TestProxyBase):
def setUp(self):
super(TestVolumeProxy, self).setUp()
super().setUp()
self.proxy = _proxy.Proxy(self.session)
@ -34,18 +35,31 @@ class TestVolume(TestVolumeProxy):
self.verify_get(self.proxy.get_volume, volume.Volume)
def test_volume_find(self):
self.verify_find(self.proxy.find_volume, volume.Volume)
self.verify_find(
self.proxy.find_volume,
volume.Volume,
method_kwargs={'all_projects': True},
expected_kwargs={'all_projects': True},
)
def test_volumes_detailed(self):
self.verify_list(self.proxy.volumes, volume.Volume,
method_kwargs={"details": True, "query": 1},
expected_kwargs={"query": 1,
"base_path": "/volumes/detail"})
self.verify_list(
self.proxy.volumes,
volume.Volume,
method_kwargs={"details": True, "all_projects": True},
expected_kwargs={
"base_path": "/volumes/detail",
"all_projects": True,
}
)
def test_volumes_not_detailed(self):
self.verify_list(self.proxy.volumes, volume.Volume,
method_kwargs={"details": False, "query": 1},
expected_kwargs={"query": 1})
self.verify_list(
self.proxy.volumes,
volume.Volume,
method_kwargs={"details": False, "all_projects": True},
expected_kwargs={"all_projects": True},
)
def test_volume_create_attrs(self):
self.verify_create(self.proxy.create_volume, volume.Volume)
@ -197,6 +211,7 @@ class TestVolumeActions(TestVolumeProxy):
class TestBackup(TestVolumeProxy):
def test_backups_detailed(self):
self.verify_list(
self.proxy.backups,
@ -257,21 +272,33 @@ class TestBackup(TestVolumeProxy):
class TestSnapshot(TestVolumeProxy):
def test_snapshot_get(self):
self.verify_get(self.proxy.get_snapshot, snapshot.Snapshot)
def test_snapshot_find(self):
self.verify_find(self.proxy.find_snapshot, snapshot.Snapshot)
self.verify_find(
self.proxy.find_snapshot,
snapshot.Snapshot,
method_kwargs={'all_projects': True},
expected_kwargs={'all_projects': True},
)
def test_snapshots_detailed(self):
self.verify_list(self.proxy.snapshots, snapshot.SnapshotDetail,
method_kwargs={"details": True, "query": 1},
expected_kwargs={"query": 1})
self.verify_list(
self.proxy.snapshots,
snapshot.SnapshotDetail,
method_kwargs={"details": True, "all_projects": True},
expected_kwargs={"all_projects": True},
)
def test_snapshots_not_detailed(self):
self.verify_list(self.proxy.snapshots, snapshot.Snapshot,
method_kwargs={"details": False, "query": 1},
expected_kwargs={"query": 1})
self.verify_list(
self.proxy.snapshots,
snapshot.Snapshot,
method_kwargs={"details": False, "all_projects": True},
expected_kwargs={"all_projects": 1},
)
def test_snapshot_create_attrs(self):
self.verify_create(self.proxy.create_snapshot, snapshot.Snapshot)
@ -325,6 +352,7 @@ class TestSnapshot(TestVolumeProxy):
class TestType(TestVolumeProxy):
def test_type_get(self):
self.verify_get(self.proxy.get_type, type.Type)
@ -363,6 +391,7 @@ class TestType(TestVolumeProxy):
class TestQuota(TestVolumeProxy):
def test_get(self):
self._verify(
'openstack.resource.Resource.fetch',

View File

@ -41,9 +41,15 @@ class TestVolume(TestVolumeProxy):
self.verify_get(self.proxy.get_volume, volume.Volume)
def test_volume_find(self):
self.verify_find(self.proxy.find_volume, volume.Volume,
expected_kwargs=dict(
list_base_path='/volumes/detail'))
self.verify_find(
self.proxy.find_volume,
volume.Volume,
method_kwargs={'all_projects': True},
expected_kwargs={
"list_base_path": "/volumes/detail",
"all_projects": True,
},
)
def test_volumes_detailed(self):
self.verify_list(
@ -612,7 +618,12 @@ class TestSnapshot(TestVolumeProxy):
self.verify_get(self.proxy.get_snapshot, snapshot.Snapshot)
def test_snapshot_find(self):
self.verify_find(self.proxy.find_snapshot, snapshot.Snapshot)
self.verify_find(
self.proxy.find_snapshot,
snapshot.Snapshot,
method_kwargs={'all_projects': True},
expected_kwargs={'all_projects': True},
)
def test_snapshots_detailed(self):
self.verify_list(

View File

@ -80,7 +80,8 @@ class TestFlavor(TestComputeProxy):
self._verify(
'openstack.proxy.Proxy._find',
self.proxy.find_flavor,
method_args=['res', True, True],
method_args=['res', True],
method_kwargs={'get_extra_specs': True},
expected_result=res,
expected_args=[flavor.Flavor, 'res'],
expected_kwargs={'ignore_missing': True}
@ -851,7 +852,11 @@ class TestCompute(TestComputeProxy):
self.verify_find(
self.proxy.find_server,
server.Server,
expected_kwargs={'list_base_path': '/servers/detail'},
method_kwargs={'all_projects': True},
expected_kwargs={
'list_base_path': '/servers/detail',
'all_projects': True,
},
)
def test_server_get(self):
@ -1209,8 +1214,12 @@ class TestCompute(TestComputeProxy):
server_group.ServerGroup, True)
def test_server_group_find(self):
self.verify_find(self.proxy.find_server_group,
server_group.ServerGroup)
self.verify_find(
self.proxy.find_server_group,
server_group.ServerGroup,
method_kwargs={'all_projects': True},
expected_kwargs={'all_projects': True},
)
def test_server_group_get(self):
self.verify_get(self.proxy.get_server_group,

View File

@ -85,5 +85,8 @@ class TestCronTriggerProxy(test_proxy_base.TestProxyBase):
cron_trigger.CronTrigger, True)
def test_cron_trigger_find(self):
self.verify_find(self.proxy.find_cron_trigger,
cron_trigger.CronTrigger)
self.verify_find(
self.proxy.find_cron_trigger,
cron_trigger.CronTrigger,
expected_kwargs={'all_projects': False},
)

View File

@ -196,9 +196,11 @@ class Proxy(proxy.Proxy):
"""
return self._get(_cron_trigger.CronTrigger, cron_trigger)
def cron_triggers(self, **query):
def cron_triggers(self, *, all_projects=False, **query):
"""Retrieve a generator of cron triggers
:param bool all_projects: When set to ``True``, list cron triggers from
all projects. Admin-only by default.
:param kwargs query: Optional query parameters to be sent to
restrict the cron triggers to be returned. Available parameters
include:
@ -212,6 +214,8 @@ class Proxy(proxy.Proxy):
:returns: A generator of CronTrigger instances.
"""
if all_projects:
query['all_projects'] = True
return self._list(_cron_trigger.CronTrigger, **query)
def delete_cron_trigger(self, value, ignore_missing=True):
@ -231,17 +235,39 @@ class Proxy(proxy.Proxy):
return self._delete(_cron_trigger.CronTrigger, value,
ignore_missing=ignore_missing)
def find_cron_trigger(self, name_or_id, ignore_missing=True):
# TODO(stephenfin): Drop 'query' parameter or apply it consistently
def find_cron_trigger(
self,
name_or_id,
ignore_missing=True,
*,
all_projects=False,
**query,
):
"""Find a single cron trigger
:param name_or_id: The name or ID of a cron trigger.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be
raised when the resource does not exist.
When set to ``True``, None will be returned when
attempting to find a nonexistent resource.
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource does not exist. When set to ``True``, None will be
returned when attempting to find a nonexistent resource.
:param bool all_projects: When set to ``True``, search for cron
triggers by name across all projects. Note that this will likely
result in a higher chance of duplicates.
:param kwargs query: Optional query parameters to be sent to limit
the cron triggers being returned.
:returns: One :class:`~openstack.compute.v2.cron_trigger.CronTrigger`
or None
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
"""
return self._find(_cron_trigger.CronTrigger, name_or_id,
ignore_missing=ignore_missing)
return self._find(
_cron_trigger.CronTrigger,
name_or_id,
ignore_missing=ignore_missing,
all_projects=all_projects,
**query,
)

View File

@ -0,0 +1,33 @@
---
features:
- |
A number of APIs support passing an admin-only ``all_projects`` filter when
listing certain resources, allowing you to retrieve resources from all
projects rather than just the current projects. This filter is now
explicitly supported at the proxy layer for services and resources that
support it. These are:
* Block storage (v2)
* ``find_snapshot``
* ``snapshots``
* ``find_volume``
* ``volumes``
* Block storage (v3)
* ``find_snapshot``
* ``snapshots``
* ``find_volume``
* ``volumes``
* Compute (v2)
* ``find_server``
* ``find_server_group``
* ``server_groups``
* Workflow (v2)
* ``find_cron_triggers``
* ``cron_triggers``