def nova(context): args = { 'auth_url': CONF.keystone_url, 'auth_token': context.auth_token, # NOTE(ft): These parameters are not used for authentification, # but are required by novaclient < v2.18 which may be installed in # Icehouse deployment 'username': None, 'api_key': None, 'project_id': None, } global _novaclient_vertion, _nova_service_type bypass_url = _url_for(context, service_type=_nova_service_type) if not bypass_url and _nova_service_type == 'computev21': # NOTE(ft): partial compatibility with pre Kilo OS releases: # if computev21 isn't provided by Nova, use compute instead logger.warning(_LW("Nova server doesn't support v2.1, use v2 instead. " "A lot of useful EC2 compliant instance properties " "will be unavailable.")) _nova_service_type = 'compute' return nova(context) try: return novaclient.Client(_novaclient_vertion, bypass_url=bypass_url, **args) except nova_exception.UnsupportedVersion: if _novaclient_vertion == '2': raise # NOTE(ft): partial compatibility with Nova client w/o microversion # support logger.warning(_LW("Nova client doesn't support v2.3, use v2 instead. " "A lot of useful EC2 compliant instance properties " "will be unavailable.")) _novaclient_vertion = '2' return nova(context)
def _run_cleanups(self, cleanups): for function, args, kwargs in reversed(cleanups): try: function(*args, **kwargs) except Exception: if inspect.ismethod(function): name = '%s.%s.%s' % (function.im_class.__module__, function.im_class.__name__, function.__name__) elif inspect.isfunction(function): name = '%s.%s' % (function.__module__, function.__name__) else: name = '%s.%s' % (function.__class__.__module__, function.__class__.__name__) formatted_args = '' args_string = ', '.join([repr(arg) for arg in args]) kwargs_string = ', '.join( ['%s=%r' % (key, value) for key, value in kwargs.items()]) if args_string: formatted_args = args_string if kwargs_string: if formatted_args: formatted_args += ', ' formatted_args += kwargs_string LOG.warning(_LW('Error cleaning up %(name)s(%(args)s)') % { 'name': name, 'args': formatted_args }, exc_info=True) pass
def __init__(self, user_id, project_id, request_id=None, is_admin=None, remote_address=None, auth_token=None, user_name=None, project_name=None, overwrite=True, service_catalog=None, api_version=None, is_os_admin=None, **kwargs): """Parameters :param overwrite: Set to False to ensure that the greenthread local copy of the index is not overwritten. :param kwargs: Extra arguments that might be present, but we ignore because they possibly came in from older rpc messages. """ user = kwargs.pop('user', None) tenant = kwargs.pop('tenant', None) super(RequestContext, self).__init__(auth_token=auth_token, user=user_id or user, tenant=project_id or tenant, is_admin=is_admin, request_id=request_id, resource_uuid=kwargs.pop('resource_uuid', None), overwrite=overwrite) # oslo_context's RequestContext.to_dict() generates this field, we can # safely ignore this as we don't use it. kwargs.pop('user_identity', None) if kwargs: LOG.warning( _LW('Arguments dropped when creating context: %s') % str(kwargs)) self.user_id = user_id self.project_id = project_id self.remote_address = remote_address timestamp = timeutils.utcnow() if isinstance(timestamp, six.string_types): timestamp = timeutils.parse_strtime(timestamp) self.timestamp = timestamp self.service_catalog = service_catalog if self.service_catalog is None: # if list is empty or none self.service_catalog = [] self.user_name = user_name self.project_name = project_name self.is_admin = is_admin # TODO(ft): call policy.check_is_admin if is_admin is None self.is_os_admin = is_os_admin self.api_version = api_version
def _run_cleanups(self, cleanups): for function, args, kwargs in reversed(cleanups): try: function(*args, **kwargs) except Exception: if inspect.ismethod(function): name = '%s.%s.%s' % (function.im_class.__module__, function.im_class.__name__, function.__name__) elif inspect.isfunction(function): name = '%s.%s' % (function.__module__, function.__name__) else: name = '%s.%s' % (function.__class__.__module__, function.__class__.__name__) formatted_args = '' args_string = ', '.join([repr(arg) for arg in args]) kwargs_string = ', '.join([ '%s=%r' % (key, value) for key, value in kwargs.items() ]) if args_string: formatted_args = args_string if kwargs_string: if formatted_args: formatted_args += ', ' formatted_args += kwargs_string LOG.warning( _LW('Error cleaning up %(name)s(%(args)s)') % {'name': name, 'args': formatted_args}, exc_info=True) pass
def delayed_create(context, image, name, os_instance): try: os_instance.stop() # wait instance for really stopped start_time = time.time() while os_instance.status != 'SHUTOFF': time.sleep(1) os_instance.get() # NOTE(yamahata): timeout and error. 1 hour for now for safety. # Is it too short/long? # Or is there any better way? timeout = 1 * 60 * 60 if time.time() > start_time + timeout: err = (_("Couldn't stop instance within %d sec") % timeout) raise exception.EC2Exception(message=err) # NOTE(ft): create an image with ec2_id metadata to let other code # link os and db objects in race conditions os_image_id = os_instance.create_image( name, metadata={'ec2_id': image['id']}) image['os_id'] = os_image_id db_api.update_item(context, image) except Exception: LOG.exception(_LE('Failed to complete image %s creation'), image.id) try: image['state'] = 'failed' db_api.update_item(context, image) except Exception: LOG.warning(_LW("Couldn't set 'failed' state for db image %s"), image.id, exc_info=True) try: os_instance.start() except Exception: LOG.warning(_LW('Failed to start instance %(i_id)s after ' 'completed creation of image %(image_id)s'), { 'i_id': instance['id'], 'image_id': image['id'] }, exc_info=True)
def _get_nova_api_version(context): client = novaclient.Client(REQUIRED_NOVA_API_VERSION, session=context.session, service_type=CONF.nova_service_type) required = nova_api_versions.APIVersion(REQUIRED_NOVA_API_MICROVERSION) current = client.versions.get_current() if not current: logger.warning( _LW('Could not check Nova API version because no version ' 'was found in Nova version list for url %(url)s of service ' 'type "%(service_type)s". ' 'Use v%(required_api_version)s Nova API.'), { 'url': client.client.get_endpoint(), 'service_type': CONF.nova_service_type, 'required_api_version': REQUIRED_NOVA_API_MICROVERSION }) return REQUIRED_NOVA_API_MICROVERSION if current.id != REQUIRED_NOVA_API_VERSION_ID: logger.warning( _LW('Specified "%s" Nova service type does not support v2.1 API. ' 'A lot of useful EC2 compliant instance properties ' 'will be unavailable.'), CONF.nova_service_type) return LEGACY_NOVA_API_VERSION if (nova_api_versions.APIVersion(current.version) < required): logger.warning( _LW('Nova support v%(nova_api_version)s, ' 'but v%(required_api_version)s is required. ' 'A lot of useful EC2 compliant instance properties ' 'will be unavailable.'), { 'nova_api_version': current.version, 'required_api_version': REQUIRED_NOVA_API_MICROVERSION }) return current.version logger.info( _LI('Provided Nova API version is v%(nova_api_version)s, ' 'used one is v%(required_api_version)s'), { 'nova_api_version': current.version, 'required_api_version': (REQUIRED_NOVA_API_MICROVERSION) }) return REQUIRED_NOVA_API_MICROVERSION
def delayed_create(context, image, name, os_instance): try: os_instance.stop() # wait instance for really stopped start_time = time.time() while os_instance.status != 'SHUTOFF': time.sleep(1) os_instance.get() # NOTE(yamahata): timeout and error. 1 hour for now for safety. # Is it too short/long? # Or is there any better way? timeout = 1 * 60 * 60 if time.time() > start_time + timeout: err = (_("Couldn't stop instance within %d sec") % timeout) raise exception.EC2Exception(message=err) # NOTE(ft): create an image with ec2_id metadata to let other code # link os and db objects in race conditions os_image_id = os_instance.create_image( name, metadata={'ec2_id': image['id']}) image['os_id'] = os_image_id db_api.update_item(context, image) except Exception: LOG.exception(_LE('Failed to complete image %s creation'), image.id) try: image['state'] = 'failed' db_api.update_item(context, image) except Exception: LOG.warning(_LW("Couldn't set 'failed' state for db image %s"), image.id, exc_info=True) try: os_instance.start() except Exception: LOG.warning(_LW('Failed to start instance %(i_id)s after ' 'completed creation of image %(image_id)s'), {'i_id': instance['id'], 'image_id': image['id']}, exc_info=True)
def _get_nova_api_version(context): client = novaclient.Client(REQUIRED_NOVA_API_VERSION, session=context.session, service_type=CONF.nova_service_type) required = nova_api_versions.APIVersion(REQUIRED_NOVA_API_MICROVERSION) current = client.versions.get_current() if not current: logger.warning( _LW('Could not check Nova API version because no version ' 'was found in Nova version list for url %(url)s of service ' 'type "%(service_type)s". ' 'Use v%(required_api_version)s Nova API.'), {'url': client.client.get_endpoint(), 'service_type': CONF.nova_service_type, 'required_api_version': REQUIRED_NOVA_API_MICROVERSION}) return REQUIRED_NOVA_API_MICROVERSION if current.id != REQUIRED_NOVA_API_VERSION_ID: logger.warning( _LW('Specified "%s" Nova service type does not support v2.1 API. ' 'A lot of useful EC2 compliant instance properties ' 'will be unavailable.'), CONF.nova_service_type) return LEGACY_NOVA_API_VERSION if (nova_api_versions.APIVersion(current.version) < required): logger.warning( _LW('Nova support v%(nova_api_version)s, ' 'but v%(required_api_version)s is required. ' 'A lot of useful EC2 compliant instance properties ' 'will be unavailable.'), {'nova_api_version': current.version, 'required_api_version': REQUIRED_NOVA_API_MICROVERSION}) return current.version logger.info(_LI('Provided Nova API version is v%(nova_api_version)s, ' 'used one is v%(required_api_version)s'), {'nova_api_version': current.version, 'required_api_version': ( REQUIRED_NOVA_API_MICROVERSION)}) return REQUIRED_NOVA_API_MICROVERSION
def nova(context): args = { 'auth_url': CONF.keystone_url, 'auth_token': context.auth_token, # NOTE(ft): These parameters are not used for authentification, # but are required by novaclient < v2.18 which may be installed in # Icehouse deployment 'username': None, 'api_key': None, 'project_id': None, 'insecure': CONF.ssl_insecure, 'cacert': CONF.ssl_ca_file } global _novaclient_vertion, _nova_service_type bypass_url = _url_for(context, service_type=_nova_service_type) if not bypass_url and _nova_service_type == 'computev21': # NOTE(ft): partial compatibility with pre Kilo OS releases: # if computev21 isn't provided by Nova, use compute instead logger.warning( _LW("Nova server doesn't support v2.1, use v2 instead. " "A lot of useful EC2 compliant instance properties " "will be unavailable.")) _nova_service_type = 'compute' return nova(context) try: return novaclient.Client(_novaclient_vertion, bypass_url=bypass_url, **args) except nova_exception.UnsupportedVersion: if _novaclient_vertion == '2': raise # NOTE(ft): partial compatibility with Nova client w/o microversion # support logger.warning( _LW("Nova client doesn't support v2.3, use v2 instead. " "A lot of useful EC2 compliant instance properties " "will be unavailable.")) _novaclient_vertion = '2' return nova(context)
def __init__(self, user_id, project_id, request_id=None, is_admin=None, remote_address=None, auth_token=None, user_name=None, project_name=None, overwrite=True, service_catalog=None, api_version=None, is_os_admin=None, **kwargs): """Parameters :param overwrite: Set to False to ensure that the greenthread local copy of the index is not overwritten. :param kwargs: Extra arguments that might be present, but we ignore because they possibly came in from older rpc messages. """ user = kwargs.pop('user', None) tenant = kwargs.pop('tenant', None) super(RequestContext, self).__init__( auth_token=auth_token, user=user_id or user, tenant=project_id or tenant, is_admin=is_admin, request_id=request_id, resource_uuid=kwargs.pop('resource_uuid', None), overwrite=overwrite) # oslo_context's RequestContext.to_dict() generates this field, we can # safely ignore this as we don't use it. kwargs.pop('user_identity', None) self.session = kwargs.pop('session', None) if kwargs: LOG.warning(_LW('Arguments dropped when creating context: %s') % str(kwargs)) self.user_id = user_id self.project_id = project_id self.remote_address = remote_address timestamp = timeutils.utcnow() if isinstance(timestamp, six.string_types): timestamp = timeutils.parse_strtime(timestamp) self.timestamp = timestamp self.service_catalog = service_catalog if self.service_catalog is None: # if list is empty or none self.service_catalog = [] self.user_name = user_name self.project_name = project_name self.is_admin = is_admin # TODO(ft): call policy.check_is_admin if is_admin is None self.is_os_admin = is_os_admin self.api_version = api_version
def _proxy_request(self, req, requester): headers = self._build_proxy_request_headers(requester) nova_ip_port = '%s:%s' % (CONF.metadata.nova_metadata_ip, CONF.metadata.nova_metadata_port) url = urlparse.urlunsplit(( CONF.metadata.nova_metadata_protocol, nova_ip_port, req.path_info, req.query_string, '')) h = httplib2.Http( ca_certs=CONF.metadata.auth_ca_cert, disable_ssl_certificate_validation=( CONF.metadata.nova_metadata_insecure) ) if (CONF.metadata.nova_client_cert and CONF.metadata.nova_client_priv_key): h.add_certificate(CONF.metadata.nova_client_priv_key, CONF.metadata.nova_client_cert, nova_ip_port) resp, content = h.request(url, method=req.method, headers=headers, body=req.body) if resp.status == 200: LOG.debug(str(resp)) req.response.content_type = resp['content-type'] req.response.body = content return req.response elif resp.status == 403: LOG.warn(_LW( 'The remote metadata server responded with Forbidden. This ' 'response usually occurs when shared secrets do not match.' )) return webob.exc.HTTPForbidden() elif resp.status == 400: return webob.exc.HTTPBadRequest() elif resp.status == 404: return webob.exc.HTTPNotFound() elif resp.status == 409: return webob.exc.HTTPConflict() elif resp.status == 500: msg = _( 'Remote metadata server experienced an internal server error.' ) LOG.warn(msg) return webob.exc.HTTPInternalServerError( explanation=six.text_type(msg)) else: raise Exception(_('Unexpected response code: %s') % resp.status)
def _url_for(context, **kwargs): service_type = kwargs['service_type'] if service_type == 'compute': url = CONF.service_catalog.compute url = url.replace('TENANT_ID', context.project_id) elif service_type == 'image': url = CONF.service_catalog.image elif service_type == 'network': url = CONF.service_catalog.network elif service_type == 'volumev2': url = CONF.service_catalog.volumev2 url = url.replace('TENANT_ID', context.project_id) else: logger.warning(_LW("Unknown service type in JCS Layer.")) return url
def _unpack_request_attributes(self, req): os_instance_id = req.headers.get('X-Instance-ID') project_id = req.headers.get('X-Tenant-ID') signature = req.headers.get('X-Instance-ID-Signature') remote_ip = req.headers.get('X-Forwarded-For') if not remote_ip: raise exception.EC2MetadataInvalidAddress() if os_instance_id is None: msg = _('X-Instance-ID header is missing from request.') elif project_id is None: msg = _('X-Tenant-ID header is missing from request.') elif not isinstance(os_instance_id, six.string_types): msg = _('Multiple X-Instance-ID headers found within request.') elif not isinstance(project_id, six.string_types): msg = _('Multiple X-Tenant-ID headers found within request.') else: msg = None if msg: raise webob.exc.HTTPBadRequest(explanation=msg) expected_signature = hmac.new( CONF.metadata.metadata_proxy_shared_secret, os_instance_id, hashlib.sha256).hexdigest() if not utils.constant_time_compare(expected_signature, signature): LOG.warning( _LW('X-Instance-ID-Signature: %(signature)s does ' 'not match the expected value: ' '%(expected_signature)s for id: ' '%(instance_id)s. Request From: ' '%(remote_ip)s'), { 'signature': signature, 'expected_signature': expected_signature, 'instance_id': os_instance_id, 'remote_ip': remote_ip }) msg = _('Invalid proxy request signature.') raise webob.exc.HTTPForbidden(explanation=msg) return os_instance_id, project_id, remote_ip
def _unpack_request_attributes(self, req): os_instance_id = req.headers.get('X-Instance-ID') project_id = req.headers.get('X-Tenant-ID') signature = req.headers.get('X-Instance-ID-Signature') remote_ip = req.headers.get('X-Forwarded-For') if not remote_ip: raise exception.EC2MetadataInvalidAddress() if os_instance_id is None: msg = _('X-Instance-ID header is missing from request.') elif project_id is None: msg = _('X-Tenant-ID header is missing from request.') elif not isinstance(os_instance_id, six.string_types): msg = _('Multiple X-Instance-ID headers found within request.') elif not isinstance(project_id, six.string_types): msg = _('Multiple X-Tenant-ID headers found within request.') else: msg = None if msg: raise webob.exc.HTTPBadRequest(explanation=msg) expected_signature = hmac.new( CONF.metadata.metadata_proxy_shared_secret, os_instance_id, hashlib.sha256).hexdigest() if not utils.constant_time_compare(expected_signature, signature): LOG.warning(_LW( 'X-Instance-ID-Signature: %(signature)s does ' 'not match the expected value: ' '%(expected_signature)s for id: ' '%(instance_id)s. Request From: ' '%(remote_ip)s'), {'signature': signature, 'expected_signature': expected_signature, 'instance_id': os_instance_id, 'remote_ip': remote_ip}) msg = _('Invalid proxy request signature.') raise webob.exc.HTTPForbidden(explanation=msg) return os_instance_id, project_id, remote_ip
def _validate_signature(self, signature, requester_id, requester_ip): expected_signature = hmac.new( CONF.metadata.metadata_proxy_shared_secret.encode("utf-8"), requester_id.encode(), hashlib.sha256).hexdigest() if not (signature and utils.constant_time_compare(expected_signature, signature)): LOG.warning(_LW( 'X-Instance-ID-Signature: %(signature)s does ' 'not match the expected value: ' '%(expected_signature)s for id: ' '%(requester_id)s. Request From: ' '%(requester_ip)s'), {'signature': signature, 'expected_signature': expected_signature, 'requester_id': requester_id, 'requester_ip': requester_ip}) msg = _('Invalid proxy request signature.') raise webob.exc.HTTPForbidden(explanation=msg)