def pick_microversion(session, required): """Get a new microversion if it is higher than session's default. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param required: Version that is required for an action. :type required: String or tuple or None. :return: ``required`` as a string if the ``session``'s default is too low, the ``session``'s default otherwise. Returns ``None`` of both are ``None``. :raises: TypeError if ``required`` is invalid. """ if required is not None: required = discover.normalize_version_number(required) if session.default_microversion is not None: default = discover.normalize_version_number( session.default_microversion) if required is None: required = default else: required = (default if discover.version_match(required, default) else required) if required is not None: return discover.version_to_string(required)
def pick_microversion(session, required): """Get a new microversion if it is higher than session's default. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param required: Version that is required for an action. :type required: String or tuple or None. :return: ``required`` as a string if the ``session``'s default is too low, the ``session``'s default otherwise. Returns ``None`` of both are ``None``. :raises: TypeError if ``required`` is invalid. """ if required is not None: required = discover.normalize_version_number(required) if session.default_microversion is not None: default = discover.normalize_version_number( session.default_microversion) if required is None: required = default else: required = (default if discover.version_match(required, default) else required) if required is not None: return discover.version_to_string(required)
def maximum_supported_microversion(adapter, client_maximum): """Determinte the maximum microversion supported by both client and server. :param adapter: :class:`~keystoneauth1.adapter.Adapter` instance. :param client_maximum: Maximum microversion supported by the client. If ``None``, ``None`` is returned. :returns: the maximum supported microversion as string or ``None``. """ if client_maximum is None: return None endpoint_data = adapter.get_endpoint_data() if not endpoint_data.max_microversion: return None client_max = discover.normalize_version_number(client_maximum) server_max = discover.normalize_version_number( endpoint_data.max_microversion) if endpoint_data.min_microversion: server_min = discover.normalize_version_number( endpoint_data.min_microversion) if client_max < server_min: # NOTE(dtantsur): we may want to raise in this case, but this keeps # the current behavior intact. return None result = min(client_max, server_max) return discover.version_to_string(result)
def _format_endpoint(endpoint, **kwargs): # can't format AUTH_INTERFACE object so replace with string if kwargs.get('service_type') is plugin.AUTH_INTERFACE: kwargs['service_type'] = 'identity' version = kwargs.get('version') if version: discover.normalize_version_number(version) kwargs['version'] = ".".join(str(v) for v in version) return endpoint % kwargs # pass kwargs ok?
def _create_identity_server(self): # NOTE(jamielennox): Loading Session here should be exactly the # same as calling Session.load_from_conf_options(CONF, GROUP) # however we can't do that because we have to use _conf_get to # support the paste.ini options. sess = session_loading.Session().load_from_options( cert=self._conf_get('certfile'), key=self._conf_get('keyfile'), cacert=self._conf_get('cafile'), insecure=self._conf_get('insecure'), timeout=self._conf_get('http_connect_timeout'), user_agent=self._build_useragent_string()) auth_plugin = self._get_auth_plugin() adap = adapter.Adapter( sess, auth=auth_plugin, service_type='identity', interface='admin', region_name=self._conf_get('region_name'), connect_retries=self._conf_get('http_request_max_retries')) auth_version = self._conf_get('auth_version') if auth_version is not None: auth_version = discover.normalize_version_number(auth_version) return _identity.IdentityServer( self.log, adap, include_service_catalog=self._include_service_catalog, requested_auth_version=auth_version)
def _create_identity_server(self): # NOTE(jamielennox): Loading Session here should be exactly the # same as calling Session.load_from_conf_options(CONF, GROUP) # however we can't do that because we have to use _conf_get to # support the paste.ini options. sess = session_loading.Session().load_from_options( cert=self._conf_get('certfile'), key=self._conf_get('keyfile'), cacert=self._conf_get('cafile'), insecure=self._conf_get('insecure'), timeout=self._conf_get('http_connect_timeout'), user_agent=self._build_useragent_string() ) auth_plugin = self._get_auth_plugin() adap = adapter.Adapter( sess, auth=auth_plugin, service_type='identity', interface='admin', region_name=self._conf_get('region_name'), connect_retries=self._conf_get('http_request_max_retries')) auth_version = self._conf_get('auth_version') if auth_version is not None: auth_version = discover.normalize_version_number(auth_version) return _identity.IdentityServer( self.log, adap, include_service_catalog=self._include_service_catalog, requested_auth_version=auth_version)
def version_data(self): """Get an endpoint with its versions""" all_version_data = [] if not self.endpoint_with_versions: return None else: for endpoint in self.endpoint_with_versions: if not endpoint: continue for version in endpoint: if not self.desired_version: if 'version' in version and version['version']: all_version_data.append(version) elif 'id' in version: all_version_data.append(version) else: if 'version' in version and version['version']: if self.exact_match: if version['version'] == str( self.desired_version): all_version_data.append(version) else: current_micro_version = normalize_version_number( version['version']) required_micro_version = normalize_version_number( str(self.desired_version)) if version_match(required_micro_version, current_micro_version): all_version_data.append(version) elif 'id' in version and version["id"]: if self.exact_match: if version['id'] == 'v' + str( self.desired_version): all_version_data.append(version) else: current_version = normalize_version_number( version['id']) required_version = normalize_version_number( str(self.desired_version)) if version_match(required_version, current_version): all_version_data.append(version) return all_version_data
def supports_microversion(adapter, microversion, raise_exception=False): """Determine if the given adapter supports the given microversion. Checks the min and max microversion asserted by the service and checks to make sure that ``min <= microversion <= max``. Current default microversion is taken into consideration if set and verifies that ``microversion <= default``. :param adapter: :class:`~keystoneauth1.adapter.Adapter` instance. :param str microversion: String containing the desired microversion. :param bool raise_exception: Raise exception when requested microversion is not supported be the server side or is higher than the current default microversion. :returns: True if the service supports the microversion. :rtype: bool :raises: :class:`~openstack.exceptions.SDKException` when requested microversion is not supported. """ endpoint_data = adapter.get_endpoint_data() if (endpoint_data.min_microversion and endpoint_data.max_microversion and discover.version_between( endpoint_data.min_microversion, endpoint_data.max_microversion, microversion)): if adapter.default_microversion is not None: # If default_microversion is set - evaluate # whether it match the expectation candidate = discover.normalize_version_number( adapter.default_microversion) required = discover.normalize_version_number(microversion) supports = discover.version_match(required, candidate) if raise_exception and not supports: raise exceptions.SDKException( 'Required microversion {ver} is higher than currently ' 'selected {curr}'.format( ver=microversion, curr=adapter.default_microversion)) return supports return True if raise_exception: raise exceptions.SDKException( 'Required microversion {ver} is not supported ' 'by the server side'.format(ver=microversion)) return False
def maximum_supported_microversion(adapter, client_maximum): """Determinte the maximum microversion supported by both client and server. :param adapter: :class:`~keystoneauth1.adapter.Adapter` instance. :param client_maximum: Maximum microversion supported by the client. If ``None``, ``None`` is returned. :returns: the maximum supported microversion as string or ``None``. """ if client_maximum is None: return None # NOTE(dtantsur): if we cannot determine supported microversions, fall back # to the default one. try: endpoint_data = adapter.get_endpoint_data() except keystoneauth1.exceptions.discovery.DiscoveryFailure: endpoint_data = None if endpoint_data is None: log = _log.setup_logging('openstack') log.warning('Cannot determine endpoint data for service %s', adapter.service_type or adapter.service_name) return None if not endpoint_data.max_microversion: return None client_max = discover.normalize_version_number(client_maximum) server_max = discover.normalize_version_number( endpoint_data.max_microversion) if endpoint_data.min_microversion: server_min = discover.normalize_version_number( endpoint_data.min_microversion) if client_max < server_min: # NOTE(dtantsur): we may want to raise in this case, but this keeps # the current behavior intact. return None result = min(client_max, server_max) return discover.version_to_string(result)
def maximum_supported_microversion(adapter, client_maximum): """Determinte the maximum microversion supported by both client and server. :param adapter: :class:`~keystoneauth1.adapter.Adapter` instance. :param client_maximum: Maximum microversion supported by the client. If ``None``, ``None`` is returned. :returns: the maximum supported microversion as string or ``None``. """ if client_maximum is None: return None # NOTE(dtantsur): if we cannot determine supported microversions, fall back # to the default one. try: endpoint_data = adapter.get_endpoint_data() except keystoneauth1.exceptions.discovery.DiscoveryFailure: endpoint_data = None if endpoint_data is None: log = _log.setup_logging('openstack') log.warning('Cannot determine endpoint data for service %s', adapter.service_type or adapter.service_name) return None if not endpoint_data.max_microversion: return None client_max = discover.normalize_version_number(client_maximum) server_max = discover.normalize_version_number( endpoint_data.max_microversion) if endpoint_data.min_microversion: server_min = discover.normalize_version_number( endpoint_data.min_microversion) if client_max < server_min: # NOTE(dtantsur): we may want to raise in this case, but this keeps # the current behavior intact. return None result = min(client_max, server_max) return discover.version_to_string(result)
def get_endpoint(self, session, interface=None, version=None, **kwargs): """Return an endpoint for the client. There are no required keyword arguments to ``get_endpoint`` as a plugin implementation should use best effort with the information available to determine the endpoint. :param session: The session object that the auth_plugin belongs to. :type session: keystoneauth1.session.Session :param version: The version number required for this endpoint. :type version: tuple or str :param str interface: what visibility the endpoint should have. :returns: The base URL that will be used to talk to the required service or None if not available. :rtype: string """ if interface == plugin.AUTH_INTERFACE: return self._identity_uri if not version: # NOTE(jamielennox): This plugin can only be used within auth_token # and auth_token will always provide version= with requests. return None if not self._discover: self._discover = discover.Discover(session, url=self._identity_uri, authenticated=False) if not self._discover.url_for(version): # NOTE(jamielennox): The requested version is not supported by the # identity server. return None # NOTE(jamielennox): for backwards compatibility here we don't # actually use the URL from discovery we hack it up instead. :( # NOTE(blk-u): Normalizing the version is a workaround for bug 1450272. # This can be removed once that's fixed. Also fix the docstring for the # version parameter to be just "tuple". version = discover.normalize_version_number(version) if discover.version_match((2, 0), version): return '%s/v2.0' % self._identity_uri elif discover.version_match((3, 0), version): return '%s/v3' % self._identity_uri # NOTE(jamielennox): This plugin will only get called from auth_token # middleware. The middleware should never request a version that the # plugin doesn't know how to handle. msg = _('Invalid version asked for in auth_token plugin') raise NotImplementedError(msg)
def get_endpoint(self, session, interface=None, version=None, **kwargs): """Return an endpoint for the client. There are no required keyword arguments to ``get_endpoint`` as a plugin implementation should use best effort with the information available to determine the endpoint. :param session: The session object that the auth_plugin belongs to. :type session: keystoneauth1.session.Session :param version: The version number required for this endpoint. :type version: tuple or str :param str interface: what visibility the endpoint should have. :returns: The base URL that will be used to talk to the required service or None if not available. :rtype: string """ if interface == plugin.AUTH_INTERFACE: return self._identity_uri if not version: # NOTE(jamielennox): This plugin can only be used within auth_token # and auth_token will always provide version= with requests. return None if not self._discover: self._discover = discover.Discover(session, url=self._identity_uri, authenticated=False) if not self._discover.url_for(version): # NOTE(jamielennox): The requested version is not supported by the # identity server. return None # NOTE(jamielennox): for backwards compatibility here we don't # actually use the URL from discovery we hack it up instead. :( # NOTE(blk-u): Normalizing the version is a workaround for bug 1450272. # This can be removed once that's fixed. Also fix the docstring for the # version parameter to be just "tuple". version = discover.normalize_version_number(version) if discover.version_match((2, 0), version): return '%s/v2.0' % self._identity_uri elif discover.version_match((3, 0), version): return '%s/v3' % self._identity_uri # NOTE(jamielennox): This plugin will only get called from auth_token # middleware. The middleware should never request a version that the # plugin doesn't know how to handle. msg = _('Invalid version asked for in auth_token plugin') raise NotImplementedError(msg)
def _create_identity_server(self): adap = adapter.Adapter( self._session, auth=self._auth, service_type='identity', interface='admin', region_name=self._conf.get('region_name'), connect_retries=self._conf.get('http_request_max_retries')) auth_version = self._conf.get('auth_version') if auth_version is not None: auth_version = discover.normalize_version_number(auth_version) return _identity.IdentityServer( self.log, adap, include_service_catalog=self._include_service_catalog, requested_auth_version=auth_version)
def _create_identity_server(self): adap = adapter.Adapter( self._session, auth=self._auth, service_type='identity', interface='admin', region_name=self._conf.get('region_name'), connect_retries=self._conf.get('http_request_max_retries')) auth_version = self._conf.get('auth_version') if auth_version is not None: auth_version = discover.normalize_version_number(auth_version) return _identity.IdentityServer( self.log, adap, include_service_catalog=self._include_service_catalog, requested_auth_version=auth_version)
def _set_microversion_headers(headers, microversion, service_type, endpoint_filter): # We're converting it to normalized version number for two reasons. # First, to validate it's a real version number. Second, so that in # the future we can pre-validate that it is within the range of # available microversions before we send the request. # TODO(mordred) Validate when we get the response back that # the server executed in the microversion we expected. # TODO(mordred) Validate that the requested microversion works # with the microversion range we found in discovery. microversion = discover.normalize_version_number(microversion) # Can't specify a M.latest microversion if (microversion[0] != discover.LATEST and discover.LATEST in microversion[1:]): raise TypeError( "Specifying a '{major}.latest' microversion is not allowed.") microversion = discover.version_to_string(microversion) if not service_type: if endpoint_filter and 'service_type' in endpoint_filter: service_type = endpoint_filter['service_type'] else: raise TypeError( "microversion {microversion} was requested but no" " service_type information is available. Either provide a" " service_type in endpoint_filter or pass" " microversion_service_type as an argument.".format( microversion=microversion)) # TODO(mordred) cinder uses volume in its microversion header. This # logic should be handled in the future by os-service-types but for # now hard-code for cinder. if (service_type.startswith('volume') or service_type == 'block-storage'): service_type = 'volume' headers.setdefault( 'OpenStack-API-Version', '{service_type} {microversion}'.format(service_type=service_type, microversion=microversion)) header_names = _mv_legacy_headers_for_service(service_type) for h in header_names: headers.setdefault(h, microversion)
def assertVersion(out, inp): self.assertEqual(out, discover.normalize_version_number(inp))
def assertVersion(inp, out): self.assertEqual(out, discover.normalize_version_number(inp))