def get_client(context): """Get a cinder client connection. :param context: request context, instance of ironic.common.context.RequestContext :returns: A cinder client. """ service_auth = keystone.get_auth('cinder') session = _get_cinder_session() # TODO(pas-ha) remove in Rocky adapter_opts = {} # NOTE(pas-ha) new option must always win if set if CONF.cinder.url and not CONF.cinder.endpoint_override: adapter_opts['endpoint_override'] = CONF.cinder.url adapter = keystone.get_adapter('cinder', session=session, auth=service_auth, **adapter_opts) # TODO(pas-ha) use versioned endpoint data to select required # cinder api version cinder_url = adapter.get_endpoint() # TODO(pas-ha) investigate possibility of passing a user context here, # similar to what neutron/glance-related code does # NOTE(pas-ha) cinderclient has both 'connect_retries' (passed to # ksa.Adapter) and 'retries' (used in its subclass of ksa.Adapter) options. # The first governs retries on establishing the HTTP connection, # the second governs retries on OverLimit exceptions from API. # The description of [cinder]/retries fits the first, # so this is what we pass. return client.Client(session=session, auth=service_auth, endpoint_override=cinder_url, connect_retries=CONF.cinder.retries, global_request_id=context.global_id)
def get_client(token=None, context=None): if not context: context = ironic_context.RequestContext(auth_token=token) # NOTE(pas-ha) neutronclient supports passing both session # and the auth to client separately, makes things easier session = _get_neutron_session() service_auth = keystone.get_auth('neutron') # TODO(pas-ha) remove in Rocky, always simply load from config # 'noauth' then would correspond to 'auth_type=none' and # 'endpoint_override' adapter_params = {} if (CONF.neutron.auth_strategy == 'noauth' and CONF.neutron.auth_type is None): CONF.set_override('auth_type', 'none', group='neutron') if not CONF.neutron.endpoint_override: adapter_params['endpoint_override'] = (CONF.neutron.url or DEFAULT_NEUTRON_URL) else: if CONF.neutron.url and not CONF.neutron.endpoint_override: adapter_params['endpoint_override'] = CONF.neutron.url adapter = keystone.get_adapter('neutron', session=session, auth=service_auth, **adapter_params) endpoint = adapter.get_endpoint() user_auth = None if CONF.neutron.auth_type != 'none' and context.auth_token: user_auth = keystone.get_service_auth(context, endpoint, service_auth) return clientv20.Client(session=session, auth=user_auth or service_auth, endpoint_override=endpoint, retries=CONF.neutron.retries, global_request_id=context.global_id, timeout=CONF.neutron.request_timeout)
def _get_session(): global _SESSION if _SESSION is None: auth_strategy = json_rpc.auth_strategy() if auth_strategy == 'keystone': auth = keystone.get_auth('json_rpc') else: auth = None session = keystone.get_session('json_rpc', auth=auth) headers = {'Content-Type': 'application/json'} if auth_strategy == 'http_basic': token = '{}:{}'.format( CONF.json_rpc.http_basic_username, CONF.json_rpc.http_basic_password).encode('utf-8') encoded = base64.b64encode(token).decode('utf-8') headers['Authorization'] = 'Basic {}'.format(encoded) # Adds options like connect_retries _SESSION = keystone.get_adapter('json_rpc', session=session, additional_headers=headers) return _SESSION
def get_client(token=None, context=None): if not context: context = ironic_context.RequestContext(auth_token=token) # NOTE(pas-ha) neutronclient supports passing both session # and the auth to client separately, makes things easier session = _get_neutron_session() service_auth = keystone.get_auth('neutron') # TODO(pas-ha) remove in Rocky, always simply load from config # 'noauth' then would correspond to 'auth_type=none' and # 'endpoint_override' adapter_params = {} if (CONF.neutron.auth_strategy == 'noauth' and CONF.neutron.auth_type is None): CONF.set_override('auth_type', 'none', group='neutron') if not CONF.neutron.endpoint_override: adapter_params['endpoint_override'] = (CONF.neutron.url or DEFAULT_NEUTRON_URL) else: if CONF.neutron.url and not CONF.neutron.endpoint_override: adapter_params['endpoint_override'] = CONF.neutron.url adapter = keystone.get_adapter('neutron', session=session, auth=service_auth, **adapter_params) endpoint = adapter.get_endpoint() user_auth = None if CONF.neutron.auth_type != 'none' and context.auth_token: user_auth = keystone.get_service_auth(context, endpoint, service_auth) return clientv20.Client(session=session, auth=user_auth or service_auth, endpoint_override=endpoint, retries=CONF.neutron.retries, global_request_id=context.global_id)
def _get_session(): global _SESSION if _SESSION is None: kwargs = {} auth_strategy = json_rpc.auth_strategy() if auth_strategy != 'keystone': auth_type = 'none' if auth_strategy == 'noauth' else auth_strategy CONF.set_default('auth_type', auth_type, group='json_rpc') # Deprecated, remove in W if auth_strategy == 'http_basic': if CONF.json_rpc.http_basic_username: kwargs['username'] = CONF.json_rpc.http_basic_username if CONF.json_rpc.http_basic_password: kwargs['password'] = CONF.json_rpc.http_basic_password auth = keystone.get_auth('json_rpc', **kwargs) session = keystone.get_session('json_rpc', auth=auth) headers = {'Content-Type': 'application/json'} # Adds options like connect_retries _SESSION = keystone.get_adapter('json_rpc', session=session, additional_headers=headers) return _SESSION
def get_client(context): """Get a cinder client connection. :param context: request context, instance of ironic.common.context.RequestContext :returns: A cinder client. """ service_auth = keystone.get_auth('cinder') session = _get_cinder_session() # TODO(pas-ha) remove in Rocky adapter_opts = {} # NOTE(pas-ha) new option must always win if set if CONF.cinder.url and not CONF.cinder.endpoint_override: adapter_opts['endpoint_override'] = CONF.cinder.url if CONF.keystone.region_name and not CONF.cinder.region_name: adapter_opts['region_name'] = CONF.keystone.region_name adapter = keystone.get_adapter('cinder', session=session, auth=service_auth, **adapter_opts) # TODO(pas-ha) use versioned endpoint data to select required # cinder api version cinder_url = adapter.get_endpoint() # TODO(pas-ha) investigate possibility of passing a user context here, # similar to what neutron/glance-related code does # NOTE(pas-ha) cinderclient has both 'connect_retries' (passed to # ksa.Adapter) and 'retries' (used in its subclass of ksa.Adapter) options. # The first governs retries on establishing the HTTP connection, # the second governs retries on OverLimit exceptions from API. # The description of [cinder]/retries fits the first, # so this is what we pass. return client.Client(session=session, auth=service_auth, endpoint_override=cinder_url, connect_retries=CONF.cinder.retries, global_request_id=context.global_id)
def wrapper(self, *args, **kwargs): """Wrapper around methods calls. :param image_href: href that describes the location of an image """ if self.client: return func(self, *args, **kwargs) # TODO(pas-ha) remove in Rocky session_params = {} if CONF.glance.glance_api_insecure and not CONF.glance.insecure: session_params['insecure'] = CONF.glance.glance_api_insecure if CONF.glance.glance_cafile and not CONF.glance.cafile: session_params['cacert'] = CONF.glance.glance_cafile # NOTE(pas-ha) glanceclient uses Adapter-based SessionClient, # so we can pass session and auth separately, makes things easier session = _get_glance_session(**session_params) # TODO(pas-ha) remove in Rocky # NOTE(pas-ha) new option must win if configured if (CONF.glance.glance_api_servers and not CONF.glance.endpoint_override): # NOTE(pas-ha) all the 2 methods have image_href as the first # positional arg, but check in kwargs too image_href = args[0] if args else kwargs.get('image_href') url = service_utils.get_glance_api_server(image_href) CONF.set_override('endpoint_override', url, group='glance') # TODO(pas-ha) remove in Rocky if CONF.glance.auth_strategy == 'noauth': CONF.set_override('auth_type', 'none', group='glance') service_auth = keystone.get_auth('glance') # TODO(pas-ha) remove in Rocky adapter_params = {} if CONF.keystone.region_name and not CONF.glance.region_name: adapter_params['region_name'] = CONF.keystone.region_name adapter = keystone.get_adapter('glance', session=session, auth=service_auth, **adapter_params) self.endpoint = adapter.get_endpoint() user_auth = None # NOTE(pas-ha) our ContextHook removes context.auth_token in noauth # case, so when ironic is in noauth but glance is not, we will not # enter the next if-block and use auth from [glance] config section if self.context.auth_token: user_auth = keystone.get_service_auth(self.context, self.endpoint, service_auth) self.client = client.Client(self.version, session=session, auth=user_auth or service_auth, endpoint_override=self.endpoint, global_request_id=self.context.global_id) return func(self, *args, **kwargs)
def test_get_adapter_from_config(self): self.config(valid_interfaces=['internal', 'public'], group=self.test_group) session = keystone.get_session(self.test_group) adapter = keystone.get_adapter(self.test_group, session=session, interface='admin') self.assertEqual('admin', adapter.interface) self.assertEqual(session, adapter.session)
def test_get_adapter_from_config(self): self.config(valid_interfaces=['internal', 'public'], group=self.test_group) session = keystone.get_session(self.test_group) adapter = keystone.get_adapter(self.test_group, session=session, interface='admin') self.assertEqual('admin', adapter.interface) self.assertEqual(session, adapter.session)
def _get_nova_adapter(): global _NOVA_ADAPTER if not _NOVA_ADAPTER: _NOVA_ADAPTER = keystone.get_adapter( 'nova', session=keystone.get_session('nova'), auth=keystone.get_auth('nova'), version=NOVA_API_VERSION) return _NOVA_ADAPTER
def wrapper(self, *args, **kwargs): """Wrapper around methods calls. :param image_href: href that describes the location of an image """ if self.client: return func(self, *args, **kwargs) # TODO(pas-ha) remove in Rocky session_params = {} if CONF.glance.glance_api_insecure and not CONF.glance.insecure: session_params['insecure'] = CONF.glance.glance_api_insecure if CONF.glance.glance_cafile and not CONF.glance.cafile: session_params['cacert'] = CONF.glance.glance_cafile # NOTE(pas-ha) glanceclient uses Adapter-based SessionClient, # so we can pass session and auth separately, makes things easier session = _get_glance_session(**session_params) # TODO(pas-ha) remove in Rocky # NOTE(pas-ha) new option must win if configured if (CONF.glance.glance_api_servers and not CONF.glance.endpoint_override): # NOTE(pas-ha) all the 2 methods have image_href as the first # positional arg, but check in kwargs too image_href = args[0] if args else kwargs.get('image_href') url = service_utils.get_glance_api_server(image_href) CONF.set_override('endpoint_override', url, group='glance') # TODO(pas-ha) remove in Rocky if CONF.glance.auth_strategy == 'noauth': CONF.set_override('auth_type', 'none', group='glance') service_auth = keystone.get_auth('glance') adapter_params = {} adapter = keystone.get_adapter('glance', session=session, auth=service_auth, **adapter_params) self.endpoint = adapter.get_endpoint() user_auth = None # NOTE(pas-ha) our ContextHook removes context.auth_token in noauth # case, so when ironic is in noauth but glance is not, we will not # enter the next if-block and use auth from [glance] config section if self.context.auth_token: user_auth = keystone.get_service_auth(self.context, self.endpoint, service_auth) self.client = client.Client(2, session=session, auth=user_auth or service_auth, endpoint_override=self.endpoint, global_request_id=self.context.global_id) return func(self, *args, **kwargs)
def _get_session(): global _SESSION if _SESSION is None: if json_rpc.require_authentication(): auth = keystone.get_auth('json_rpc') else: auth = None session = keystone.get_session('json_rpc', auth=auth) session.headers = {'Content-Type': 'application/json'} # Adds options like connect_retries _SESSION = keystone.get_adapter('json_rpc', session=session) return _SESSION
def __init__(self): """Initialize the connection with swift or radosgw :raises: ConfigInvalid if required keystone authorization credentials with swift are missing. """ params = {'retries': CONF.swift.swift_max_retries} if CONF.deploy.object_store_endpoint_type == 'radosgw': params.update({ 'authurl': CONF.swift.auth_url, 'user': CONF.swift.username, 'key': CONF.swift.password }) else: # NOTE(pas-ha) swiftclient still (as of 3.3.0) does not use # (adapter-based) SessionClient, and uses the passed in session # only to resolve endpoint and get a token, # but not to make further requests to Swift itself (LP 1736135). # Thus we need to deconstruct back all the adapter- and # session-related args as loaded by keystoneauth from config # to pass them to the client explicitly. # TODO(pas-ha) re-write this when swiftclient is brought on par # with other OS clients re auth plugins, sessions and adapters # support. # TODO(pas-ha) pass the context here and use token from context # with service auth params['session'] = session = get_swift_session() adapter = keystone.get_adapter('swift', session=session) params['os_options'] = { 'object_storage_url': adapter.get_endpoint() } # deconstruct back session-related options params['timeout'] = session.timeout if session.verify is False: params['insecure'] = True elif isinstance(session.verify, six.string_types): params['cacert'] = session.verify if session.cert: # NOTE(pas-ha) although setting cert as path to single file # with both client cert and key is supported by Session, # keystoneauth loading always sets the session.cert # as tuple of cert and key. params['cert'], params['cert_key'] = session.cert self.connection = swift_client.Connection(**params)
def _get_client(context): """Helper to get inspector client instance.""" # NOTE(pas-ha) remove in Rocky if CONF.auth_strategy != 'keystone': CONF.set_override('auth_type', 'none', group='inspector') service_auth = keystone.get_auth('inspector') session = _get_inspector_session(auth=service_auth) adapter_params = {} if CONF.inspector.service_url and not CONF.inspector.endpoint_override: adapter_params['endpoint_override'] = CONF.inspector.service_url adapter = keystone.get_adapter('inspector', session=session, **adapter_params) inspector_url = adapter.get_endpoint() # TODO(pas-ha) investigate possibility of passing user context here, # similar to what neutron/glance-related code does # NOTE(pas-ha) ironic-inspector-client has no Adaper-based # SessionClient, so we'll resolve inspector API form adapter loaded # form config options # TODO(pas-ha) rewrite when inspectorclient is based on ksa Adapter, # also add global_request_id to the call return client.ClientV1(api_version=INSPECTOR_API_VERSION, session=session, inspector_url=inspector_url)
def _get_client(context): """Helper to get inspector client instance.""" # NOTE(pas-ha) remove in Rocky if CONF.auth_strategy != 'keystone': CONF.set_override('auth_type', 'none', group='inspector') service_auth = keystone.get_auth('inspector') session = _get_inspector_session(auth=service_auth) adapter_params = {} if CONF.inspector.service_url and not CONF.inspector.endpoint_override: adapter_params['endpoint_override'] = CONF.inspector.service_url adapter = keystone.get_adapter('inspector', session=session, **adapter_params) inspector_url = adapter.get_endpoint() # TODO(pas-ha) investigate possibility of passing user context here, # similar to what neutron/glance-related code does # NOTE(pas-ha) ironic-inspector-client has no Adaper-based # SessionClient, so we'll resolve inspector API form adapter loaded # form config options # TODO(pas-ha) rewrite when inspectorclient is based on ksa Adapter, # also add global_request_id to the call return client.ClientV1(api_version=INSPECTOR_API_VERSION, session=session, inspector_url=inspector_url)
def __init__(self): """Initialize the connection with swift :raises: ConfigInvalid if required keystone authorization credentials with swift are missing. """ params = {'retries': CONF.swift.swift_max_retries} # NOTE(pas-ha) swiftclient still (as of 3.3.0) does not use # (adapter-based) SessionClient, and uses the passed in session # only to resolve endpoint and get a token, # but not to make further requests to Swift itself (LP 1736135). # Thus we need to deconstruct back all the adapter- and # session-related args as loaded by keystoneauth from config # to pass them to the client explicitly. # TODO(pas-ha) re-write this when swiftclient is brought on par # with other OS clients re auth plugins, sessions and adapters # support. # TODO(pas-ha) pass the context here and use token from context # with service auth params['session'] = session = get_swift_session() adapter = keystone.get_adapter('swift', session=session) params['os_options'] = {'object_storage_url': adapter.get_endpoint()} # deconstruct back session-related options params['timeout'] = session.timeout if session.verify is False: params['insecure'] = True elif isinstance(session.verify, six.string_types): params['cacert'] = session.verify if session.cert: # NOTE(pas-ha) although setting cert as path to single file # with both client cert and key is supported by Session, # keystoneauth loading always sets the session.cert # as tuple of cert and key. params['cert'], params['cert_key'] = session.cert self.connection = swift_client.Connection(**params)
def swift_temp_url(self, image_info): """Generate a no-auth Swift temporary URL. This function will generate (or return the cached one if temp URL cache is enabled) the temporary Swift URL using the image id from Glance and the config options: 'swift_endpoint_url', 'swift_api_version', 'swift_account' and 'swift_container'. The temporary URL will be valid for 'swift_temp_url_duration' seconds. This allows Ironic to download a Glance image without passing around an auth_token. :param image_info: The return from a GET request to Glance for a certain image_id. Should be a dictionary, with keys like 'name' and 'checksum'. See https://docs.openstack.org/glance/latest/user/glanceapi.html for examples. :returns: A signed Swift URL from which an image can be downloaded, without authentication. :raises: InvalidParameterValue if Swift config options are not set correctly. :raises: MissingParameterValue if a required parameter is not set. :raises: ImageUnacceptable if the image info from Glance does not have an image ID. """ self._validate_temp_url_config() if ('id' not in image_info or not uuidutils.is_uuid_like(image_info['id'])): raise exc.ImageUnacceptable( _('The given image info does not have a valid image id: %s') % image_info) image_id = image_info['id'] url_fragments = { 'api_version': CONF.glance.swift_api_version, 'container': self._get_swift_container(image_id), 'object_id': image_id } endpoint_url = CONF.glance.swift_endpoint_url if not endpoint_url: swift_session = swift.get_swift_session() adapter = keystone.get_adapter('swift', session=swift_session) endpoint_url = adapter.get_endpoint() if not endpoint_url: raise exc.MissingParameterValue( _('Swift temporary URLs require a Swift endpoint URL, but it ' 'was not found in the service catalog. ' 'You must provide "swift_endpoint_url" as a config option.')) # Strip /v1/AUTH_%(tenant_id)s, if present endpoint_url = re.sub('/v1/AUTH_[^/]+/?$', '', endpoint_url) key = CONF.glance.swift_temp_url_key if CONF.deploy.object_store_endpoint_type == 'radosgw': chunks = urlparse.urlsplit(CONF.glance.swift_endpoint_url) if not chunks.path: endpoint_url = urlparse.urljoin(endpoint_url, 'swift') elif chunks.path != '/swift': raise exc.InvalidParameterValue( _('Swift endpoint URL should only contain scheme, ' 'hostname, optional port and optional /swift path ' 'without trailing slash; provided value is: %s') % endpoint_url) template = '/{api_version}/{container}/{object_id}' else: account = CONF.glance.swift_account if not account: swift_session = swift.get_swift_session() auth_ref = swift_session.auth.get_auth_ref(swift_session) account = 'AUTH_%s' % auth_ref.project_id if not key: swift_api = swift.SwiftAPI() key_header = 'x-account-meta-temp-url-key' key = swift_api.connection.head_account().get(key_header) if not key: raise exc.MissingParameterValue( _('Swift temporary URLs require a shared secret to be ' 'created. You must provide "swift_temp_url_key" as a ' 'config option or pre-generate the key on the project ' 'used to access Swift.')) url_fragments['account'] = account template = '/{api_version}/{account}/{container}/{object_id}' url_path = template.format(**url_fragments) return self._generate_temp_url( path=url_path, seconds=CONF.glance.swift_temp_url_duration, key=key, method='GET', endpoint=endpoint_url, image_id=image_id)
def swift_temp_url(self, image_info): """Generate a no-auth Swift temporary URL. This function will generate (or return the cached one if temp URL cache is enabled) the temporary Swift URL using the image id from Glance and the config options: 'swift_endpoint_url', 'swift_api_version', 'swift_account' and 'swift_container'. The temporary URL will be valid for 'swift_temp_url_duration' seconds. This allows Ironic to download a Glance image without passing around an auth_token. :param image_info: The return from a GET request to Glance for a certain image_id. Should be a dictionary, with keys like 'name' and 'checksum'. See https://docs.openstack.org/glance/latest/user/glanceapi.html for examples. :returns: A signed Swift URL from which an image can be downloaded, without authentication. :raises: InvalidParameterValue if Swift config options are not set correctly. :raises: MissingParameterValue if a required parameter is not set. :raises: ImageUnacceptable if the image info from Glance does not have an image ID. """ self._validate_temp_url_config() if ('id' not in image_info or not uuidutils.is_uuid_like(image_info['id'])): raise exc.ImageUnacceptable(_( 'The given image info does not have a valid image id: %s') % image_info) image_id = image_info['id'] url_fragments = { 'api_version': CONF.glance.swift_api_version, 'container': self._get_swift_container(image_id), 'object_id': image_id } endpoint_url = CONF.glance.swift_endpoint_url if not endpoint_url: swift_session = swift.get_swift_session() adapter = keystone.get_adapter('swift', session=swift_session) endpoint_url = adapter.get_endpoint() if not endpoint_url: raise exc.MissingParameterValue(_( 'Swift temporary URLs require a Swift endpoint URL, but it ' 'was not found in the service catalog. ' 'You must provide "swift_endpoint_url" as a config option.')) # Strip /v1/AUTH_%(tenant_id)s, if present endpoint_url = re.sub('/v1/AUTH_[^/]+/?$', '', endpoint_url) key = CONF.glance.swift_temp_url_key account = CONF.glance.swift_account if not account: swift_session = swift.get_swift_session() auth_ref = swift_session.auth.get_auth_ref(swift_session) account = 'AUTH_%s' % auth_ref.project_id if not key: swift_api = swift.SwiftAPI() key_header = 'x-account-meta-temp-url-key' key = swift_api.connection.head_account().get(key_header) if not key: raise exc.MissingParameterValue(_( 'Swift temporary URLs require a shared secret to be ' 'created. You must provide "swift_temp_url_key" as a ' 'config option or pre-generate the key on the project ' 'used to access Swift.')) url_fragments['account'] = account template = '/{api_version}/{account}/{container}/{object_id}' url_path = template.format(**url_fragments) return self._generate_temp_url( path=url_path, seconds=CONF.glance.swift_temp_url_duration, key=key, method='GET', endpoint=endpoint_url, image_id=image_id )