Merge "Use direct calls to get_<resource>_by_id"
This commit is contained in:
commit
92b294afbf
@ -23,6 +23,7 @@ import six
|
||||
import sre_constants
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from decorator import decorator
|
||||
|
||||
@ -196,12 +197,15 @@ def _filter_list(data, name_or_id, filters):
|
||||
return filtered
|
||||
|
||||
|
||||
def _get_entity(func, name_or_id, filters, **kwargs):
|
||||
def _get_entity(cloud, resource, name_or_id, filters, **kwargs):
|
||||
"""Return a single entity from the list returned by a given method.
|
||||
|
||||
:param callable func:
|
||||
A function that takes `name_or_id` and `filters` as parameters
|
||||
and returns a list of entities to filter.
|
||||
:param object cloud:
|
||||
The controller class (Example: the main OpenStackCloud object) .
|
||||
:param string or callable resource:
|
||||
The string that identifies the resource to use to lookup the
|
||||
get_<>_by_id or search_<resource>s methods(Example: network)
|
||||
or a callable to invoke.
|
||||
:param string name_or_id:
|
||||
The name or ID of the entity being filtered or a dict
|
||||
:param filters:
|
||||
@ -210,20 +214,33 @@ def _get_entity(func, name_or_id, filters, **kwargs):
|
||||
A string containing a jmespath expression for further filtering.
|
||||
Example:: "[?last_name==`Smith`] | [?other.gender]==`Female`]"
|
||||
"""
|
||||
|
||||
# Sometimes in the control flow of shade, we already have an object
|
||||
# fetched. Rather than then needing to pull the name or id out of that
|
||||
# object, pass it in here and rely on caching to prevent us from making
|
||||
# an additional call, it's simple enough to test to see if we got an
|
||||
# object and just short-circuit return it.
|
||||
|
||||
if hasattr(name_or_id, 'id'):
|
||||
return name_or_id
|
||||
entities = func(name_or_id, filters, **kwargs)
|
||||
if not entities:
|
||||
return None
|
||||
if len(entities) > 1:
|
||||
raise exc.OpenStackCloudException(
|
||||
"Multiple matches found for %s" % name_or_id)
|
||||
return entities[0]
|
||||
|
||||
# If a uuid is passed short-circuit it calling the
|
||||
# get_<resorce_name>_by_id method
|
||||
if getattr(cloud, 'use_direct_get', False) and _is_uuid_like(name_or_id):
|
||||
get_resource = getattr(cloud, 'get_%s_by_id' % resource, None)
|
||||
if get_resource:
|
||||
return get_resource(name_or_id)
|
||||
|
||||
search = resource if callable(resource) else getattr(
|
||||
cloud, 'search_%ss' % resource, None)
|
||||
if search:
|
||||
entities = search(name_or_id, filters, **kwargs)
|
||||
if entities:
|
||||
if len(entities) > 1:
|
||||
raise exc.OpenStackCloudException(
|
||||
"Multiple matches found for %s" % name_or_id)
|
||||
return entities[0]
|
||||
return None
|
||||
|
||||
|
||||
def normalize_keystone_services(services):
|
||||
@ -670,3 +687,27 @@ class FileSegment(object):
|
||||
|
||||
def reset(self):
|
||||
self._file.seek(self.offset, 0)
|
||||
|
||||
|
||||
def _format_uuid_string(string):
|
||||
return (string.replace('urn:', '')
|
||||
.replace('uuid:', '')
|
||||
.strip('{}')
|
||||
.replace('-', '')
|
||||
.lower())
|
||||
|
||||
|
||||
def _is_uuid_like(val):
|
||||
"""Returns validation of a value as a UUID.
|
||||
|
||||
:param val: Value to verify
|
||||
:type val: string
|
||||
:returns: bool
|
||||
|
||||
.. versionchanged:: 1.1.1
|
||||
Support non-lowercase UUIDs.
|
||||
"""
|
||||
try:
|
||||
return str(uuid.UUID(val)).replace('-', '') == _format_uuid_string(val)
|
||||
except (TypeError, ValueError, AttributeError):
|
||||
return False
|
||||
|
@ -27,7 +27,8 @@ class OpenStackInventory(object):
|
||||
|
||||
def __init__(
|
||||
self, config_files=None, refresh=False, private=False,
|
||||
config_key=None, config_defaults=None, cloud=None):
|
||||
config_key=None, config_defaults=None, cloud=None,
|
||||
use_direct_get=False):
|
||||
if config_files is None:
|
||||
config_files = []
|
||||
config = os_client_config.config.OpenStackConfig(
|
||||
@ -82,4 +83,4 @@ class OpenStackInventory(object):
|
||||
func = self.search_hosts
|
||||
else:
|
||||
func = functools.partial(self.search_hosts, expand=False)
|
||||
return _utils._get_entity(func, name_or_id, filters)
|
||||
return _utils._get_entity(self, func, name_or_id, filters)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# Licensed under the Apache License, Version 3.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
@ -138,6 +138,7 @@ class OpenStackCloud(
|
||||
strict=False,
|
||||
app_name=None,
|
||||
app_version=None,
|
||||
use_direct_get=False,
|
||||
**kwargs):
|
||||
|
||||
if log_inner_exceptions:
|
||||
@ -211,6 +212,7 @@ class OpenStackCloud(
|
||||
warnings.filterwarnings('ignore', category=category)
|
||||
|
||||
self._disable_warnings = {}
|
||||
self.use_direct_get = use_direct_get
|
||||
|
||||
self._servers = None
|
||||
self._servers_time = 0
|
||||
@ -838,7 +840,7 @@ class OpenStackCloud(
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the OpenStack API call.
|
||||
"""
|
||||
return _utils._get_entity(self.search_projects, name_or_id, filters,
|
||||
return _utils._get_entity(self, 'project', name_or_id, filters,
|
||||
domain_id=domain_id)
|
||||
|
||||
@_utils.valid_kwargs('description')
|
||||
@ -967,8 +969,7 @@ class OpenStackCloud(
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the OpenStack API call.
|
||||
"""
|
||||
return _utils._get_entity(self.search_users, name_or_id, filters,
|
||||
**kwargs)
|
||||
return _utils._get_entity(self, 'user', name_or_id, filters, **kwargs)
|
||||
|
||||
def get_user_by_id(self, user_id, normalize=True):
|
||||
"""Get a user by ID.
|
||||
@ -2597,7 +2598,7 @@ class OpenStackCloud(
|
||||
:returns: A keypair ``munch.Munch`` or None if no matching keypair is
|
||||
found.
|
||||
"""
|
||||
return _utils._get_entity(self.search_keypairs, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'keypair', name_or_id, filters)
|
||||
|
||||
def get_network(self, name_or_id, filters=None):
|
||||
"""Get a network by name or ID.
|
||||
@ -2622,7 +2623,7 @@ class OpenStackCloud(
|
||||
found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_networks, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'network', name_or_id, filters)
|
||||
|
||||
def get_network_by_id(self, id):
|
||||
""" Get a network by ID
|
||||
@ -2661,7 +2662,7 @@ class OpenStackCloud(
|
||||
found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_routers, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'router', name_or_id, filters)
|
||||
|
||||
def get_subnet(self, name_or_id, filters=None):
|
||||
"""Get a subnet by name or ID.
|
||||
@ -2682,7 +2683,7 @@ class OpenStackCloud(
|
||||
found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_subnets, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'subnet', name_or_id, filters)
|
||||
|
||||
def get_subnet_by_id(self, id):
|
||||
""" Get a subnet by ID
|
||||
@ -2720,7 +2721,7 @@ class OpenStackCloud(
|
||||
:returns: A port ``munch.Munch`` or None if no matching port is found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_ports, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'port', name_or_id, filters)
|
||||
|
||||
def get_port_by_id(self, id):
|
||||
""" Get a port by ID
|
||||
@ -2760,7 +2761,7 @@ class OpenStackCloud(
|
||||
|
||||
"""
|
||||
return _utils._get_entity(
|
||||
self.search_qos_policies, name_or_id, filters)
|
||||
self, 'qos_policie', name_or_id, filters)
|
||||
|
||||
def get_volume(self, name_or_id, filters=None):
|
||||
"""Get a volume by name or ID.
|
||||
@ -2785,7 +2786,7 @@ class OpenStackCloud(
|
||||
found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_volumes, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'volume', name_or_id, filters)
|
||||
|
||||
def get_volume_by_id(self, id):
|
||||
""" Get a volume by ID
|
||||
@ -2826,7 +2827,7 @@ class OpenStackCloud(
|
||||
|
||||
"""
|
||||
return _utils._get_entity(
|
||||
self.search_volume_types, name_or_id, filters)
|
||||
self, 'volume_type', name_or_id, filters)
|
||||
|
||||
def get_flavor(self, name_or_id, filters=None, get_extra=True):
|
||||
"""Get a flavor by name or ID.
|
||||
@ -2856,7 +2857,7 @@ class OpenStackCloud(
|
||||
"""
|
||||
search_func = functools.partial(
|
||||
self.search_flavors, get_extra=get_extra)
|
||||
return _utils._get_entity(search_func, name_or_id, filters)
|
||||
return _utils._get_entity(self, search_func, name_or_id, filters)
|
||||
|
||||
def get_flavor_by_id(self, id, get_extra=True):
|
||||
""" Get a flavor by ID
|
||||
@ -2918,7 +2919,7 @@ class OpenStackCloud(
|
||||
|
||||
"""
|
||||
return _utils._get_entity(
|
||||
self.search_security_groups, name_or_id, filters)
|
||||
self, 'security_group', name_or_id, filters)
|
||||
|
||||
def get_security_group_by_id(self, id):
|
||||
""" Get a security group by ID
|
||||
@ -3007,7 +3008,7 @@ class OpenStackCloud(
|
||||
"""
|
||||
searchfunc = functools.partial(self.search_servers,
|
||||
detailed=detailed, bare=True)
|
||||
server = _utils._get_entity(searchfunc, name_or_id, filters)
|
||||
server = _utils._get_entity(self, searchfunc, name_or_id, filters)
|
||||
return self._expand_server(server, detailed, bare)
|
||||
|
||||
def _expand_server(self, server, detailed, bare):
|
||||
@ -3043,7 +3044,7 @@ class OpenStackCloud(
|
||||
is found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_server_groups, name_or_id,
|
||||
return _utils._get_entity(self, 'server_group', name_or_id,
|
||||
filters)
|
||||
|
||||
def get_image(self, name_or_id, filters=None):
|
||||
@ -3069,7 +3070,7 @@ class OpenStackCloud(
|
||||
is found
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_images, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'image', name_or_id, filters)
|
||||
|
||||
def get_image_by_id(self, id):
|
||||
""" Get a image by ID
|
||||
@ -3160,7 +3161,7 @@ class OpenStackCloud(
|
||||
IP is found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_floating_ips, id, filters)
|
||||
return _utils._get_entity(self, 'floating_ip', id, filters)
|
||||
|
||||
def get_floating_ip_by_id(self, id):
|
||||
""" Get a floating ip by ID
|
||||
@ -3213,7 +3214,7 @@ class OpenStackCloud(
|
||||
return _utils._filter_list([stack], name_or_id, filters)
|
||||
|
||||
return _utils._get_entity(
|
||||
_search_one_stack, name_or_id, filters)
|
||||
self, _search_one_stack, name_or_id, filters)
|
||||
|
||||
def create_keypair(self, name, public_key=None):
|
||||
"""Create a new keypair.
|
||||
@ -5175,7 +5176,7 @@ class OpenStackCloud(
|
||||
:returns: A volume ``munch.Munch`` or None if no matching volume is
|
||||
found.
|
||||
"""
|
||||
return _utils._get_entity(self.search_volume_snapshots, name_or_id,
|
||||
return _utils._get_entity(self, 'volume_snapshot', name_or_id,
|
||||
filters)
|
||||
|
||||
def create_volume_backup(self, volume_id, name=None, description=None,
|
||||
@ -5234,7 +5235,7 @@ class OpenStackCloud(
|
||||
:returns: A backup ``munch.Munch`` or None if no matching backup is
|
||||
found.
|
||||
"""
|
||||
return _utils._get_entity(self.search_volume_backups, name_or_id,
|
||||
return _utils._get_entity(self, 'volume_backup', name_or_id,
|
||||
filters)
|
||||
|
||||
def list_volume_snapshots(self, detailed=True, search_opts=None):
|
||||
@ -6480,7 +6481,6 @@ class OpenStackCloud(
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
# TODO(mordred) Add support for description starting in 2.19
|
||||
|
||||
security_groups = kwargs.get('security_groups', [])
|
||||
if security_groups and not isinstance(kwargs['security_groups'], list):
|
||||
security_groups = [security_groups]
|
||||
@ -8161,7 +8161,7 @@ class OpenStackCloud(
|
||||
:returns: A zone dict or None if no matching zone is found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_zones, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'zone', name_or_id, filters)
|
||||
|
||||
def search_zones(self, name_or_id=None, filters=None):
|
||||
zones = self.list_zones()
|
||||
@ -8453,7 +8453,7 @@ class OpenStackCloud(
|
||||
:returns: A cluster template dict or None if no matching
|
||||
cluster template is found.
|
||||
"""
|
||||
return _utils._get_entity(self.search_cluster_templates, name_or_id,
|
||||
return _utils._get_entity(self, 'cluster_template', name_or_id,
|
||||
filters=filters, detail=detail)
|
||||
get_baymodel = get_cluster_template
|
||||
|
||||
|
@ -849,7 +849,7 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during the
|
||||
openstack API call or if multiple matches are found.
|
||||
"""
|
||||
return _utils._get_entity(self.search_services, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'service', name_or_id, filters)
|
||||
|
||||
def delete_service(self, name_or_id):
|
||||
"""Delete a Keystone service.
|
||||
@ -1057,7 +1057,7 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
- internal_url: <endpoint internal url> (optional)
|
||||
- admin_url: <endpoint admin url> (optional)
|
||||
"""
|
||||
return _utils._get_entity(self.search_endpoints, id, filters)
|
||||
return _utils._get_entity(self, 'endpoint', id, filters)
|
||||
|
||||
def delete_endpoint(self, id):
|
||||
"""Delete a Keystone endpoint.
|
||||
@ -1226,7 +1226,7 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
# duplicate that logic here
|
||||
if hasattr(name_or_id, 'id'):
|
||||
return name_or_id
|
||||
return _utils._get_entity(self.search_domains, filters, name_or_id)
|
||||
return _utils._get_entity(self, 'domain', filters, name_or_id)
|
||||
else:
|
||||
error_msg = 'Failed to get domain {id}'.format(id=domain_id)
|
||||
data = self._identity_client.get(
|
||||
@ -1281,8 +1281,7 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the openstack API call.
|
||||
"""
|
||||
return _utils._get_entity(self.search_groups, name_or_id, filters,
|
||||
**kwargs)
|
||||
return _utils._get_entity(self, 'group', name_or_id, filters, **kwargs)
|
||||
|
||||
def create_group(self, name, description, domain=None):
|
||||
"""Create a group.
|
||||
@ -1424,7 +1423,7 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the openstack API call.
|
||||
"""
|
||||
return _utils._get_entity(self.search_roles, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'role', name_or_id, filters)
|
||||
|
||||
def _keystone_v2_role_assignments(self, user, project=None,
|
||||
role=None, **kwargs):
|
||||
@ -1900,7 +1899,7 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
found.
|
||||
|
||||
"""
|
||||
return _utils._get_entity(self.search_aggregates, name_or_id, filters)
|
||||
return _utils._get_entity(self, 'aggregate', name_or_id, filters)
|
||||
|
||||
def create_aggregate(self, name, availability_zone=None):
|
||||
"""Create a new host aggregate.
|
||||
|
@ -15,7 +15,9 @@
|
||||
import random
|
||||
import string
|
||||
import tempfile
|
||||
from uuid import uuid4
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from shade import _utils
|
||||
@ -318,3 +320,61 @@ class TestUtils(base.TestCase):
|
||||
name)
|
||||
segment_content += segment.read()
|
||||
self.assertEqual(content, segment_content)
|
||||
|
||||
def test_get_entity_pass_object(self):
|
||||
obj = mock.Mock(id=uuid4().hex)
|
||||
self.cloud.use_direct_get = True
|
||||
self.assertEqual(obj, _utils._get_entity(self.cloud, '', obj, {}))
|
||||
|
||||
def test_get_entity_no_use_direct_get(self):
|
||||
# test we are defaulting to the search_<resource> methods
|
||||
# if the use_direct_get flag is set to False(default).
|
||||
uuid = uuid4().hex
|
||||
resource = 'network'
|
||||
func = 'search_%ss' % resource
|
||||
filters = {}
|
||||
with mock.patch.object(self.cloud, func) as search:
|
||||
_utils._get_entity(self.cloud, resource, uuid, filters)
|
||||
search.assert_called_once_with(uuid, filters)
|
||||
|
||||
def test_get_entity_no_uuid_like(self):
|
||||
# test we are defaulting to the search_<resource> methods
|
||||
# if the name_or_id param is a name(string) but not a uuid.
|
||||
self.cloud.use_direct_get = True
|
||||
name = 'name_no_uuid'
|
||||
resource = 'network'
|
||||
func = 'search_%ss' % resource
|
||||
filters = {}
|
||||
with mock.patch.object(self.cloud, func) as search:
|
||||
_utils._get_entity(self.cloud, resource, name, filters)
|
||||
search.assert_called_once_with(name, filters)
|
||||
|
||||
def test_get_entity_pass_uuid(self):
|
||||
uuid = uuid4().hex
|
||||
self.cloud.use_direct_get = True
|
||||
resources = ['flavor', 'image', 'volume', 'network',
|
||||
'subnet', 'port', 'floating_ip', 'security_group']
|
||||
for r in resources:
|
||||
f = 'get_%s_by_id' % r
|
||||
with mock.patch.object(self.cloud, f) as get:
|
||||
_utils._get_entity(self.cloud, r, uuid, {})
|
||||
get.assert_called_once_with(uuid)
|
||||
|
||||
def test_get_entity_pass_search_methods(self):
|
||||
self.cloud.use_direct_get = True
|
||||
resources = ['flavor', 'image', 'volume', 'network',
|
||||
'subnet', 'port', 'floating_ip', 'security_group']
|
||||
filters = {}
|
||||
name = 'name_no_uuid'
|
||||
for r in resources:
|
||||
f = 'search_%ss' % r
|
||||
with mock.patch.object(self.cloud, f) as search:
|
||||
_utils._get_entity(self.cloud, r, name, {})
|
||||
search.assert_called_once_with(name, filters)
|
||||
|
||||
def test_get_entity_get_and_search(self):
|
||||
resources = ['flavor', 'image', 'volume', 'network',
|
||||
'subnet', 'port', 'floating_ip', 'security_group']
|
||||
for r in resources:
|
||||
self.assertTrue(hasattr(self.cloud, 'get_%s_by_id' % r))
|
||||
self.assertTrue(hasattr(self.cloud, 'search_%ss' % r))
|
||||
|
Loading…
Reference in New Issue
Block a user