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) 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 """Find a single snapshot
:param snapshot: The name or ID a 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 :class:`~openstack.exceptions.ResourceNotFound` will be raised
when the snapshot does not exist. When set to ``True``, None will when the snapshot does not exist. When set to ``True``, None will
be returned when attempting to find a nonexistent resource. 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 :returns: One :class:`~openstack.block_storage.v2.snapshot.Snapshot` or
None. None.
@ -53,13 +62,17 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple :raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found. resources are found.
""" """
query = {}
if all_projects:
query['all_projects'] = True
return self._find( return self._find(
_snapshot.Snapshot, _snapshot.Snapshot,
name_or_id, name_or_id,
ignore_missing=ignore_missing, 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 """Retrieve a generator of snapshots
:param bool details: When set to ``False`` :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 objects will be returned. The default, ``True``, will cause
:class:`~openstack.block_storage.v2.snapshot.SnapshotDetail` :class:`~openstack.block_storage.v2.snapshot.SnapshotDetail`
objects to be returned. 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 :param kwargs query: Optional query parameters to be sent to limit
the snapshots being returned. Available parameters include: the snapshots being returned. Available parameters include:
* name: Name of the snapshot as a string. * name: Name of the snapshot as a string.
* all_projects: Whether return the snapshots in all projects.
* volume_id: volume id of a snapshot. * volume_id: volume id of a snapshot.
* status: Value of the status of the snapshot so that you can * status: Value of the status of the snapshot so that you can
filter on "available" for example. filter on "available" for example.
:returns: A generator of snapshot objects. :returns: A generator of snapshot objects.
""" """
if all_projects:
query['all_projects'] = True
base_path = '/snapshots/detail' if details else None base_path = '/snapshots/detail' if details else None
return self._list(_snapshot.Snapshot, base_path=base_path, **query) 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) 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 """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`` :param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised :class:`~openstack.exceptions.ResourceNotFound` will be raised
when the volume does not exist. 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` :raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found. when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find(_volume.Volume, name_or_id, query = {}
ignore_missing=ignore_missing) 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 """Retrieve a generator of volumes
:param bool details: When set to ``False`` no extended attributes :param bool details: When set to ``False`` no extended attributes
will be returned. The default, ``True``, will cause objects with will be returned. The default, ``True``, will cause objects with
additional attributes to be returned. 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 :param kwargs query: Optional query parameters to be sent to limit
the volumes being returned. Available parameters include: the volumes being returned. Available parameters include:
* name: Name of the volume as a string. * 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 * status: Value of the status of the volume so that you can filter
on "available" for example. on "available" for example.
:returns: A generator of volume objects. :returns: A generator of volume objects.
""" """
if all_projects:
query['all_projects'] = True
base_path = '/volumes/detail' if details else None base_path = '/volumes/detail' if details else None
return self._list(_volume.Volume, base_path=base_path, **query) 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) 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 """Find a single snapshot
:param snapshot: The name or ID a snapshot :param snapshot: The name or ID a snapshot
:param bool ignore_missing: When set to ``False`` :param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised :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` :returns: One :class:`~openstack.block_storage.v3.snapshot.Snapshot`
:raises: :class:`~openstack.exceptions.ResourceNotFound` :raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found. when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find(_snapshot.Snapshot, name_or_id, query = {}
ignore_missing=ignore_missing) 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 """Retrieve a generator of snapshots
:param bool details: When set to ``False`` :class: :param bool details: When set to ``False`` :class:
`~openstack.block_storage.v3.snapshot.Snapshot` `~openstack.block_storage.v3.snapshot.Snapshot`
objects will be returned. The default, ``True``, will cause objects will be returned. The default, ``True``, will cause
more attributes to be returned. 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 :param kwargs query: Optional query parameters to be sent to limit
the snapshots being returned. Available parameters include: the snapshots being returned. Available parameters include:
* name: Name of the snapshot as a string. * name: Name of the snapshot as a string.
* all_projects: Whether return the snapshots in all projects.
* project_id: Filter the snapshots by project. * project_id: Filter the snapshots by project.
* volume_id: volume id of a snapshot. * volume_id: volume id of a snapshot.
* status: Value of the status of the snapshot so that you can * 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. :returns: A generator of snapshot objects.
""" """
if all_projects:
query['all_projects'] = True
base_path = '/snapshots/detail' if details else None base_path = '/snapshots/detail' if details else None
return self._list(_snapshot.Snapshot, base_path=base_path, **query) 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 snapshot: The name or ID a volume type
:param bool ignore_missing: When set to ``False`` :param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised :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` :returns: One :class:`~openstack.block_storage.v3.type.Type`
:raises: :class:`~openstack.exceptions.ResourceNotFound` :raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found. when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find(_type.Type, name_or_id, return self._find(
ignore_missing=ignore_missing) _type.Type,
name_or_id,
ignore_missing=ignore_missing,
)
def types(self, **query): def types(self, **query):
"""Retrieve a generator of volume types """Retrieve a generator of volume types
@ -469,38 +496,59 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
""" """
return self._get(_volume.Volume, volume) 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 """Find a single volume
:param snapshot: The name or ID a volume :param snapshot: The name or ID a volume
:param bool ignore_missing: When set to ``False`` :param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised :class:`~openstack.exceptions.ResourceNotFound` will be raised
when the volume does not exist. 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` :returns: One :class:`~openstack.block_storage.v3.volume.Volume`
:raises: :class:`~openstack.exceptions.ResourceNotFound` :raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found. when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find(_volume.Volume, name_or_id, query = {}
ignore_missing=ignore_missing, if all_projects:
list_base_path='/volumes/detail') 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 """Retrieve a generator of volumes
:param bool details: When set to ``False`` no extended attributes :param bool details: When set to ``False`` no extended attributes
will be returned. The default, ``True``, will cause objects with will be returned. The default, ``True``, will cause objects with
additional attributes to be returned. 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 :param kwargs query: Optional query parameters to be sent to limit
the volumes being returned. Available parameters include: the volumes being returned. Available parameters include:
* name: Name of the volume as a string. * 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 * status: Value of the status of the volume so that you can filter
on "available" for example. on "available" for example.
:returns: A generator of volume objects. :returns: A generator of volume objects.
""" """
if all_projects:
query['all_projects'] = True
base_path = '/volumes/detail' if details else None base_path = '/volumes/detail' if details else None
return self._list(_volume.Volume, base_path=base_path, **query) 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) return self._list(_stats.Pools, **query)
# ====== BACKUPS ====== # ====== BACKUPS ======
def backups(self, details=True, **query): def backups(self, *, details=True, **query):
"""Retrieve a generator of backups """Retrieve a generator of backups
:param bool details: When set to ``False`` :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` :returns: One :class:`~openstack.block_storage.v3.backup.Backup`
:raises: :class:`~openstack.exceptions.ResourceNotFound` :raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found. when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find(_backup.Backup, name_or_id, return self._find(
ignore_missing=ignore_missing) _backup.Backup,
name_or_id,
ignore_missing=ignore_missing,
)
def create_backup(self, **attrs): def create_backup(self, **attrs):
"""Create a new Backup from attributes with native API """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` :returns: One :class:`~openstack.block_storage.v3.group.Group`
:raises: :class:`~openstack.exceptions.ResourceNotFound` :raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found. when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find( 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 """Retrieve a generator of groups
:param bool details: When set to ``False``, no additional details will :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` :returns: One :class:`~openstack.block_storage.v3.group_snapshot`
:raises: :class:`~openstack.exceptions.ResourceNotFound` :raises: :class:`~openstack.exceptions.ResourceNotFound`
when no resource can be found. when no resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find( return self._find(
_group_snapshot.GroupSnapshot, name_or_id, _group_snapshot.GroupSnapshot,
ignore_missing=ignore_missing) 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 """Retrieve a generator of group snapshots
:param bool details: When ``True``, returns :param bool details: When ``True``, returns
@ -1245,9 +1307,14 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
:class:`~openstack.block_storage.v3.group_type.GroupType` :class:`~openstack.block_storage.v3.group_type.GroupType`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no :raises: :class:`~openstack.exceptions.ResourceNotFound` when no
resource can be found. resource can be found.
:raises: :class:`~openstack.exceptions.DuplicateResource` when multiple
resources are found.
""" """
return self._find( 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): def group_types(self, **query):
"""Retrive a generator of group types """Retrive a generator of group types

View File

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

View File

@ -2192,6 +2192,7 @@ class Resource(dict):
list_base_path=None, list_base_path=None,
*, *,
microversion=None, microversion=None,
all_projects=None,
**params, **params,
): ):
"""Find a resource by its name or id. """Find a resource by its name or id.
@ -2220,10 +2221,13 @@ class Resource(dict):
is found and ignore_missing is ``False``. is found and ignore_missing is ``False``.
""" """
session = cls._get_session(session) session = cls._get_session(session)
# Try to short-circuit by looking directly for a matching ID. # Try to short-circuit by looking directly for a matching ID.
try: try:
match = cls.existing( 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) return match.fetch(session, microversion=microversion, **params)
except (exceptions.NotFoundException, exceptions.BadRequestException): except (exceptions.NotFoundException, exceptions.BadRequestException):
@ -2234,6 +2238,12 @@ class Resource(dict):
if list_base_path: if list_base_path:
params['base_path'] = 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 ( if (
'name' in cls._query_mapping._mapping.keys() 'name' in cls._query_mapping._mapping.keys()
and 'name' not in params and 'name' not in params
@ -2248,6 +2258,7 @@ class Resource(dict):
if ignore_missing: if ignore_missing:
return None return None
raise exceptions.ResourceNotFound( raise exceptions.ResourceNotFound(
"No %s found for %s" % (cls.__name__, name_or_id) "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): class TestVolumeProxy(test_proxy_base.TestProxyBase):
def setUp(self): def setUp(self):
super(TestVolumeProxy, self).setUp() super().setUp()
self.proxy = _proxy.Proxy(self.session) self.proxy = _proxy.Proxy(self.session)
@ -34,18 +35,31 @@ class TestVolume(TestVolumeProxy):
self.verify_get(self.proxy.get_volume, volume.Volume) self.verify_get(self.proxy.get_volume, volume.Volume)
def test_volume_find(self): 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): def test_volumes_detailed(self):
self.verify_list(self.proxy.volumes, volume.Volume, self.verify_list(
method_kwargs={"details": True, "query": 1}, self.proxy.volumes,
expected_kwargs={"query": 1, volume.Volume,
"base_path": "/volumes/detail"}) method_kwargs={"details": True, "all_projects": True},
expected_kwargs={
"base_path": "/volumes/detail",
"all_projects": True,
}
)
def test_volumes_not_detailed(self): def test_volumes_not_detailed(self):
self.verify_list(self.proxy.volumes, volume.Volume, self.verify_list(
method_kwargs={"details": False, "query": 1}, self.proxy.volumes,
expected_kwargs={"query": 1}) volume.Volume,
method_kwargs={"details": False, "all_projects": True},
expected_kwargs={"all_projects": True},
)
def test_volume_create_attrs(self): def test_volume_create_attrs(self):
self.verify_create(self.proxy.create_volume, volume.Volume) self.verify_create(self.proxy.create_volume, volume.Volume)
@ -197,6 +211,7 @@ class TestVolumeActions(TestVolumeProxy):
class TestBackup(TestVolumeProxy): class TestBackup(TestVolumeProxy):
def test_backups_detailed(self): def test_backups_detailed(self):
self.verify_list( self.verify_list(
self.proxy.backups, self.proxy.backups,
@ -257,21 +272,33 @@ class TestBackup(TestVolumeProxy):
class TestSnapshot(TestVolumeProxy): class TestSnapshot(TestVolumeProxy):
def test_snapshot_get(self): def test_snapshot_get(self):
self.verify_get(self.proxy.get_snapshot, snapshot.Snapshot) self.verify_get(self.proxy.get_snapshot, snapshot.Snapshot)
def test_snapshot_find(self): 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): def test_snapshots_detailed(self):
self.verify_list(self.proxy.snapshots, snapshot.SnapshotDetail, self.verify_list(
method_kwargs={"details": True, "query": 1}, self.proxy.snapshots,
expected_kwargs={"query": 1}) snapshot.SnapshotDetail,
method_kwargs={"details": True, "all_projects": True},
expected_kwargs={"all_projects": True},
)
def test_snapshots_not_detailed(self): def test_snapshots_not_detailed(self):
self.verify_list(self.proxy.snapshots, snapshot.Snapshot, self.verify_list(
method_kwargs={"details": False, "query": 1}, self.proxy.snapshots,
expected_kwargs={"query": 1}) snapshot.Snapshot,
method_kwargs={"details": False, "all_projects": True},
expected_kwargs={"all_projects": 1},
)
def test_snapshot_create_attrs(self): def test_snapshot_create_attrs(self):
self.verify_create(self.proxy.create_snapshot, snapshot.Snapshot) self.verify_create(self.proxy.create_snapshot, snapshot.Snapshot)
@ -325,6 +352,7 @@ class TestSnapshot(TestVolumeProxy):
class TestType(TestVolumeProxy): class TestType(TestVolumeProxy):
def test_type_get(self): def test_type_get(self):
self.verify_get(self.proxy.get_type, type.Type) self.verify_get(self.proxy.get_type, type.Type)
@ -363,6 +391,7 @@ class TestType(TestVolumeProxy):
class TestQuota(TestVolumeProxy): class TestQuota(TestVolumeProxy):
def test_get(self): def test_get(self):
self._verify( self._verify(
'openstack.resource.Resource.fetch', 'openstack.resource.Resource.fetch',

View File

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

View File

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

View File

@ -85,5 +85,8 @@ class TestCronTriggerProxy(test_proxy_base.TestProxyBase):
cron_trigger.CronTrigger, True) cron_trigger.CronTrigger, True)
def test_cron_trigger_find(self): def test_cron_trigger_find(self):
self.verify_find(self.proxy.find_cron_trigger, self.verify_find(
cron_trigger.CronTrigger) 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) 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 """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 :param kwargs query: Optional query parameters to be sent to
restrict the cron triggers to be returned. Available parameters restrict the cron triggers to be returned. Available parameters
include: include:
@ -212,6 +214,8 @@ class Proxy(proxy.Proxy):
:returns: A generator of CronTrigger instances. :returns: A generator of CronTrigger instances.
""" """
if all_projects:
query['all_projects'] = True
return self._list(_cron_trigger.CronTrigger, **query) return self._list(_cron_trigger.CronTrigger, **query)
def delete_cron_trigger(self, value, ignore_missing=True): def delete_cron_trigger(self, value, ignore_missing=True):
@ -231,17 +235,39 @@ class Proxy(proxy.Proxy):
return self._delete(_cron_trigger.CronTrigger, value, return self._delete(_cron_trigger.CronTrigger, value,
ignore_missing=ignore_missing) 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 """Find a single cron trigger
:param name_or_id: The name or ID of a cron trigger. :param name_or_id: The name or ID of a cron trigger.
:param bool ignore_missing: When set to ``False`` :param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be :class:`~openstack.exceptions.ResourceNotFound` will be raised when
raised when the resource does not exist. the resource does not exist. When set to ``True``, None will be
When set to ``True``, None will be returned when returned when attempting to find a nonexistent resource.
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` :returns: One :class:`~openstack.compute.v2.cron_trigger.CronTrigger`
or None 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, return self._find(
ignore_missing=ignore_missing) _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``