From 0a6083b8766c86e198c0f6761d5da84d7f2bdfa5 Mon Sep 17 00:00:00 2001 From: Monty Taylor <mordred@inaugust.com> Date: Tue, 30 Jan 2018 16:22:20 -0600 Subject: [PATCH] Merge Connection and OpenStackCloud There's no good reason to have the shade functions in a cloud attribute. Go ahead and dive all the way in and make OpenStackCloud a mixin class. There was one wrapped exception that got removed and thus the test to test that we were wrapping it. Change-Id: Iebd80fe5bc511ea879ea71aa88ce7d79c5e8fa58 --- SHADE-MERGE-TODO.rst | 22 +- doc/source/user/connection.rst | 1 + openstack/cloud/__init__.py | 4 +- openstack/cloud/_normalize.py | 4 +- openstack/cloud/inventory.py | 23 +- openstack/cloud/meta.py | 2 +- openstack/cloud/openstackcloud.py | 338 +++++++----------- openstack/connection.py | 31 +- openstack/tests/functional/cloud/base.py | 10 +- .../tests/functional/cloud/test_domain.py | 2 +- .../tests/functional/cloud/test_endpoints.py | 2 +- .../tests/functional/cloud/test_groups.py | 2 +- .../tests/functional/cloud/test_project.py | 2 +- .../tests/functional/cloud/test_services.py | 2 +- .../tests/functional/cloud/test_users.py | 4 +- openstack/tests/unit/base.py | 12 +- openstack/tests/unit/cloud/test_image.py | 12 +- openstack/tests/unit/cloud/test_inventory.py | 30 +- openstack/tests/unit/cloud/test_meta.py | 14 +- openstack/tests/unit/cloud/test_operator.py | 2 +- .../tests/unit/config/test_from_session.py | 2 +- ...hade-into-connection-81191fb3d0ddaf6e.yaml | 5 + 22 files changed, 227 insertions(+), 299 deletions(-) create mode 100644 releasenotes/notes/shade-into-connection-81191fb3d0ddaf6e.yaml diff --git a/SHADE-MERGE-TODO.rst b/SHADE-MERGE-TODO.rst index bc5aa8349..71be13eaa 100644 --- a/SHADE-MERGE-TODO.rst +++ b/SHADE-MERGE-TODO.rst @@ -24,6 +24,17 @@ already. For reference, those are: * Plumbed Proxy use of Adapter through the Adapter subclass from shade that uses the TaskManager to run REST calls. * Finish migrating to Resource2 and Proxy2, rename them to Resource and Proxy. +* Merge OpenStackCloud into Connection. This should result + in being able to use the connection interact with the cloud using all three + interfaces. For instance: + + .. code-block:: python + + conn = connection.Connection() + servers = conn.list_servers() # High-level resource interface from shade + servers = conn.compute.servers() # SDK Service/Object Interface + response = conn.compute.get('/servers') # REST passthrough + Next steps ========== @@ -40,17 +51,6 @@ Next steps shade integration ----------------- -* Merge OpenStackCloud into Connection. This should result - in being able to use the connection interact with the cloud using all three - interfaces. For instance: - - .. code-block:: python - - conn = connection.Connection() - servers = conn.list_servers() # High-level resource interface from shade - servers = conn.compute.servers() # SDK Service/Object Interface - response = conn.compute.get('/servers') # REST passthrough - * Invent some terminology that is clear and makes sense to distinguish between the object interface that came originally from python-openstacksdk and the interface that came from shade. diff --git a/doc/source/user/connection.rst b/doc/source/user/connection.rst index e5afce0d8..21833c314 100644 --- a/doc/source/user/connection.rst +++ b/doc/source/user/connection.rst @@ -11,6 +11,7 @@ Connection Object .. autoclass:: openstack.connection.Connection :members: + :inherited-members: Transitioning from Profile diff --git a/openstack/cloud/__init__.py b/openstack/cloud/__init__.py index 22ea13756..c3ad1f9cf 100644 --- a/openstack/cloud/__init__.py +++ b/openstack/cloud/__init__.py @@ -56,6 +56,8 @@ def openstack_clouds( def openstack_cloud( config=None, strict=False, app_name=None, app_version=None, **kwargs): + # Late import while we unwind things + from openstack import connection if not config: config = _get_openstack_config(app_name, app_version) try: @@ -63,4 +65,4 @@ def openstack_cloud( except keystoneauth1.exceptions.auth_plugins.NoMatchingPlugin as e: raise OpenStackCloudException( "Invalid cloud configuration: {exc}".format(exc=str(e))) - return OpenStackCloud(cloud_config=cloud_region, strict=strict) + return connection.Connection(config=cloud_region, strict=strict) diff --git a/openstack/cloud/_normalize.py b/openstack/cloud/_normalize.py index d56809069..ceb08bcb8 100644 --- a/openstack/cloud/_normalize.py +++ b/openstack/cloud/_normalize.py @@ -518,8 +518,8 @@ class Normalizer(object): ret['config_drive'] = config_drive ret['project_id'] = project_id ret['tenant_id'] = project_id - ret['region'] = self.region_name - ret['cloud'] = self.name + ret['region'] = self.config.region_name + ret['cloud'] = self.config.name ret['az'] = az for key, val in ret['properties'].items(): ret.setdefault(key, val) diff --git a/openstack/cloud/inventory.py b/openstack/cloud/inventory.py index 5c488c2f9..9f0120c43 100644 --- a/openstack/cloud/inventory.py +++ b/openstack/cloud/inventory.py @@ -14,8 +14,9 @@ import functools -import openstack.config -import openstack.cloud +from openstack.config import loader +from openstack import connection +from openstack import exceptions from openstack.cloud import _utils @@ -30,24 +31,20 @@ class OpenStackInventory(object): use_direct_get=False): if config_files is None: config_files = [] - config = openstack.config.loader.OpenStackConfig( - config_files=openstack.config.loader.CONFIG_FILES + config_files) + config = loader.OpenStackConfig( + config_files=loader.CONFIG_FILES + config_files) self.extra_config = config.get_extra_config( config_key, config_defaults) if cloud is None: self.clouds = [ - openstack.cloud.OpenStackCloud(cloud_config=cloud_region) + connection.Connection(config=cloud_region) for cloud_region in config.get_all() ] else: - try: - self.clouds = [ - openstack.cloud.OpenStackCloud( - cloud_config=config.get_one(cloud)) - ] - except openstack.config.exceptions.OpenStackConfigException as e: - raise openstack.cloud.OpenStackCloudException(e) + self.clouds = [ + connection.Connection(config=config.get_one(cloud)) + ] if private: for cloud in self.clouds: @@ -66,7 +63,7 @@ class OpenStackInventory(object): # Cycle on servers for server in cloud.list_servers(detailed=expand): hostvars.append(server) - except openstack.cloud.OpenStackCloudException: + except exceptions.OpenStackCloudException: # Don't fail on one particular cloud as others may work if fail_on_cloud_config: raise diff --git a/openstack/cloud/meta.py b/openstack/cloud/meta.py index 173370150..30728a207 100644 --- a/openstack/cloud/meta.py +++ b/openstack/cloud/meta.py @@ -317,7 +317,7 @@ def _get_interface_ip(cloud, server): def get_groups_from_server(cloud, server, server_vars): groups = [] - region = cloud.region_name + region = cloud.config.region_name cloud_name = cloud.name # Create a group for the cloud diff --git a/openstack/cloud/openstackcloud.py b/openstack/cloud/openstackcloud.py index 956514a04..badc38271 100644 --- a/openstack/cloud/openstackcloud.py +++ b/openstack/cloud/openstackcloud.py @@ -39,7 +39,6 @@ from six.moves import urllib import keystoneauth1.exceptions import keystoneauth1.session -from openstack import version as openstack_version from openstack import _adapter from openstack import _log from openstack.cloud.exc import * # noqa @@ -118,65 +117,30 @@ class OpenStackCloud(_normalize.Normalizer): and that Floating IP will be actualized either via neutron or via nova depending on how this particular cloud has decided to arrange itself. - :param TaskManager manager: Optional task manager to use for running - OpenStack API tasks. Unless you're doing - rate limiting client side, you almost - certainly don't need this. (optional) :param bool strict: Only return documented attributes for each resource as per the Data Model contract. (Default False) - :param app_name: Name of the application to be appended to the user-agent - string. Optional, defaults to None. - :param app_version: Version of the application to be appended to the - user-agent string. Optional, defaults to None. - :param CloudRegion cloud_config: Cloud config object from os-client-config - In the future, this will be the only way - to pass in cloud configuration, but is - being phased in currently. """ - def __init__( - self, - cloud_config=None, - manager=None, - strict=False, - app_name=None, - app_version=None, - use_direct_get=False, - conn=None, - **kwargs): + def __init__(self): self.log = _log.setup_logging('openstack') - if not cloud_config: - config = openstack.config.OpenStackConfig( - app_name=app_name, app_version=app_version) + self.name = self.config.name + self.auth = self.config.get_auth_args() + self.default_interface = self.config.get_interface() + self.private = self.config.config.get('private', False) + self.image_api_use_tasks = self.config.config['image_api_use_tasks'] + self.secgroup_source = self.config.config['secgroup_source'] + self.force_ipv4 = self.config.force_ipv4 - cloud_config = config.get_one(**kwargs) + self._external_ipv4_names = self.config.get_external_ipv4_networks() + self._internal_ipv4_names = self.config.get_internal_ipv4_networks() + self._external_ipv6_names = self.config.get_external_ipv6_networks() + self._internal_ipv6_names = self.config.get_internal_ipv6_networks() + self._nat_destination = self.config.get_nat_destination() + self._default_network = self.config.get_default_network() - self.name = cloud_config.name - self.auth = cloud_config.get_auth_args() - self.region_name = cloud_config.region_name - self.default_interface = cloud_config.get_interface() - self.private = cloud_config.config.get('private', False) - self.image_api_use_tasks = cloud_config.config['image_api_use_tasks'] - self.secgroup_source = cloud_config.config['secgroup_source'] - self.force_ipv4 = cloud_config.force_ipv4 - self.strict_mode = strict - - if manager is not None: - self.manager = manager - else: - self.manager = task_manager.TaskManager( - name=':'.join([self.name, self.region_name])) - - self._external_ipv4_names = cloud_config.get_external_ipv4_networks() - self._internal_ipv4_names = cloud_config.get_internal_ipv4_networks() - self._external_ipv6_names = cloud_config.get_external_ipv6_networks() - self._internal_ipv6_names = cloud_config.get_internal_ipv6_networks() - self._nat_destination = cloud_config.get_nat_destination() - self._default_network = cloud_config.get_default_network() - - self._floating_ip_source = cloud_config.config.get( + self._floating_ip_source = self.config.config.get( 'floating_ip_source') if self._floating_ip_source: if self._floating_ip_source.lower() == 'none': @@ -184,16 +148,12 @@ class OpenStackCloud(_normalize.Normalizer): else: self._floating_ip_source = self._floating_ip_source.lower() - self._use_external_network = cloud_config.config.get( + self._use_external_network = self.config.config.get( 'use_external_network', True) - self._use_internal_network = cloud_config.config.get( + self._use_internal_network = self.config.config.get( 'use_internal_network', True) - # Work around older TaskManager objects that don't have submit_task - if not hasattr(self.manager, 'submit_task'): - self.manager.submit_task = self.manager.submitTask - - (self.verify, self.cert) = cloud_config.get_requests_verify_args() + (self.verify, self.cert) = self.config.get_requests_verify_args() # Turn off urllib3 warnings about insecure certs if we have # explicitly configured requests to tell it we do not want # cert verification @@ -206,7 +166,6 @@ class OpenStackCloud(_normalize.Normalizer): warnings.filterwarnings('ignore', category=category) self._disable_warnings = {} - self.use_direct_get = use_direct_get self._servers = None self._servers_time = 0 @@ -227,9 +186,9 @@ class OpenStackCloud(_normalize.Normalizer): self._networks_lock = threading.Lock() self._reset_network_caches() - cache_expiration_time = int(cloud_config.get_cache_expiration_time()) - cache_class = cloud_config.get_cache_class() - cache_arguments = cloud_config.get_cache_arguments() + cache_expiration_time = int(self.config.get_cache_expiration_time()) + cache_class = self.config.get_cache_class() + cache_arguments = self.config.get_cache_arguments() self._resource_caches = {} @@ -237,7 +196,7 @@ class OpenStackCloud(_normalize.Normalizer): self.cache_enabled = True self._cache = self._make_cache( cache_class, cache_expiration_time, cache_arguments) - expirations = cloud_config.get_cache_expiration() + expirations = self.config.get_cache_expiration() for expire_key in expirations.keys(): # Only build caches for things we have list operations for if getattr( @@ -279,36 +238,21 @@ class OpenStackCloud(_normalize.Normalizer): # If server expiration time is set explicitly, use that. Otherwise # fall back to whatever it was before - self._SERVER_AGE = cloud_config.get_cache_resource_expiration( + self._SERVER_AGE = self.config.get_cache_resource_expiration( 'server', self._SERVER_AGE) - self._PORT_AGE = cloud_config.get_cache_resource_expiration( + self._PORT_AGE = self.config.get_cache_resource_expiration( 'port', self._PORT_AGE) - self._FLOAT_AGE = cloud_config.get_cache_resource_expiration( + self._FLOAT_AGE = self.config.get_cache_resource_expiration( 'floating_ip', self._FLOAT_AGE) self._container_cache = dict() self._file_hash_cache = dict() - self._keystone_session = None - self._raw_clients = {} self._local_ipv6 = ( _utils.localhost_supports_ipv6() if not self.force_ipv4 else False) - self.cloud_config = cloud_config - self._conn_object = conn - - @property - def _conn(self): - if not self._conn_object: - # Importing late to avoid import cycle. If the OpenStackCloud - # object comes via Connection, it'll have connection passed in. - import openstack.connection - self._conn_object = openstack.connection.Connection( - config=self.cloud_config, session=self._keystone_session) - return self._conn_object - def connect_as(self, **kwargs): """Make a new OpenStackCloud object with new auth context. @@ -332,11 +276,12 @@ class OpenStackCloud(_normalize.Normalizer): that do not want to be overridden can be ommitted. """ + # TODO(mordred) Replace this with from_session config = openstack.config.OpenStackConfig( - app_name=self.cloud_config._app_name, - app_version=self.cloud_config._app_version, + app_name=self.config._app_name, + app_version=self.config._app_version, load_yaml_config=False) - params = copy.deepcopy(self.cloud_config.config) + params = copy.deepcopy(self.config.config) # Remove profile from current cloud so that overridding works params.pop('profile', None) @@ -373,7 +318,7 @@ class OpenStackCloud(_normalize.Normalizer): def session_constructor(*args, **kwargs): # We need to pass our current keystone session to the Session # Constructor, otherwise the new auth plugin doesn't get used. - return keystoneauth1.session.Session(session=self.keystone_session) + return keystoneauth1.session.Session(session=self.session) # Use cloud='defaults' so that we overlay settings properly cloud_config = config.get_one( @@ -385,7 +330,7 @@ class OpenStackCloud(_normalize.Normalizer): cloud_config.config['profile'] = self.name # Use self.__class__ so that we return whatever this if, like if it's # a subclass in the case of shade wrapping sdk. - return self.__class__(cloud_config=cloud_config) + return self.__class__(config=cloud_config) def connect_as_project(self, project): """Make a new OpenStackCloud object with a new project. @@ -457,7 +402,7 @@ class OpenStackCloud(_normalize.Normalizer): def _get_versioned_client( self, service_type, min_version=None, max_version=None): - config_version = self.cloud_config.get_api_version(service_type) + config_version = self.config.get_api_version(service_type) config_major = self._get_major_version_id(config_version) max_major = self._get_major_version_id(max_version) min_major = self._get_major_version_id(min_version) @@ -492,33 +437,33 @@ class OpenStackCloud(_normalize.Normalizer): request_max_version = '{version}.latest'.format( version=config_major) adapter = _adapter.ShadeAdapter( - session=self.keystone_session, - task_manager=self.manager, - service_type=self.cloud_config.get_service_type(service_type), - service_name=self.cloud_config.get_service_name(service_type), - interface=self.cloud_config.get_interface(service_type), - endpoint_override=self.cloud_config.get_endpoint(service_type), - region_name=self.cloud_config.region_name, + session=self.session, + task_manager=self.task_manager, + service_type=self.config.get_service_type(service_type), + service_name=self.config.get_service_name(service_type), + interface=self.config.get_interface(service_type), + endpoint_override=self.config.get_endpoint(service_type), + region_name=self.config.region_name, min_version=request_min_version, max_version=request_max_version) if adapter.get_endpoint(): return adapter adapter = _adapter.ShadeAdapter( - session=self.keystone_session, - task_manager=self.manager, - service_type=self.cloud_config.get_service_type(service_type), - service_name=self.cloud_config.get_service_name(service_type), - interface=self.cloud_config.get_interface(service_type), - endpoint_override=self.cloud_config.get_endpoint(service_type), - region_name=self.cloud_config.region_name, + session=self.session, + task_manager=self.task_manager, + service_type=self.config.get_service_type(service_type), + service_name=self.config.get_service_name(service_type), + interface=self.config.get_interface(service_type), + endpoint_override=self.config.get_endpoint(service_type), + region_name=self.config.region_name, min_version=min_version, max_version=max_version) # data.api_version can be None if no version was detected, such # as with neutron api_version = adapter.get_api_major_version( - endpoint_override=self.cloud_config.get_endpoint(service_type)) + endpoint_override=self.config.get_endpoint(service_type)) api_major = self._get_major_version_id(api_version) # If we detect a different version that was configured, warn the user. @@ -544,14 +489,14 @@ class OpenStackCloud(_normalize.Normalizer): def _get_raw_client( self, service_type, api_version=None, endpoint_override=None): return _adapter.ShadeAdapter( - session=self.keystone_session, - task_manager=self.manager, - service_type=self.cloud_config.get_service_type(service_type), - service_name=self.cloud_config.get_service_name(service_type), - interface=self.cloud_config.get_interface(service_type), - endpoint_override=self.cloud_config.get_endpoint( + session=self.session, + task_manager=self.task_manager, + service_type=self.config.get_service_type(service_type), + service_name=self.config.get_service_name(service_type), + interface=self.config.get_interface(service_type), + endpoint_override=self.config.get_endpoint( service_type) or endpoint_override, - region_name=self.cloud_config.region_name) + region_name=self.config.region_name) def _is_client_version(self, client, version): client_name = '_{client}_client'.format(client=client) @@ -673,23 +618,9 @@ class OpenStackCloud(_normalize.Normalizer): new_resource = _utils._dictify_resource(resource) return pprint.pformat(new_resource) - @property - def keystone_session(self): - if self._keystone_session is None: - try: - self._keystone_session = self.cloud_config.get_session() - if hasattr(self._keystone_session, 'additional_user_agent'): - self._keystone_session.additional_user_agent.append( - ('openstacksdk', openstack_version.__version__)) - except Exception as e: - raise OpenStackCloudException( - "Error authenticating to keystone: %s " % str(e)) - return self._keystone_session - @property def _keystone_catalog(self): - return self.keystone_session.auth.get_access( - self.keystone_session).service_catalog + return self.session.auth.get_access(self.session).service_catalog @property def service_catalog(self): @@ -703,13 +634,12 @@ class OpenStackCloud(_normalize.Normalizer): def auth_token(self): # Keystone's session will reuse a token if it is still valid. # We don't need to track validity here, just get_token() each time. - return self.keystone_session.get_token() + return self.session.get_token() @property def current_user_id(self): """Get the id of the currently logged-in user from the token.""" - return self.keystone_session.auth.get_access( - self.keystone_session).user_id + return self.session.auth.get_access(self.session).user_id @property def current_project_id(self): @@ -723,7 +653,7 @@ class OpenStackCloud(_normalize.Normalizer): :raises keystoneauth1.exceptions.auth_plugins.MissingAuthPlugin: if a plugin is not available. """ - return self.keystone_session.get_project_id() + return self.session.get_project_id() @property def current_project(self): @@ -748,7 +678,7 @@ class OpenStackCloud(_normalize.Normalizer): # If they don't match, that means we're an admin who has pulled # an object from a different project, so adding info from the # current token would be wrong. - auth_args = self.cloud_config.config.get('auth', {}) + auth_args = self.config.config.get('auth', {}) project_info['id'] = self.current_project_id project_info['name'] = auth_args.get('project_name') project_info['domain_id'] = auth_args.get('project_domain_id') @@ -763,7 +693,7 @@ class OpenStackCloud(_normalize.Normalizer): def _get_current_location(self, project_id=None, zone=None): return munch.Munch( cloud=self.name, - region_name=self.region_name, + region_name=self.config.region_name, zone=zone, project=self._get_project_info(project_id), ) @@ -1419,7 +1349,7 @@ class OpenStackCloud(_normalize.Normalizer): return self.name def get_region(self): - return self.region_name + return self.config.region_name def get_flavor_name(self, flavor_id): flavor = self.get_flavor(flavor_id, get_extra=False) @@ -1449,7 +1379,7 @@ class OpenStackCloud(_normalize.Normalizer): def get_session_endpoint(self, service_key): try: - return self.cloud_config.get_session_endpoint(service_key) + return self.config.get_session_endpoint(service_key) except keystoneauth1.exceptions.catalog.EndpointNotFound as e: self.log.debug( "Endpoint not found in %s cloud: %s", self.name, str(e)) @@ -1462,12 +1392,12 @@ class OpenStackCloud(_normalize.Normalizer): " {error}".format( service=service_key, cloud=self.name, - region=self.region_name, + region=self.config.region_name, error=str(e))) return endpoint def has_service(self, service_key): - if not self.cloud_config.config.get('has_%s' % service_key, True): + if not self.config.config.get('has_%s' % service_key, True): # TODO(mordred) add a stamp here so that we only report this once if not (service_key in self._disable_warnings and self._disable_warnings[service_key]): @@ -1489,7 +1419,7 @@ class OpenStackCloud(_normalize.Normalizer): def _nova_extensions(self): extensions = set() data = _adapter._json_response( - self._conn.compute.get('/extensions'), + self.compute.get('/extensions'), error_message="Error fetching extension list for nova") for extension in self._get_and_munchify('extensions', data): @@ -1694,7 +1624,7 @@ class OpenStackCloud(_normalize.Normalizer): """ data = _adapter._json_response( - self._conn.compute.get('/os-keypairs'), + self.compute.get('/os-keypairs'), error_message="Error fetching keypair list") return self._normalize_keypairs([ k['keypair'] for k in self._get_and_munchify('keypairs', data)]) @@ -1921,7 +1851,7 @@ class OpenStackCloud(_normalize.Normalizer): """ try: data = _adapter._json_response( - self._conn.compute.get('/os-availability-zone')) + self.compute.get('/os-availability-zone')) except OpenStackCloudHTTPError: self.log.debug( "Availability zone list could not be fetched", @@ -1946,7 +1876,7 @@ class OpenStackCloud(_normalize.Normalizer): """ data = _adapter._json_response( - self._conn.compute.get( + self.compute.get( '/flavors/detail', params=dict(is_public='None')), error_message="Error fetching flavor list") flavors = self._normalize_flavors( @@ -1958,7 +1888,7 @@ class OpenStackCloud(_normalize.Normalizer): id=flavor.id) try: data = _adapter._json_response( - self._conn.compute.get(endpoint), + self.compute.get(endpoint), error_message="Error fetching flavor extra specs") flavor.extra_specs = self._get_and_munchify( 'extra_specs', data) @@ -1995,7 +1925,7 @@ class OpenStackCloud(_normalize.Normalizer): return [] data = _adapter._json_response( - self._conn.compute.get( + self.compute.get( '/servers/{server_id}/os-security-groups'.format( server_id=server['id']))) return self._normalize_secgroups( @@ -2051,7 +1981,7 @@ class OpenStackCloud(_normalize.Normalizer): return False for sg in security_groups: - _adapter._json_response(self._conn.compute.post( + _adapter._json_response(self.compute.post( '/servers/%s/action' % server['id'], json={'addSecurityGroup': {'name': sg.name}})) @@ -2079,7 +2009,7 @@ class OpenStackCloud(_normalize.Normalizer): for sg in security_groups: try: - _adapter._json_response(self._conn.compute.post( + _adapter._json_response(self.compute.post( '/servers/%s/action' % server['id'], json={'removeSecurityGroup': {'name': sg.name}})) @@ -2121,7 +2051,7 @@ class OpenStackCloud(_normalize.Normalizer): # Handle nova security groups else: - data = _adapter._json_response(self._conn.compute.get( + data = _adapter._json_response(self.compute.get( '/os-security-groups', params=filters)) return self._normalize_secgroups( self._get_and_munchify('security_groups', data)) @@ -2169,13 +2099,13 @@ class OpenStackCloud(_normalize.Normalizer): filters=None): error_msg = "Error fetching server list on {cloud}:{region}:".format( cloud=self.name, - region=self.region_name) + region=self.config.region_name) params = filters or {} if all_projects: params['all_tenants'] = True data = _adapter._json_response( - self._conn.compute.get( + self.compute.get( '/servers/detail', params=params), error_message=error_msg) servers = self._normalize_servers( @@ -2192,7 +2122,7 @@ class OpenStackCloud(_normalize.Normalizer): """ data = _adapter._json_response( - self._conn.compute.get('/os-server-groups'), + self.compute.get('/os-server-groups'), error_message="Error fetching server group list") return self._get_and_munchify('server_groups', data) @@ -2219,7 +2149,7 @@ class OpenStackCloud(_normalize.Normalizer): msg=error_msg, project=name_or_id) data = _adapter._json_response( - self._conn.compute.get('/limits', params=params)) + self.compute.get('/limits', params=params)) limits = self._get_and_munchify('limits', data) return self._normalize_compute_limits(limits, project_id=project_id) @@ -2254,7 +2184,7 @@ class OpenStackCloud(_normalize.Normalizer): # We didn't have glance, let's try nova # If this doesn't work - we just let the exception propagate response = _adapter._json_response( - self._conn.compute.get('/images/detail')) + self.compute.get('/images/detail')) while 'next' in response: image_list.extend(meta.obj_list_to_munch(response['images'])) endpoint = response['next'] @@ -2295,7 +2225,7 @@ class OpenStackCloud(_normalize.Normalizer): 'Floating IP pools extension is not available on target cloud') data = _adapter._json_response( - self._conn.compute.get('os-floating-ip-pools'), + self.compute.get('os-floating-ip-pools'), error_message="Error fetching floating IP pool list") pools = self._get_and_munchify('floating_ip_pools', data) return [{'name': p['name']} for p in pools] @@ -2386,7 +2316,7 @@ class OpenStackCloud(_normalize.Normalizer): def _nova_list_floating_ips(self): try: data = _adapter._json_response( - self._conn.compute.get('/os-floating-ips')) + self.compute.get('/os-floating-ips')) except OpenStackCloudURINotFound: return [] return self._get_and_munchify('floating_ips', data) @@ -2991,7 +2921,7 @@ class OpenStackCloud(_normalize.Normalizer): :returns: A flavor ``munch.Munch``. """ data = _adapter._json_response( - self._conn.compute.get('/flavors/{id}'.format(id=id)), + self.compute.get('/flavors/{id}'.format(id=id)), error_message="Error getting flavor with ID {id}".format(id=id) ) flavor = self._normalize_flavor( @@ -3002,7 +2932,7 @@ class OpenStackCloud(_normalize.Normalizer): id=flavor.id) try: data = _adapter._json_response( - self._conn.compute.get(endpoint), + self.compute.get(endpoint), error_message="Error fetching flavor extra specs") flavor.extra_specs = self._get_and_munchify( 'extra_specs', data) @@ -3058,7 +2988,7 @@ class OpenStackCloud(_normalize.Normalizer): error_message=error_message) else: data = _adapter._json_response( - self._conn.compute.get( + self.compute.get( '/os-security-groups/{id}'.format(id=id)), error_message=error_message) return self._normalize_secgroup( @@ -3091,7 +3021,7 @@ class OpenStackCloud(_normalize.Normalizer): return "" def _get_server_console_output(self, server_id, length=None): - data = _adapter._json_response(self._conn.compute.post( + data = _adapter._json_response(self.compute.post( '/servers/{server_id}/action'.format(server_id=server_id), json={'os-getConsoleOutput': {'length': length}})) return self._get_and_munchify('output', data) @@ -3145,7 +3075,7 @@ class OpenStackCloud(_normalize.Normalizer): def get_server_by_id(self, id): data = _adapter._json_response( - self._conn.compute.get('/servers/{id}'.format(id=id))) + self.compute.get('/servers/{id}'.format(id=id))) server = self._get_and_munchify('server', data) return meta.add_server_interfaces(self, self._normalize_server(server)) @@ -3304,7 +3234,7 @@ class OpenStackCloud(_normalize.Normalizer): self._get_and_munchify('floatingip', data)) else: data = _adapter._json_response( - self._conn.compute.get('/os-floating-ips/{id}'.format(id=id)), + self.compute.get('/os-floating-ips/{id}'.format(id=id)), error_message=error_message) return self._normalize_floating_ip( self._get_and_munchify('floating_ip', data)) @@ -3355,7 +3285,7 @@ class OpenStackCloud(_normalize.Normalizer): if public_key: keypair['public_key'] = public_key data = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/os-keypairs', json={'keypair': keypair}), error_message="Unable to create keypair {name}".format(name=name)) @@ -3372,7 +3302,7 @@ class OpenStackCloud(_normalize.Normalizer): :raises: OpenStackCloudException on operation error. """ try: - _adapter._json_response(self._conn.compute.delete( + _adapter._json_response(self.compute.delete( '/os-keypairs/{name}'.format(name=name))) except OpenStackCloudURINotFound: self.log.debug("Keypair %s not found for deleting", name) @@ -4432,7 +4362,7 @@ class OpenStackCloud(_normalize.Normalizer): " could not be snapshotted.".format(server=server)) server = server_obj response = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/servers/{server_id}/action'.format(server_id=server['id']), json={ "createImage": { @@ -4523,7 +4453,7 @@ class OpenStackCloud(_normalize.Normalizer): # Try appending the disk format name_with_ext = '.'.join(( - name, self.cloud_config.config['image_format'])) + name, self.config.config['image_format'])) if os.path.exists(name_with_ext): return (os.path.basename(name), name_with_ext) @@ -4615,7 +4545,7 @@ class OpenStackCloud(_normalize.Normalizer): meta = {} if not disk_format: - disk_format = self.cloud_config.config['image_format'] + disk_format = self.config.config['image_format'] if not container_format: # https://docs.openstack.org/image-guide/image-formats.html container_format = 'bare' @@ -4661,7 +4591,7 @@ class OpenStackCloud(_normalize.Normalizer): kwargs[IMAGE_OBJECT_KEY] = '/'.join([container, name]) if disable_vendor_agent: - kwargs.update(self.cloud_config.config['disable_vendor_agent']) + kwargs.update(self.config.config['disable_vendor_agent']) # We can never have nice things. Glance v1 took "is_public" as a # boolean. Glance v2 takes "visibility". If the user gives us @@ -5169,7 +5099,7 @@ class OpenStackCloud(_normalize.Normalizer): :raises: OpenStackCloudException on operation error. """ - _adapter._json_response(self._conn.compute.delete( + _adapter._json_response(self.compute.delete( '/servers/{server_id}/os-volume_attachments/{volume_id}'.format( server_id=server['id'], volume_id=volume['id'])), error_message=( @@ -5237,7 +5167,7 @@ class OpenStackCloud(_normalize.Normalizer): if device: payload['device'] = device data = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/servers/{server_id}/os-volume_attachments'.format( server_id=server['id']), json=dict(volumeAttachment=payload)), @@ -5884,12 +5814,12 @@ class OpenStackCloud(_normalize.Normalizer): "unable to find a floating ip pool") pool = pools[0]['name'] - data = _adapter._json_response(self._conn.compute.post( + data = _adapter._json_response(self.compute.post( '/os-floating-ips', json=dict(pool=pool))) pool_ip = self._get_and_munchify('floating_ip', data) # TODO(mordred) Remove this - it's just for compat data = _adapter._json_response( - self._conn.compute.get('/os-floating-ips/{id}'.format( + self.compute.get('/os-floating-ips/{id}'.format( id=pool_ip['id']))) return self._get_and_munchify('floating_ip', data) @@ -5959,7 +5889,7 @@ class OpenStackCloud(_normalize.Normalizer): def _nova_delete_floating_ip(self, floating_ip_id): try: _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( '/os-floating-ips/{id}'.format(id=floating_ip_id)), error_message='Unable to delete floating IP {fip_id}'.format( fip_id=floating_ip_id)) @@ -6209,7 +6139,7 @@ class OpenStackCloud(_normalize.Normalizer): if fixed_address: body['fixed_address'] = fixed_address return _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/servers/{server_id}/action'.format(server_id=server_id), json=dict(addFloatingIp=body)), error_message=error_message) @@ -6261,7 +6191,7 @@ class OpenStackCloud(_normalize.Normalizer): error_message = "Error detaching IP {ip} from instance {id}".format( ip=floating_ip_id, id=server_id) return _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/servers/{server_id}/action'.format(server_id=server_id), json=dict(removeFloatingIp=dict( address=f_ip['floating_ip_address']))), @@ -6512,7 +6442,7 @@ class OpenStackCloud(_normalize.Normalizer): 'Volume {boot_volume} is not a valid volume' ' in {cloud}:{region}'.format( boot_volume=boot_volume, - cloud=self.name, region=self.region_name)) + cloud=self.name, region=self.config.region_name)) block_mapping = { 'boot_index': '0', 'delete_on_termination': terminate_volume, @@ -6533,7 +6463,7 @@ class OpenStackCloud(_normalize.Normalizer): 'Image {image} is not a valid image in' ' {cloud}:{region}'.format( image=image, - cloud=self.name, region=self.region_name)) + cloud=self.name, region=self.config.region_name)) block_mapping = { 'boot_index': '0', @@ -6563,7 +6493,7 @@ class OpenStackCloud(_normalize.Normalizer): 'Volume {volume} is not a valid volume' ' in {cloud}:{region}'.format( volume=volume, - cloud=self.name, region=self.region_name)) + cloud=self.name, region=self.config.region_name)) block_mapping = { 'boot_index': '-1', 'delete_on_termination': False, @@ -6757,7 +6687,7 @@ class OpenStackCloud(_normalize.Normalizer): 'Network {network} is not a valid network in' ' {cloud}:{region}'.format( network=network, - cloud=self.name, region=self.region_name)) + cloud=self.name, region=self.config.region_name)) nics.append({'net-id': network_obj['id']}) kwargs['nics'] = nics @@ -6827,7 +6757,7 @@ class OpenStackCloud(_normalize.Normalizer): endpoint = '/os-volumes_boot' with _utils.shade_exceptions("Error in creating instance"): data = _adapter._json_response( - self._conn.compute.post(endpoint, json={'server': kwargs})) + self.compute.post(endpoint, json={'server': kwargs})) server = self._get_and_munchify('server', data) admin_pass = server.get('adminPass') or kwargs.get('admin_pass') if not wait: @@ -6947,7 +6877,7 @@ class OpenStackCloud(_normalize.Normalizer): kwargs['adminPass'] = admin_pass data = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/servers/{server_id}/action'.format(server_id=server_id), json={'rebuild': kwargs}), error_message="Error in rebuilding instance") @@ -6997,7 +6927,7 @@ class OpenStackCloud(_normalize.Normalizer): 'Invalid Server {server}'.format(server=name_or_id)) _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/servers/{server_id}/metadata'.format(server_id=server['id']), json={'metadata': metadata}), error_message='Error updating server metadata') @@ -7021,7 +6951,7 @@ class OpenStackCloud(_normalize.Normalizer): error_message = 'Error deleting metadata {key} on {server}'.format( key=key, server=name_or_id) _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( '/servers/{server_id}/metadata/{key}'.format( server_id=server['id'], key=key)), @@ -7095,7 +7025,7 @@ class OpenStackCloud(_normalize.Normalizer): try: _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( '/servers/{id}'.format(id=server['id'])), error_message="Error in deleting server") except OpenStackCloudURINotFound: @@ -7160,7 +7090,7 @@ class OpenStackCloud(_normalize.Normalizer): "failed to find server '{server}'".format(server=name_or_id)) data = _adapter._json_response( - self._conn.compute.put( + self.compute.put( '/servers/{server_id}'.format(server_id=server['id']), json={'server': kwargs}), error_message="Error updating server {0}".format(name_or_id)) @@ -7179,7 +7109,7 @@ class OpenStackCloud(_normalize.Normalizer): :raises: OpenStackCloudException on operation error. """ data = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/os-server-groups', json={ 'server_group': { @@ -7205,7 +7135,7 @@ class OpenStackCloud(_normalize.Normalizer): return False _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( '/os-server-groups/{id}'.format(id=server_group['id'])), error_message="Error deleting server group {name}".format( name=name_or_id)) @@ -8149,7 +8079,7 @@ class OpenStackCloud(_normalize.Normalizer): json=security_group_json, error_message="Error creating security group {0}".format(name)) else: - data = _adapter._json_response(self._conn.compute.post( + data = _adapter._json_response(self.compute.post( '/os-security-groups', json=security_group_json)) return self._normalize_secgroup( self._get_and_munchify('security_group', data)) @@ -8188,7 +8118,7 @@ class OpenStackCloud(_normalize.Normalizer): return True else: - _adapter._json_response(self._conn.compute.delete( + _adapter._json_response(self.compute.delete( '/os-security-groups/{id}'.format(id=secgroup['id']))) return True @@ -8226,7 +8156,7 @@ class OpenStackCloud(_normalize.Normalizer): for key in ('name', 'description'): kwargs.setdefault(key, group[key]) data = _adapter._json_response( - self._conn.compute.put( + self.compute.put( '/os-security-groups/{id}'.format(id=group['id']), json={'security-group': kwargs})) return self._normalize_secgroup( @@ -8361,7 +8291,7 @@ class OpenStackCloud(_normalize.Normalizer): security_group_rule_dict[ 'security_group_rule']['tenant_id'] = project_id data = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/os-security-group-rules', json=security_group_rule_dict )) @@ -8396,7 +8326,7 @@ class OpenStackCloud(_normalize.Normalizer): return True else: - _adapter._json_response(self._conn.compute.delete( + _adapter._json_response(self.compute.delete( '/os-security-group-rules/{id}'.format(id=rule_id))) return True @@ -10509,7 +10439,7 @@ class OpenStackCloud(_normalize.Normalizer): } if flavorid == 'auto': payload['id'] = None - data = _adapter._json_response(self._conn.compute.post( + data = _adapter._json_response(self.compute.post( '/flavors', json=dict(flavor=payload))) @@ -10532,7 +10462,7 @@ class OpenStackCloud(_normalize.Normalizer): return False _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( '/flavors/{id}'.format(id=flavor['id'])), error_message="Unable to delete flavor {name}".format( name=name_or_id)) @@ -10549,7 +10479,7 @@ class OpenStackCloud(_normalize.Normalizer): :raises: OpenStackCloudResourceNotFound if flavor ID is not found. """ _adapter._json_response( - self._conn.compute.post( + self.compute.post( "/flavors/{id}/os-extra_specs".format(id=flavor_id), json=dict(extra_specs=extra_specs)), error_message="Unable to set flavor specs") @@ -10565,7 +10495,7 @@ class OpenStackCloud(_normalize.Normalizer): """ for key in keys: _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( "/flavors/{id}/os-extra_specs/{key}".format( id=flavor_id, key=key)), error_message="Unable to delete flavor spec {0}".format(key)) @@ -10581,7 +10511,7 @@ class OpenStackCloud(_normalize.Normalizer): access_key = '{action}TenantAccess'.format(action=action) _adapter._json_response( - self._conn.compute.post(endpoint, json={access_key: access})) + self.compute.post(endpoint, json={access_key: access})) def add_flavor_access(self, flavor_id, project_id): """Grant access to a private flavor for a project/tenant. @@ -10613,7 +10543,7 @@ class OpenStackCloud(_normalize.Normalizer): :raises: OpenStackCloudException on operation error. """ data = _adapter._json_response( - self._conn.compute.get( + self.compute.get( '/flavors/{id}/os-flavor-access'.format(id=flavor_id)), error_message=( "Error trying to list access from flavorID {flavor}".format( @@ -10892,7 +10822,7 @@ class OpenStackCloud(_normalize.Normalizer): """ data = _adapter._json_response( - self._conn.compute.get('/os-hypervisors/detail'), + self.compute.get('/os-hypervisors/detail'), error_message="Error fetching hypervisor list") return self._get_and_munchify('hypervisors', data) @@ -10917,7 +10847,7 @@ class OpenStackCloud(_normalize.Normalizer): """ data = _adapter._json_response( - self._conn.compute.get('/os-aggregates'), + self.compute.get('/os-aggregates'), error_message="Error fetching aggregate list") return self._get_and_munchify('aggregates', data) @@ -10953,7 +10883,7 @@ class OpenStackCloud(_normalize.Normalizer): :raises: OpenStackCloudException on operation error. """ data = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/os-aggregates', json={'aggregate': { 'name': name, @@ -10981,7 +10911,7 @@ class OpenStackCloud(_normalize.Normalizer): "Host aggregate %s not found." % name_or_id) data = _adapter._json_response( - self._conn.compute.put( + self.compute.put( '/os-aggregates/{id}'.format(id=aggregate['id']), json={'aggregate': kwargs}), error_message="Error updating aggregate {name}".format( @@ -11003,7 +10933,7 @@ class OpenStackCloud(_normalize.Normalizer): return False return _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( '/os-aggregates/{id}'.format(id=aggregate['id'])), error_message="Error deleting aggregate {name}".format( name=name_or_id)) @@ -11030,7 +10960,7 @@ class OpenStackCloud(_normalize.Normalizer): name=name_or_id) data = _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/os-aggregates/{id}/action'.format(id=aggregate['id']), json={'set_metadata': {'metadata': metadata}}), error_message=err_msg) @@ -11053,7 +10983,7 @@ class OpenStackCloud(_normalize.Normalizer): host=host_name, name=name_or_id) return _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/os-aggregates/{id}/action'.format(id=aggregate['id']), json={'add_host': {'host': host_name}}), error_message=err_msg) @@ -11075,7 +11005,7 @@ class OpenStackCloud(_normalize.Normalizer): host=host_name, name=name_or_id) return _adapter._json_response( - self._conn.compute.post( + self.compute.post( '/os-aggregates/{id}/action'.format(id=aggregate['id']), json={'remove_host': {'host': host_name}}), error_message=err_msg) @@ -11167,7 +11097,7 @@ class OpenStackCloud(_normalize.Normalizer): kwargs['force'] = True _adapter._json_response( - self._conn.compute.put( + self.compute.put( '/os-quota-sets/{project}'.format(project=proj.id), json={'quota_set': kwargs}), error_message="No valid quota or resource") @@ -11184,7 +11114,7 @@ class OpenStackCloud(_normalize.Normalizer): if not proj: raise OpenStackCloudException("project does not exist") data = _adapter._json_response( - self._conn.compute.get( + self.compute.get( '/os-quota-sets/{project}'.format(project=proj.id))) return self._get_and_munchify('quota_set', data) @@ -11201,7 +11131,7 @@ class OpenStackCloud(_normalize.Normalizer): if not proj: raise OpenStackCloudException("project does not exist") return _adapter._json_response( - self._conn.compute.delete( + self.compute.delete( '/os-quota-sets/{project}'.format(project=proj.id))) def get_compute_usage(self, name_or_id, start=None, end=None): @@ -11260,7 +11190,7 @@ class OpenStackCloud(_normalize.Normalizer): name=proj.id)) data = _adapter._json_response( - self._conn.compute.get( + self.compute.get( '/os-simple-tenant-usage/{project}'.format(project=proj.id), params=dict(start=start.isoformat(), end=end.isoformat())), error_message="Unable to get usage for project: {name}".format( diff --git a/openstack/connection.py b/openstack/connection.py index a7cad826d..57663b444 100644 --- a/openstack/connection.py +++ b/openstack/connection.py @@ -211,7 +211,8 @@ def from_config(cloud=None, config=None, options=None, **kwargs): return Connection(config=config) -class Connection(six.with_metaclass(_meta.ConnectionMeta)): +class Connection(six.with_metaclass(_meta.ConnectionMeta, + _cloud.OpenStackCloud)): def __init__(self, cloud=None, config=None, session=None, app_name=None, app_version=None, @@ -219,6 +220,8 @@ class Connection(six.with_metaclass(_meta.ConnectionMeta)): # python-openstackclient to not use the profile interface. authenticator=None, profile=None, extra_services=None, + strict=False, + use_direct_get=False, **kwargs): """Create a connection to a cloud. @@ -305,17 +308,23 @@ class Connection(six.with_metaclass(_meta.ConnectionMeta)): # TODO(mordred) Expose constructor option for this in OCC self.config._keystone_session = session - self.session = self.config.get_session() - # Hide a reference to the connection on the session to help with - # backwards compatibility for folks trying to just pass conn.session - # to a Resource method's session argument. - self.session._sdk_connection = self - + self._session = None self._proxies = {} - self.cloud = _cloud.OpenStackCloud( - cloud_config=self.config, - manager=self.task_manager, - conn=self) + self.use_direct_get = use_direct_get + self.strict_mode = strict + # Call the OpenStackCloud constructor while we work on integrating + # things better. + _cloud.OpenStackCloud.__init__(self) + + @property + def session(self): + if not self._session: + self._session = self.config.get_session() + # Hide a reference to the connection on the session to help with + # backwards compatibility for folks trying to just pass + # conn.session to a Resource method's session argument. + self.session._sdk_connection = self + return self._session def add_service(self, service): """Add a service to the Connection. diff --git a/openstack/tests/functional/cloud/base.py b/openstack/tests/functional/cloud/base.py index dbbcb474b..f84aa6684 100644 --- a/openstack/tests/functional/cloud/base.py +++ b/openstack/tests/functional/cloud/base.py @@ -16,7 +16,7 @@ import os import openstack.config as occ -import openstack.cloud +from openstack import connection from openstack.tests import base @@ -33,19 +33,17 @@ class BaseFunctionalTestCase(base.TestCase): self._set_operator_cloud() self.identity_version = \ - self.operator_cloud.cloud_config.get_api_version('identity') + self.operator_cloud.config.get_api_version('identity') def _set_user_cloud(self, **kwargs): user_config = self.config.get_one( cloud=self._demo_name, **kwargs) - self.user_cloud = openstack.cloud.OpenStackCloud( - cloud_config=user_config) + self.user_cloud = connection.Connection(config=user_config) def _set_operator_cloud(self, **kwargs): operator_config = self.config.get_one( cloud=self._op_name, **kwargs) - self.operator_cloud = openstack.cloud.OpenStackCloud( - cloud_config=operator_config) + self.operator_cloud = connection.Connection(config=operator_config) def pick_image(self): images = self.user_cloud.list_images() diff --git a/openstack/tests/functional/cloud/test_domain.py b/openstack/tests/functional/cloud/test_domain.py index a2c22bf09..bce1dc491 100644 --- a/openstack/tests/functional/cloud/test_domain.py +++ b/openstack/tests/functional/cloud/test_domain.py @@ -27,7 +27,7 @@ class TestDomain(base.BaseFunctionalTestCase): def setUp(self): super(TestDomain, self).setUp() - i_ver = self.operator_cloud.cloud_config.get_api_version('identity') + i_ver = self.operator_cloud.config.get_api_version('identity') if i_ver in ('2', '2.0'): self.skipTest('Identity service does not support domains') self.domain_prefix = self.getUniqueString('domain') diff --git a/openstack/tests/functional/cloud/test_endpoints.py b/openstack/tests/functional/cloud/test_endpoints.py index 2f5216905..8135a18da 100644 --- a/openstack/tests/functional/cloud/test_endpoints.py +++ b/openstack/tests/functional/cloud/test_endpoints.py @@ -103,7 +103,7 @@ class TestEndpoints(base.KeystoneBaseFunctionalTestCase): self.assertIsNotNone(endpoints[0].get('id')) def test_update_endpoint(self): - ver = self.operator_cloud.cloud_config.get_api_version('identity') + ver = self.operator_cloud.config.get_api_version('identity') if ver.startswith('2'): # NOTE(SamYaple): Update endpoint only works with v3 api self.assertRaises(OpenStackCloudUnavailableFeature, diff --git a/openstack/tests/functional/cloud/test_groups.py b/openstack/tests/functional/cloud/test_groups.py index b5e3a4f7d..cad046752 100644 --- a/openstack/tests/functional/cloud/test_groups.py +++ b/openstack/tests/functional/cloud/test_groups.py @@ -27,7 +27,7 @@ class TestGroup(base.BaseFunctionalTestCase): def setUp(self): super(TestGroup, self).setUp() - i_ver = self.operator_cloud.cloud_config.get_api_version('identity') + i_ver = self.operator_cloud.config.get_api_version('identity') if i_ver in ('2', '2.0'): self.skipTest('Identity service does not support groups') self.group_prefix = self.getUniqueString('group') diff --git a/openstack/tests/functional/cloud/test_project.py b/openstack/tests/functional/cloud/test_project.py index 4ac522aa6..6f6277052 100644 --- a/openstack/tests/functional/cloud/test_project.py +++ b/openstack/tests/functional/cloud/test_project.py @@ -73,7 +73,7 @@ class TestProject(base.KeystoneBaseFunctionalTestCase): new_cloud = self.operator_cloud.connect_as_project(project) self.add_info_on_exception( - 'new_cloud_config', pprint.pformat(new_cloud.cloud_config.config)) + 'new_cloud_config', pprint.pformat(new_cloud.config.config)) location = new_cloud.current_location self.assertEqual(project_name, location['project']['name']) diff --git a/openstack/tests/functional/cloud/test_services.py b/openstack/tests/functional/cloud/test_services.py index efaf1fd9b..8359e6598 100644 --- a/openstack/tests/functional/cloud/test_services.py +++ b/openstack/tests/functional/cloud/test_services.py @@ -65,7 +65,7 @@ class TestServices(base.KeystoneBaseFunctionalTestCase): self.assertIsNotNone(service.get('id')) def test_update_service(self): - ver = self.operator_cloud.cloud_config.get_api_version('identity') + ver = self.operator_cloud.config.get_api_version('identity') if ver.startswith('2'): # NOTE(SamYaple): Update service only works with v3 api self.assertRaises(OpenStackCloudUnavailableFeature, diff --git a/openstack/tests/functional/cloud/test_users.py b/openstack/tests/functional/cloud/test_users.py index 3b8f5a27e..2ac99053a 100644 --- a/openstack/tests/functional/cloud/test_users.py +++ b/openstack/tests/functional/cloud/test_users.py @@ -42,7 +42,7 @@ class TestUsers(base.KeystoneBaseFunctionalTestCase): def _create_user(self, **kwargs): domain_id = None - i_ver = self.operator_cloud.cloud_config.get_api_version('identity') + i_ver = self.operator_cloud.config.get_api_version('identity') if i_ver not in ('2', '2.0'): domain = self.operator_cloud.get_domain('default') domain_id = domain['id'] @@ -143,7 +143,7 @@ class TestUsers(base.KeystoneBaseFunctionalTestCase): self.assertIsNotNone(new_cloud.service_catalog) def test_users_and_groups(self): - i_ver = self.operator_cloud.cloud_config.get_api_version('identity') + i_ver = self.operator_cloud.config.get_api_version('identity') if i_ver in ('2', '2.0'): self.skipTest('Identity service does not support groups') diff --git a/openstack/tests/unit/base.py b/openstack/tests/unit/base.py index 2c4eaf08a..d32740a67 100644 --- a/openstack/tests/unit/base.py +++ b/openstack/tests/unit/base.py @@ -119,10 +119,11 @@ class BaseTestCase(base.TestCase): secure_files=['non-existant']) self.cloud_config = self.config.get_one( cloud=test_cloud, validate=False) - self.cloud = openstack.cloud.OpenStackCloud( - cloud_config=self.cloud_config) - self.strict_cloud = openstack.cloud.OpenStackCloud( - cloud_config=self.cloud_config, + self.cloud = openstack.connection.Connection( + config=self.cloud_config, + strict=False) + self.strict_cloud = openstack.connection.Connection( + config=self.cloud_config, strict=True) @@ -460,8 +461,7 @@ class RequestsMockTestCase(BaseTestCase): cloud=test_cloud, validate=True, **kwargs) self.conn = openstack.connection.Connection( config=self.cloud_config) - self.cloud = openstack.cloud.OpenStackCloud( - cloud_config=self.cloud_config) + self.cloud = self.conn def get_glance_discovery_mock_dict( self, diff --git a/openstack/tests/unit/cloud/test_image.py b/openstack/tests/unit/cloud/test_image.py index 9bc08b346..11a95deb1 100644 --- a/openstack/tests/unit/cloud/test_image.py +++ b/openstack/tests/unit/cloud/test_image.py @@ -58,7 +58,7 @@ class TestImage(BaseTestImage): self.use_glance() def test_config_v1(self): - self.cloud.cloud_config.config['image_api_version'] = '1' + self.cloud.config.config['image_api_version'] = '1' # We override the scheme of the endpoint with the scheme of the service # because glance has a bug where it doesn't return https properly. self.assertEqual( @@ -68,7 +68,7 @@ class TestImage(BaseTestImage): '1', self.cloud_config.get_api_version('image')) def test_config_v2(self): - self.cloud.cloud_config.config['image_api_version'] = '2' + self.cloud.config.config['image_api_version'] = '2' # We override the scheme of the endpoint with the scheme of the service # because glance has a bug where it doesn't return https properly. self.assertEqual( @@ -937,7 +937,7 @@ class TestImageV1Only(base.RequestsMockTestCase): def test_config_v1(self): - self.cloud.cloud_config.config['image_api_version'] = '1' + self.cloud.config.config['image_api_version'] = '1' # We override the scheme of the endpoint with the scheme of the service # because glance has a bug where it doesn't return https properly. self.assertEqual( @@ -946,7 +946,7 @@ class TestImageV1Only(base.RequestsMockTestCase): self.assertTrue(self.cloud._is_client_version('image', 1)) def test_config_v2(self): - self.cloud.cloud_config.config['image_api_version'] = '2' + self.cloud.config.config['image_api_version'] = '2' # We override the scheme of the endpoint with the scheme of the service # because glance has a bug where it doesn't return https properly. self.assertEqual( @@ -962,7 +962,7 @@ class TestImageV2Only(base.RequestsMockTestCase): self.use_glance(image_version_json='image-version-v2.json') def test_config_v1(self): - self.cloud.cloud_config.config['image_api_version'] = '1' + self.cloud.config.config['image_api_version'] = '1' # We override the scheme of the endpoint with the scheme of the service # because glance has a bug where it doesn't return https properly. self.assertEqual( @@ -971,7 +971,7 @@ class TestImageV2Only(base.RequestsMockTestCase): self.assertTrue(self.cloud._is_client_version('image', 2)) def test_config_v2(self): - self.cloud.cloud_config.config['image_api_version'] = '2' + self.cloud.config.config['image_api_version'] = '2' # We override the scheme of the endpoint with the scheme of the service # because glance has a bug where it doesn't return https properly. self.assertEqual( diff --git a/openstack/tests/unit/cloud/test_inventory.py b/openstack/tests/unit/cloud/test_inventory.py index 4f4b04104..339fa6695 100644 --- a/openstack/tests/unit/cloud/test_inventory.py +++ b/openstack/tests/unit/cloud/test_inventory.py @@ -12,10 +12,8 @@ import mock -from openstack.cloud import exc from openstack.cloud import inventory import openstack.config -from openstack.config import exceptions as occ_exc from openstack.tests import fakes from openstack.tests.unit import base @@ -26,7 +24,7 @@ class TestInventory(base.TestCase): super(TestInventory, self).setUp() @mock.patch("openstack.config.loader.OpenStackConfig") - @mock.patch("openstack.cloud.OpenStackCloud") + @mock.patch("openstack.connection.Connection") def test__init(self, mock_cloud, mock_config): mock_config.return_value.get_all.return_value = [{}] @@ -40,7 +38,7 @@ class TestInventory(base.TestCase): self.assertTrue(mock_config.return_value.get_all.called) @mock.patch("openstack.config.loader.OpenStackConfig") - @mock.patch("openstack.cloud.OpenStackCloud") + @mock.patch("openstack.connection.Connection") def test__init_one_cloud(self, mock_cloud, mock_config): mock_config.return_value.get_one.return_value = [{}] @@ -56,23 +54,7 @@ class TestInventory(base.TestCase): 'supercloud') @mock.patch("openstack.config.loader.OpenStackConfig") - @mock.patch("openstack.cloud.OpenStackCloud") - def test__raise_exception_on_no_cloud(self, mock_cloud, mock_config): - """ - Test that when os-client-config can't find a named cloud, a - shade exception is emitted. - """ - mock_config.return_value.get_one.side_effect = ( - occ_exc.OpenStackConfigException() - ) - self.assertRaises(exc.OpenStackCloudException, - inventory.OpenStackInventory, - cloud='supercloud') - mock_config.return_value.get_one.assert_called_once_with( - 'supercloud') - - @mock.patch("openstack.config.loader.OpenStackConfig") - @mock.patch("openstack.cloud.OpenStackCloud") + @mock.patch("openstack.connection.Connection") def test_list_hosts(self, mock_cloud, mock_config): mock_config.return_value.get_all.return_value = [{}] @@ -91,7 +73,7 @@ class TestInventory(base.TestCase): self.assertEqual([server], ret) @mock.patch("openstack.config.loader.OpenStackConfig") - @mock.patch("openstack.cloud.OpenStackCloud") + @mock.patch("openstack.connection.Connection") def test_list_hosts_no_detail(self, mock_cloud, mock_config): mock_config.return_value.get_all.return_value = [{}] @@ -110,7 +92,7 @@ class TestInventory(base.TestCase): self.assertFalse(inv.clouds[0].get_openstack_vars.called) @mock.patch("openstack.config.loader.OpenStackConfig") - @mock.patch("openstack.cloud.OpenStackCloud") + @mock.patch("openstack.connection.Connection") def test_search_hosts(self, mock_cloud, mock_config): mock_config.return_value.get_all.return_value = [{}] @@ -126,7 +108,7 @@ class TestInventory(base.TestCase): self.assertEqual([server], ret) @mock.patch("openstack.config.loader.OpenStackConfig") - @mock.patch("openstack.cloud.OpenStackCloud") + @mock.patch("openstack.connection.Connection") def test_get_host(self, mock_cloud, mock_config): mock_config.return_value.get_all.return_value = [{}] diff --git a/openstack/tests/unit/cloud/test_meta.py b/openstack/tests/unit/cloud/test_meta.py index cd2886713..5610d739e 100644 --- a/openstack/tests/unit/cloud/test_meta.py +++ b/openstack/tests/unit/cloud/test_meta.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. +# Copyrigh # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,8 +24,12 @@ PUBLIC_V4 = '192.0.2.99' PUBLIC_V6 = '2001:0db8:face:0da0:face::0b00:1c' # rfc3849 -class FakeCloud(object): +class FakeConfig(object): region_name = 'test-region' + + +class FakeCloud(object): + config = FakeConfig() name = 'test-name' private = False force_ipv4 = False @@ -586,7 +590,7 @@ class TestMeta(base.RequestsMockTestCase): def test_get_server_cloud_rackspace_v6( self, mock_get_flavor_name, mock_get_image_name, mock_get_volumes): - self.cloud.cloud_config.config['has_network'] = False + self.cloud.config.config['has_network'] = False self.cloud._floating_ip_source = None self.cloud.force_ipv4 = False self.cloud._local_ipv6 = True @@ -830,7 +834,7 @@ class TestMeta(base.RequestsMockTestCase): def test_get_server_external_ipv4_nova_public(self): # Testing Clouds w/o Neutron and a network named public - self.cloud.cloud_config.config['has_network'] = False + self.cloud.config.config['has_network'] = False srv = fakes.make_fake_server( server_id='test-id', name='test-name', status='ACTIVE', @@ -841,7 +845,7 @@ class TestMeta(base.RequestsMockTestCase): def test_get_server_external_ipv4_nova_none(self): # Testing Clouds w/o Neutron or a globally routable IP - self.cloud.cloud_config.config['has_network'] = False + self.cloud.config.config['has_network'] = False srv = fakes.make_fake_server( server_id='test-id', name='test-name', status='ACTIVE', diff --git a/openstack/tests/unit/cloud/test_operator.py b/openstack/tests/unit/cloud/test_operator.py index 90d72cd26..c1c3ed863 100644 --- a/openstack/tests/unit/cloud/test_operator.py +++ b/openstack/tests/unit/cloud/test_operator.py @@ -46,7 +46,7 @@ class TestOperatorCloud(base.RequestsMockTestCase): session_mock.get_endpoint.side_effect = side_effect get_session_mock.return_value = session_mock self.cloud.name = 'testcloud' - self.cloud.region_name = 'testregion' + self.cloud.config.region_name = 'testregion' with testtools.ExpectedException( exc.OpenStackCloudException, "Error getting image endpoint on testcloud:testregion:" diff --git a/openstack/tests/unit/config/test_from_session.py b/openstack/tests/unit/config/test_from_session.py index abc13f14d..9b2b1fe29 100644 --- a/openstack/tests/unit/config/test_from_session.py +++ b/openstack/tests/unit/config/test_from_session.py @@ -30,7 +30,7 @@ class TestFromSession(base.RequestsMockTestCase): def test_from_session(self): config = cloud_region.from_session( - self.cloud.keystone_session, region_name=self.test_region) + self.cloud.session, region_name=self.test_region) self.assertEqual(config.name, 'identity.example.com') if not self.test_region: self.assertIsNone(config.region_name) diff --git a/releasenotes/notes/shade-into-connection-81191fb3d0ddaf6e.yaml b/releasenotes/notes/shade-into-connection-81191fb3d0ddaf6e.yaml new file mode 100644 index 000000000..7fdbf3b11 --- /dev/null +++ b/releasenotes/notes/shade-into-connection-81191fb3d0ddaf6e.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + All of the methods formerly part of the ``shade`` library have been added + to the `openstack.connection.Connection`` object.