def call(self, ctxt, method, **kwargs): """Invoke a method and wait for a reply. See RPCClient.call().""" if self.target.fanout: raise exceptions.InvalidTarget('A call cannot be used with fanout', self.target) msg = self._make_message(ctxt, method, kwargs) msg_ctxt = self.serializer.serialize_context(ctxt) span_host = tomograph.getHost() ser_name = "%s[%s]" % ("RPC_call", self.target.topic) tomograph.start(ser_name, self.target.topic, span_host, 0) trace_info = tomograph.get_trace_info() msg_ctxt["trace_id"] = trace_info[0] msg_ctxt["span_id"] = trace_info[1] timeout = self.timeout if self.timeout is None: timeout = self.conf.rpc_response_timeout if self.version_cap: self._check_version_cap(msg.get('version')) try: result = self.transport._send(self.target, msg_ctxt, msg, wait_for_reply=True, timeout=timeout, retry=self.retry) tomograph.stop(self.target.topic) except driver_base.TransportDriverError as ex: raise ClientSendError(self.target, ex) return self.serializer.deserialize_entity(ctxt, result)
def call(self, ctxt, method, **kwargs): """Invoke a method and wait for a reply. See RPCClient.call().""" if self.target.fanout: raise exceptions.InvalidTarget("A call cannot be used with fanout", self.target) msg = self._make_message(ctxt, method, kwargs) msg_ctxt = self.serializer.serialize_context(ctxt) span_host = tomograph.getHost() ser_name = "%s[%s]" % ("RPC_call", self.target.topic) tomograph.start(ser_name, self.target.topic, span_host, 0) trace_info = tomograph.get_trace_info() msg_ctxt["trace_id"] = trace_info[0] msg_ctxt["span_id"] = trace_info[1] timeout = self.timeout if self.timeout is None: timeout = self.conf.rpc_response_timeout if self.version_cap: self._check_version_cap(msg.get("version")) try: result = self.transport._send( self.target, msg_ctxt, msg, wait_for_reply=True, timeout=timeout, retry=self.retry ) tomograph.stop(self.target.topic) except driver_base.TransportDriverError as ex: raise ClientSendError(self.target, ex) return self.serializer.deserialize_entity(ctxt, result)
def request(self, url, method, **kwargs): endpoint_filter = kwargs.setdefault('endpoint_filter', {}) self._set_endpoint_filter_kwargs(endpoint_filter) if self.endpoint_override: kwargs.setdefault('endpoint_override', self.endpoint_override) if self.auth: kwargs.setdefault('auth', self.auth) if self.user_agent: kwargs.setdefault('user_agent', self.user_agent) if self.connect_retries is not None: kwargs.setdefault('connect_retries', self.connect_retries) if self.logger: kwargs.setdefault('logger', self.logger) if isinstance(self.session, Session): if self.user_agent: span_service_name = self.user_agent else: span_service_name = "unknown user_agent" if self.service_type: span_name = self.service_type else: span_name = "unknown sevice_type" span_host = tomograph.getHost() ser_name = "%s[%s]" % (span_service_name, span_name) tomograph.start_http_h(ser_name, span_name, kwargs["headers"], span_host, 0) tomograph.add_trace_info_header(kwargs["headers"]) ret = self.session.request(url, method, **kwargs) tomograph.stop(span_name) return ret else: return self.session.request(url, method, **kwargs)
def dispatch(self, obj, action, *args, **kwargs): """Find action-specific method on self and call it.""" try: method = getattr(obj, action) if isinstance(args[0], Request): ser_name = "%s[%s]" % (method.__module__, method.__name__) tomograph.start_http(ser_name, method.__name__, args[0]) tomograph.stop(method.__name__) except AttributeError: method = getattr(obj, 'default') return method(*args, **kwargs)
def process_request(self, request): """Process request. Evaluate the headers in a request and attempt to authenticate the request against the identity server. If authenticated then additional headers are added to the request for use by applications. If not authenticated the request will be rejected or marked unauthenticated depending on configuration. """ # pdb.set_trace() tomograph.start_http( "keystonemiddleware.auth_token.AuthProtocol[process_request]", "process_request", request) tomograph.add_trace_info_header(request.headers) self._token_cache.initialize(request.environ) resp = super(AuthProtocol, self).process_request(request) tomograph.stop("process_request") if resp: return resp if not request.user_token: # if no user token is present then that's an invalid request request.user_token_valid = False # NOTE(jamielennox): The service status is allowed to be missing if a # service token is not passed. If the service status is missing that's # a valid request. We should find a better way to expose this from the # request object. user_status = request.user_token and request.user_token_valid service_status = request.headers.get('X-Service-Identity-Status', 'Confirmed') if not (user_status and service_status == 'Confirmed'): if self._delay_auth_decision: self.log.info(_LI('Deferring reject downstream')) else: self.log.info(_LI('Rejecting request')) self._reject_request() if request.user_token_valid: request.set_user_headers(request.token_auth._user_auth_ref, self._include_service_catalog) if request.service_token and request.service_token_valid: request.set_service_headers(request.token_auth._serv_auth_ref) if self.log.isEnabledFor(logging.DEBUG): self.log.debug('Received request from %s', request.token_auth._log_format)
def dec(request, *args, **kwargs): if dashboard: request.horizon['dashboard'] = dashboard if panel: request.horizon['panel'] = panel if not tomograph.tracing_started(): span_host = tomograph.getHost() ser_name = "%s[%s]" % (view_func.__module__, view_func.__name__) tomograph.start(ser_name, view_func.__name__, span_host, 0) ret = view_func(request, *args, **kwargs) tomograph.stop(view_func.__name__) return ret return view_func(request, *args, **kwargs)
def process_request(self, request): """Process request. Evaluate the headers in a request and attempt to authenticate the request against the identity server. If authenticated then additional headers are added to the request for use by applications. If not authenticated the request will be rejected or marked unauthenticated depending on configuration. """ # pdb.set_trace() tomograph.start_http("keystonemiddleware.auth_token.AuthProtocol[process_request]", "process_request", request) tomograph.add_trace_info_header(request.headers) self._token_cache.initialize(request.environ) resp = super(AuthProtocol, self).process_request(request) tomograph.stop("process_request") if resp: return resp if not request.user_token: # if no user token is present then that's an invalid request request.user_token_valid = False # NOTE(jamielennox): The service status is allowed to be missing if a # service token is not passed. If the service status is missing that's # a valid request. We should find a better way to expose this from the # request object. user_status = request.user_token and request.user_token_valid service_status = request.headers.get("X-Service-Identity-Status", "Confirmed") if not (user_status and service_status == "Confirmed"): if self._delay_auth_decision: self.log.info(_LI("Deferring reject downstream")) else: self.log.info(_LI("Rejecting request")) self._reject_request() if request.user_token_valid: request.set_user_headers(request.token_auth._user_auth_ref, self._include_service_catalog) if request.service_token and request.service_token_valid: request.set_service_headers(request.token_auth._serv_auth_ref) if self.log.isEnabledFor(logging.DEBUG): self.log.debug("Received request from %s", request.token_auth._log_format)
def inner(*args, **kwargs): try: span_host = tomograph.getHost() ser_name = "%s[%s]" % (func.__module__, func.__name__) tomograph.start(ser_name, func.__name__, span_host, 0) ret = func(*args, **kwargs) tomograph.stop(func.__name__) return ret # Take advantage of the fact that we can catch # multiple exception types using a tuple of # exception classes, with subclass detection # for free. Any exception that is not in or # derived from the args passed to us will be # ignored and thrown as normal. except exceptions: tomograph.stop(func.__name__) raise rpc_dispatcher.ExpectedException()
def cast(self, ctxt, method, **kwargs): """Invoke a method and return immediately. See RPCClient.cast().""" msg = self._make_message(ctxt, method, kwargs) msg_ctxt = self.serializer.serialize_context(ctxt) span_host = tomograph.getHost() ser_name = "%s[%s]" % ("RPC_cast", self.target.topic) tomograph.start(ser_name, self.target.topic, span_host, 0) trace_info = tomograph.get_trace_info() msg_ctxt["trace_id"] = trace_info[0] msg_ctxt["span_id"] = trace_info[1] if self.version_cap: self._check_version_cap(msg.get("version")) try: self.transport._send(self.target, msg_ctxt, msg, retry=self.retry) tomograph.stop(self.target.topic) except driver_base.TransportDriverError as ex: raise ClientSendError(self.target, ex)
def cast(self, ctxt, method, **kwargs): """Invoke a method and return immediately. See RPCClient.cast().""" msg = self._make_message(ctxt, method, kwargs) msg_ctxt = self.serializer.serialize_context(ctxt) span_host = tomograph.getHost() ser_name = "%s[%s]" % ("RPC_cast", self.target.topic) tomograph.start(ser_name, self.target.topic, span_host, 0) trace_info = tomograph.get_trace_info() msg_ctxt["trace_id"] = trace_info[0] msg_ctxt["span_id"] = trace_info[1] if self.version_cap: self._check_version_cap(msg.get('version')) try: self.transport._send(self.target, msg_ctxt, msg, retry=self.retry) tomograph.stop(self.target.topic) except driver_base.TransportDriverError as ex: raise ClientSendError(self.target, ex)
def __init__(self, endpoint, **kwargs): self.endpoint = endpoint self.identity_headers = kwargs.get('identity_headers') self.auth_token = kwargs.get('token') if self.identity_headers: if self.identity_headers.get('X-Auth-Token'): self.auth_token = self.identity_headers.get('X-Auth-Token') del self.identity_headers['X-Auth-Token'] self.session = requests.Session() self.session.headers["User-Agent"] = USER_AGENT if self.auth_token: self.session.headers["X-Auth-Token"] = self.auth_token self.timeout = float(kwargs.get('timeout', 600)) if self.endpoint.startswith("https"): compression = kwargs.get('ssl_compression', True) if compression is False: # Note: This is not seen by default. (python must be # run with -Wd) warnings.warn( 'The "ssl_compression" argument has been ' 'deprecated.', DeprecationWarning) if kwargs.get('insecure', False) is True: self.session.verify = False else: if kwargs.get('cacert', None) is not '': self.session.verify = kwargs.get('cacert', True) self.session.cert = (kwargs.get('cert_file'), kwargs.get('key_file')) span_host = tomograph.getHost() span_name = "HTTPClient" ser_name = "%s[%s]" % (USER_AGENT, span_name) tomograph.start_http_h(ser_name, span_name, self.session.headers, span_host, 0) tomograph.add_trace_info_header(self.session.headers) tomograph.stop(span_name)
def __init__(self, endpoint, **kwargs): self.endpoint = endpoint self.identity_headers = kwargs.get("identity_headers") self.auth_token = kwargs.get("token") if self.identity_headers: if self.identity_headers.get("X-Auth-Token"): self.auth_token = self.identity_headers.get("X-Auth-Token") del self.identity_headers["X-Auth-Token"] self.session = requests.Session() self.session.headers["User-Agent"] = USER_AGENT if self.auth_token: self.session.headers["X-Auth-Token"] = self.auth_token self.timeout = float(kwargs.get("timeout", 600)) if self.endpoint.startswith("https"): compression = kwargs.get("ssl_compression", True) if compression is False: # Note: This is not seen by default. (python must be # run with -Wd) warnings.warn('The "ssl_compression" argument has been ' "deprecated.", DeprecationWarning) if kwargs.get("insecure", False) is True: self.session.verify = False else: if kwargs.get("cacert", None) is not "": self.session.verify = kwargs.get("cacert", True) self.session.cert = (kwargs.get("cert_file"), kwargs.get("key_file")) span_host = tomograph.getHost() span_name = "HTTPClient" ser_name = "%s[%s]" % (USER_AGENT, span_name) tomograph.start_http_h(ser_name, span_name, self.session.headers, span_host, 0) tomograph.add_trace_info_header(self.session.headers) tomograph.stop(span_name)
def _do_dispatch(self, endpoint, method, ctxt, args, executor_callback): #pdb.set_trace() trace_info = None if ctxt.__contains__("trace_id"): trace_info = ctxt.pop("trace_id"), ctxt.pop("span_id") ctxt = self.serializer.deserialize_context(ctxt) new_args = dict() for argname, arg in six.iteritems(args): new_args[argname] = self.serializer.deserialize_entity(ctxt, arg) func = getattr(endpoint, method) span_host = tomograph.getHost() ser_name = "%s[%s]" % (func.__module__, func.__name__) tomograph.start(ser_name, func.__name__, span_host, 0, trace_info) if executor_callback: result = executor_callback(func, ctxt, **new_args) else: result = func(ctxt, **new_args) tomograph.stop(func.__name__) return self.serializer.serialize_entity(ctxt, result)
def __call__(self, req): arg_dict = req.environ['wsgiorg.routing_args'][1] action = arg_dict.pop('action') del arg_dict['controller'] # allow middleware up the stack to provide context, params and headers. context = req.environ.get(CONTEXT_ENV, {}) context['query_string'] = dict(req.params.items()) context['headers'] = dict(req.headers.items()) context['path'] = req.environ['PATH_INFO'] scheme = (None if not CONF.secure_proxy_ssl_header else req.environ.get(CONF.secure_proxy_ssl_header)) if scheme: # NOTE(andrey-mp): "wsgi.url_scheme" contains the protocol used # before the proxy removed it ('https' usually). So if # the webob.Request instance is modified in order to use this # scheme instead of the one defined by API, the call to # webob.Request.relative_url() will return a URL with the correct # scheme. req.environ['wsgi.url_scheme'] = scheme context['host_url'] = req.host_url params = req.environ.get(PARAMS_ENV, {}) # authentication and authorization attributes are set as environment # values by the container and processed by the pipeline. the complete # set is not yet know. context['environment'] = req.environ context['accept_header'] = req.accept req.environ = None params.update(arg_dict) context.setdefault('is_admin', False) # TODO(termie): do some basic normalization on methods method = getattr(self, action) # NOTE(morganfainberg): use the request method to normalize the # response code between GET and HEAD requests. The HTTP status should # be the same. LOG.info( '%(req_method)s %(uri)s', { 'req_method': req.environ['REQUEST_METHOD'].upper(), 'uri': wsgiref.util.request_uri(req.environ), }) params = self._normalize_dict(params) try: ser_name = "%s[%s]" % (method.__module__, method.__name__) tomograph.start_http(ser_name, method.__name__, req) result = method(context, **params) tomograph.stop(method.__name__) except exception.Unauthorized as e: LOG.warning( _LW("Authorization failed. %(exception)s from " "%(remote_addr)s"), { 'exception': e, 'remote_addr': req.environ['REMOTE_ADDR'] }) return render_exception(e, context=context, user_locale=best_match_language(req)) except exception.Error as e: LOG.warning(six.text_type(e)) return render_exception(e, context=context, user_locale=best_match_language(req)) except TypeError as e: LOG.exception(six.text_type(e)) return render_exception(exception.ValidationError(e), context=context, user_locale=best_match_language(req)) except Exception as e: LOG.exception(six.text_type(e)) return render_exception(exception.UnexpectedError(exception=e), context=context, user_locale=best_match_language(req)) if result is None: return render_response(status=(204, 'No Content')) elif isinstance(result, six.string_types): return result elif isinstance(result, webob.Response): return result elif isinstance(result, webob.exc.WSGIHTTPException): return result response_code = self._get_response_code(req) return render_response(body=result, status=response_code, method=req.environ['REQUEST_METHOD'])
def main(self, argv): # Parse args once to find version parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) # build available subcommands based on version api_version = options.os_identity_api_version subcommand_parser = self.get_subcommand_parser(api_version) self.parser = subcommand_parser # Handle top-level --help/-h before attempting to parse # a command off the command line if not argv or options.help: self.do_help(options) return 0 # Parse args again and call whatever callback was selected args = subcommand_parser.parse_args(argv) # Short-circuit and deal with help command right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if args.debug: logging_level = logging.DEBUG iso_logger = logging.getLogger('iso8601') iso_logger.setLevel('WARN') else: logging_level = logging.WARNING logging.basicConfig(level=logging_level) # TODO(heckj): supporting backwards compatibility with environment # variables. To be removed after DEVSTACK is updated, ideally in # the Grizzly release cycle. args.os_token = args.os_token or env('SERVICE_TOKEN') args.os_endpoint = args.os_endpoint or env('SERVICE_ENDPOINT') if utils.isunauthenticated(args.func): self.cs = shell_generic.CLIENT_CLASS(endpoint=args.os_auth_url, cacert=args.os_cacert, key=args.os_key, cert=args.os_cert, insecure=args.insecure, timeout=args.timeout) else: self.auth_check(args) token = None if args.os_token and args.os_endpoint: token = args.os_token api_version = options.os_identity_api_version self.cs = self.get_api_class(api_version)( username=args.os_username, tenant_name=args.os_tenant_name, tenant_id=args.os_tenant_id, token=token, endpoint=args.os_endpoint, password=args.os_password, auth_url=args.os_auth_url, region_name=args.os_region_name, cacert=args.os_cacert, key=args.os_key, cert=args.os_cert, insecure=args.insecure, debug=args.debug, use_keyring=args.os_cache, force_new_token=args.force_new_token, stale_duration=args.stale_duration, timeout=args.timeout) try: span_host = tomograph.getHost() span_name = ' '.join(sys.argv) tomograph.start("keystoneclient-shell", span_name, span_host, 0) args.func(self.cs, args) tomograph.stop(span_name) except exc.Unauthorized: tomograph.tag("Exception", "Unauthorized") tomograph.stop(span_name) raise exc.CommandError("Invalid OpenStack Identity credentials.") except exc.AuthorizationFailure: tomograph.tag("Exception", "AuthorizationFailure") tomograph.stop(span_name) raise exc.CommandError("Unable to authorize user")
def _process_stack(self, request, action, action_args, content_type, body, accept): """Implement the processing stack.""" # Get the implementing method try: meth, extensions = self.get_method(request, action, content_type, body) except (AttributeError, TypeError): return Fault(webob.exc.HTTPNotFound()) except KeyError as ex: msg = _("There is no such action: %s") % ex.args[0] return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") return Fault(webob.exc.HTTPBadRequest(explanation=msg)) ser_name = "%s[%s]" % (meth.__module__, meth.__name__) tomograph.start_http(ser_name, meth.__name__, request) if body: msg = _("Action: '%(action)s', calling method: %(meth)s, body: " "%(body)s") % {'action': action, 'body': six.text_type(body, 'utf-8'), 'meth': str(meth)} LOG.debug(strutils.mask_password(msg)) else: LOG.debug("Calling method '%(meth)s'", {'meth': str(meth)}) # Now, deserialize the request body... try: contents = {} if self._should_have_body(request): # allow empty body with PUT and POST if request.content_length == 0: contents = {'body': None} else: contents = self.deserialize(meth, content_type, body) except exception.InvalidContentType: msg = _("Unsupported Content-Type") tomograph.tag("Exception", "Unsupported Content-Type") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") tomograph.tag("Exception", "Malformed request body") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Update the action args action_args.update(contents) project_id = action_args.pop("project_id", None) context = request.environ.get('nova.context') if (context and project_id and (project_id != context.project_id)): msg = _("Malformed request URL: URL's project_id '%(project_id)s'" " doesn't match Context's project_id" " '%(context_project_id)s'") % \ {'project_id': project_id, 'context_project_id': context.project_id} tomograph.tag("Exception", "HTTPBadRequest") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Run pre-processing extensions response, post = self.pre_process_extensions(extensions, request, action_args) if not response: try: with ResourceExceptionHandler(): action_result = self.dispatch(meth, request, action_args) except Fault as ex: response = ex if not response: # No exceptions; convert action_result into a # ResponseObject resp_obj = None if type(action_result) is dict or action_result is None: resp_obj = ResponseObject(action_result) elif isinstance(action_result, ResponseObject): resp_obj = action_result else: response = action_result # Run post-processing extensions if resp_obj: # Do a preserialize to set up the response object serializers = getattr(meth, 'wsgi_serializers', {}) resp_obj._bind_method_serializers(serializers) if hasattr(meth, 'wsgi_code'): resp_obj._default_code = meth.wsgi_code resp_obj.preserialize(accept, self.default_serializers) # Process post-processing extensions response = self.post_process_extensions(post, resp_obj, request, action_args) if resp_obj and not response: response = resp_obj.serialize(request, accept, self.default_serializers) if hasattr(response, 'headers'): for hdr, val in response.headers.items(): # Headers must be utf-8 strings response.headers[hdr] = utils.utf8(str(val)) if not request.api_version_request.is_null(): response.headers[API_VERSION_REQUEST_HEADER] = \ request.api_version_request.get_string() response.headers['Vary'] = API_VERSION_REQUEST_HEADER tomograph.stop(meth.__name__) return response
def main(self, argv): # Parse args once to find version # NOTE(flepied) Under Python3, parsed arguments are removed # from the list so make a copy for the first parsing base_argv = copy.deepcopy(argv) parser = self.get_base_parser() (options, args) = parser.parse_known_args(base_argv) span_host = tomograph.getHost() span_name = ' '.join(sys.argv) tomograph.start("glanceclient-shell", span_name, span_host, 0) try: # NOTE(flaper87): Try to get the version from the # image-url first. If no version was specified, fallback # to the api-image-version arg. If both of these fail then # fallback to the minimum supported one and let keystone # do the magic. endpoint = self._get_image_url(options) endpoint, url_version = utils.strip_version(endpoint) except ValueError: # NOTE(flaper87): ValueError is raised if no endpoint is povided url_version = None # build available subcommands based on version try: api_version = int(options.os_image_api_version or url_version or 2) if api_version not in SUPPORTED_VERSIONS: raise ValueError except ValueError: msg = ("Invalid API version parameter. " "Supported values are %s" % SUPPORTED_VERSIONS) utils.exit(msg=msg) if api_version == 2: switch_version = self._cache_schemas(options) if switch_version: print('WARNING: The client is falling back to v1 because' ' the accessing to v2 failed. This behavior will' ' be removed in future versions') api_version = 1 try: subcommand_parser = self.get_subcommand_parser(api_version) except ImportError as e: if options.debug: traceback.print_exc() if not str(e): # Add a generic import error message if the raised ImportError # has none. raise ImportError('Unable to import module. Re-run ' 'with --debug for more info.') raise except Exception: if options.debug: traceback.print_exc() raise self.parser = subcommand_parser # Handle top-level --help/-h before attempting to parse # a command off the command line if options.help or not argv: self.do_help(options) return 0 # Parse args again and call whatever callback was selected args = subcommand_parser.parse_args(argv) # Short-circuit and deal with help command right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 LOG = logging.getLogger('glanceclient') LOG.addHandler(logging.StreamHandler()) LOG.setLevel(logging.DEBUG if args.debug else logging.INFO) profile = osprofiler_profiler and options.profile if profile: osprofiler_profiler.init(options.profile) client = self._get_versioned_client(api_version, args, force_auth=False) try: # pdb.set_trace() args.func(client, args) except exc.Unauthorized: raise exc.CommandError("Invalid OpenStack Identity credentials.") except Exception: # NOTE(kragniz) Print any exceptions raised to stderr if the # --debug flag is set if args.debug: traceback.print_exc() raise finally: tomograph.stop(span_name) if profile: trace_id = osprofiler_profiler.get().get_base_id() print("Profiling trace ID: %s" % trace_id) print("To display trace use next command:\n" "osprofiler trace show --html %s " % trace_id)
def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' (args, args_list) = parser.parse_known_args(argv) self.setup_debugging(args.debug) self.extensions = [] do_help = ('help' in argv) or not argv # Discover available auth plugins #tomograph.annotate("Discover available auth plugins", "novaclient-shell") novaclient.auth_plugin.discover_auth_systems() if not args.os_compute_api_version: api_version = api_versions.get_api_version( DEFAULT_MAJOR_OS_COMPUTE_API_VERSION) else: api_version = api_versions.get_api_version( args.os_compute_api_version) os_username = args.os_username os_user_id = args.os_user_id os_password = None # Fetched and set later as needed os_tenant_name = args.os_tenant_name os_tenant_id = args.os_tenant_id os_auth_url = args.os_auth_url os_region_name = args.os_region_name os_auth_system = args.os_auth_system endpoint_type = args.endpoint_type insecure = args.insecure service_type = args.service_type service_name = args.service_name volume_service_name = args.volume_service_name bypass_url = args.bypass_url os_cache = args.os_cache cacert = args.os_cacert timeout = args.timeout keystone_session = None keystone_auth = None # We may have either, both or none of these. # If we have both, we don't need USERNAME, PASSWORD etc. # Fill in the blanks from the SecretsHelper if possible. # Finally, authenticate unless we have both. # Note if we don't auth we probably don't have a tenant ID so we can't # cache the token. auth_token = args.os_auth_token if args.os_auth_token else None management_url = bypass_url if bypass_url else None if os_auth_system and os_auth_system != "keystone": auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system) else: auth_plugin = None if not endpoint_type: endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE # This allow users to use endpoint_type as (internal, public or admin) # just like other openstack clients (glance, cinder etc) if endpoint_type in ['internal', 'public', 'admin']: endpoint_type += 'URL' if not service_type: # Note(alex_xu): We need discover version first, so if there isn't # service type specified, we use default nova service type. service_type = DEFAULT_NOVA_SERVICE_TYPE # If we have an auth token but no management_url, we must auth anyway. # Expired tokens are handled by client.py:_cs_request must_auth = not (auth_token and management_url) # Do not use Keystone session for cases with no session support. The # presence of auth_plugin means os_auth_system is present and is not # keystone. use_session = True if auth_plugin or bypass_url or os_cache or volume_service_name: use_session = False # FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if must_auth and not do_help: if auth_plugin: auth_plugin.parse_opts(args) if not auth_plugin or not auth_plugin.opts: if not os_username and not os_user_id: raise exc.CommandError( _("You must provide a username " "or user id via --os-username, --os-user-id, " "env[OS_USERNAME] or env[OS_USER_ID]")) if not any([args.os_tenant_name, args.os_tenant_id, args.os_project_id, args.os_project_name]): raise exc.CommandError(_("You must provide a project name or" " project id via --os-project-name," " --os-project-id, env[OS_PROJECT_ID]" " or env[OS_PROJECT_NAME]. You may" " use os-project and os-tenant" " interchangeably.")) if not os_auth_url: if os_auth_system and os_auth_system != 'keystone': os_auth_url = auth_plugin.get_auth_url() if not os_auth_url: raise exc.CommandError( _("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL] " "or specify an auth_system which defines a " "default url with --os-auth-system " "or env[OS_AUTH_SYSTEM]")) project_id = args.os_project_id or args.os_tenant_id project_name = args.os_project_name or args.os_tenant_name if use_session: # Not using Nova auth plugin, so use keystone with utils.record_time(self.times, args.timings, 'auth_url', args.os_auth_url): keystone_session = (ksession.Session .load_from_cli_options(args)) keystone_auth = self._get_keystone_auth( keystone_session, args.os_auth_url, username=args.os_username, user_id=args.os_user_id, user_domain_id=args.os_user_domain_id, user_domain_name=args.os_user_domain_name, password=args.os_password, auth_token=args.os_auth_token, project_id=project_id, project_name=project_name, project_domain_id=args.os_project_domain_id, project_domain_name=args.os_project_domain_name) if not do_help and not any([args.os_tenant_id, args.os_tenant_name, args.os_project_id, args.os_project_name]): raise exc.CommandError(_("You must provide a project name or" " project id via --os-project-name," " --os-project-id, env[OS_PROJECT_ID]" " or env[OS_PROJECT_NAME]. You may" " use os-project and os-tenant" " interchangeably.")) if not os_auth_url and not do_help: raise exc.CommandError( _("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL]")) # This client is just used to discover api version. Version API needn't # microversion, so we just pass version 2 at here. #tomograph.annotate("Discover api version", "novaclient-shell") self.cs = client.Client( api_versions.APIVersion("2.0"), os_username, os_password, os_tenant_name, tenant_id=os_tenant_id, user_id=os_user_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_system=os_auth_system, auth_plugin=auth_plugin, auth_token=auth_token, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=args.debug, cacert=cacert, timeout=timeout, session=keystone_session, auth=keystone_auth) if not do_help: if not api_version.is_latest(): if api_version > api_versions.APIVersion("2.0"): if not api_version.matches(novaclient.API_MIN_VERSION, novaclient.API_MAX_VERSION): raise exc.CommandError( _("The specified version isn't supported by " "client. The valid version range is '%(min)s' " "to '%(max)s'") % { "min": novaclient.API_MIN_VERSION.get_string(), "max": novaclient.API_MAX_VERSION.get_string()} ) api_version = api_versions.discover_version(self.cs, api_version) # build available subcommands based on version self.extensions = client.discover_extensions(api_version) self._run_extension_hooks('__pre_parse_args__') subcommand_parser = self.get_subcommand_parser( api_version, do_help=do_help) self.parser = subcommand_parser if args.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) self._run_extension_hooks('__post_parse_args__', args) # Short-circuit and deal with help right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if not args.service_type: service_type = (cliutils.get_service_type(args.func) or DEFAULT_NOVA_SERVICE_TYPE) if cliutils.isunauthenticated(args.func): # NOTE(alex_xu): We need authentication for discover microversion. # But the subcommands may needn't it. If the subcommand needn't, # we clear the session arguements. keystone_session = None keystone_auth = None # Recreate client object with discovered version. self.cs = client.Client( api_version, os_username, os_password, os_tenant_name, tenant_id=os_tenant_id, user_id=os_user_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_system=os_auth_system, auth_plugin=auth_plugin, auth_token=auth_token, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=args.debug, cacert=cacert, timeout=timeout, session=keystone_session, auth=keystone_auth) # Now check for the password/token of which pieces of the # identifying keyring key can come from the underlying client span_host = tomograph.getHost() span_name = ' '.join(sys.argv) # for arg in argv: # if arg[0] is not '-': # span_name = span_name + ' ' + arg tomograph.start("novaclient-shell", span_name, span_host, 0) if must_auth: helper = SecretsHelper(args, self.cs.client) self.cs.client.keyring_saver = helper if (auth_plugin and auth_plugin.opts and "os_password" not in auth_plugin.opts): use_pw = False else: use_pw = True tenant_id = helper.tenant_id # Allow commandline to override cache if not auth_token: auth_token = helper.auth_token if not management_url: management_url = helper.management_url if tenant_id and auth_token and management_url: self.cs.client.tenant_id = tenant_id self.cs.client.auth_token = auth_token self.cs.client.management_url = management_url self.cs.client.password_func = lambda: helper.password elif use_pw: # We're missing something, so auth with user/pass and save # the result in our helper. self.cs.client.password = helper.password try: # This does a couple of bits which are useful even if we've # got the token + service URL already. It exits fast in that case. if not cliutils.isunauthenticated(args.func): if not use_session: # Only call authenticate() if Nova auth plugin is used. # If keystone is used, authentication is handled as part # of session. self.cs.authenticate() except exc.Unauthorized: tomograph.tag("Exception", "Unauthorized") tomograph.stop(span_name) raise exc.CommandError(_("Invalid OpenStack Nova credentials.")) except exc.AuthorizationFailure: tomograph.tag("Exception", "AuthorizationFailure") tomograph.stop(span_name) raise exc.CommandError(_("Unable to authorize user")) args.func(self.cs, args) if args.timings: self._dump_timings(self.times + self.cs.get_timings()) tomograph.stop(span_name)
def _process_stack(self, request, action, action_args, content_type, body, accept): """Implement the processing stack.""" # Get the implementing method try: meth, extensions = self.get_method(request, action, content_type, body) except (AttributeError, TypeError): return Fault(webob.exc.HTTPNotFound()) except KeyError as ex: msg = _("There is no such action: %s") % ex.args[0] return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") return Fault(webob.exc.HTTPBadRequest(explanation=msg)) ser_name = "%s[%s]" % (meth.__module__, meth.__name__) tomograph.start_http(ser_name, meth.__name__, request) if body: msg = _("Action: '%(action)s', calling method: %(meth)s, body: " "%(body)s") % { 'action': action, 'body': six.text_type(body, 'utf-8'), 'meth': str(meth) } LOG.debug(strutils.mask_password(msg)) else: LOG.debug("Calling method '%(meth)s'", {'meth': str(meth)}) # Now, deserialize the request body... try: contents = {} if self._should_have_body(request): # allow empty body with PUT and POST if request.content_length == 0: contents = {'body': None} else: contents = self.deserialize(meth, content_type, body) except exception.InvalidContentType: msg = _("Unsupported Content-Type") tomograph.tag("Exception", "Unsupported Content-Type") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exception.MalformedRequestBody: msg = _("Malformed request body") tomograph.tag("Exception", "Malformed request body") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Update the action args action_args.update(contents) project_id = action_args.pop("project_id", None) context = request.environ.get('nova.context') if (context and project_id and (project_id != context.project_id)): msg = _("Malformed request URL: URL's project_id '%(project_id)s'" " doesn't match Context's project_id" " '%(context_project_id)s'") % \ {'project_id': project_id, 'context_project_id': context.project_id} tomograph.tag("Exception", "HTTPBadRequest") tomograph.stop(meth.__name__) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) # Run pre-processing extensions response, post = self.pre_process_extensions(extensions, request, action_args) if not response: try: with ResourceExceptionHandler(): action_result = self.dispatch(meth, request, action_args) except Fault as ex: response = ex if not response: # No exceptions; convert action_result into a # ResponseObject resp_obj = None if type(action_result) is dict or action_result is None: resp_obj = ResponseObject(action_result) elif isinstance(action_result, ResponseObject): resp_obj = action_result else: response = action_result # Run post-processing extensions if resp_obj: # Do a preserialize to set up the response object serializers = getattr(meth, 'wsgi_serializers', {}) resp_obj._bind_method_serializers(serializers) if hasattr(meth, 'wsgi_code'): resp_obj._default_code = meth.wsgi_code resp_obj.preserialize(accept, self.default_serializers) # Process post-processing extensions response = self.post_process_extensions( post, resp_obj, request, action_args) if resp_obj and not response: response = resp_obj.serialize(request, accept, self.default_serializers) if hasattr(response, 'headers'): for hdr, val in response.headers.items(): # Headers must be utf-8 strings response.headers[hdr] = utils.utf8(str(val)) if not request.api_version_request.is_null(): response.headers[API_VERSION_REQUEST_HEADER] = \ request.api_version_request.get_string() response.headers['Vary'] = API_VERSION_REQUEST_HEADER tomograph.stop(meth.__name__) return response
def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' (args, args_list) = parser.parse_known_args(argv) self.setup_debugging(args.debug) self.extensions = [] do_help = ('help' in argv) or not argv # Discover available auth plugins #tomograph.annotate("Discover available auth plugins", "novaclient-shell") novaclient.auth_plugin.discover_auth_systems() if not args.os_compute_api_version: api_version = api_versions.get_api_version( DEFAULT_MAJOR_OS_COMPUTE_API_VERSION) else: api_version = api_versions.get_api_version( args.os_compute_api_version) os_username = args.os_username os_user_id = args.os_user_id os_password = None # Fetched and set later as needed os_tenant_name = args.os_tenant_name os_tenant_id = args.os_tenant_id os_auth_url = args.os_auth_url os_region_name = args.os_region_name os_auth_system = args.os_auth_system endpoint_type = args.endpoint_type insecure = args.insecure service_type = args.service_type service_name = args.service_name volume_service_name = args.volume_service_name bypass_url = args.bypass_url os_cache = args.os_cache cacert = args.os_cacert timeout = args.timeout keystone_session = None keystone_auth = None # We may have either, both or none of these. # If we have both, we don't need USERNAME, PASSWORD etc. # Fill in the blanks from the SecretsHelper if possible. # Finally, authenticate unless we have both. # Note if we don't auth we probably don't have a tenant ID so we can't # cache the token. auth_token = args.os_auth_token if args.os_auth_token else None management_url = bypass_url if bypass_url else None if os_auth_system and os_auth_system != "keystone": auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system) else: auth_plugin = None if not endpoint_type: endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE # This allow users to use endpoint_type as (internal, public or admin) # just like other openstack clients (glance, cinder etc) if endpoint_type in ['internal', 'public', 'admin']: endpoint_type += 'URL' if not service_type: # Note(alex_xu): We need discover version first, so if there isn't # service type specified, we use default nova service type. service_type = DEFAULT_NOVA_SERVICE_TYPE # If we have an auth token but no management_url, we must auth anyway. # Expired tokens are handled by client.py:_cs_request must_auth = not (auth_token and management_url) # Do not use Keystone session for cases with no session support. The # presence of auth_plugin means os_auth_system is present and is not # keystone. use_session = True if auth_plugin or bypass_url or os_cache or volume_service_name: use_session = False # FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if must_auth and not do_help: if auth_plugin: auth_plugin.parse_opts(args) if not auth_plugin or not auth_plugin.opts: if not os_username and not os_user_id: raise exc.CommandError( _("You must provide a username " "or user id via --os-username, --os-user-id, " "env[OS_USERNAME] or env[OS_USER_ID]")) if not any([ args.os_tenant_name, args.os_tenant_id, args.os_project_id, args.os_project_name ]): raise exc.CommandError( _("You must provide a project name or" " project id via --os-project-name," " --os-project-id, env[OS_PROJECT_ID]" " or env[OS_PROJECT_NAME]. You may" " use os-project and os-tenant" " interchangeably.")) if not os_auth_url: if os_auth_system and os_auth_system != 'keystone': os_auth_url = auth_plugin.get_auth_url() if not os_auth_url: raise exc.CommandError( _("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL] " "or specify an auth_system which defines a " "default url with --os-auth-system " "or env[OS_AUTH_SYSTEM]")) project_id = args.os_project_id or args.os_tenant_id project_name = args.os_project_name or args.os_tenant_name if use_session: # Not using Nova auth plugin, so use keystone with utils.record_time(self.times, args.timings, 'auth_url', args.os_auth_url): keystone_session = ( ksession.Session.load_from_cli_options(args)) keystone_auth = self._get_keystone_auth( keystone_session, args.os_auth_url, username=args.os_username, user_id=args.os_user_id, user_domain_id=args.os_user_domain_id, user_domain_name=args.os_user_domain_name, password=args.os_password, auth_token=args.os_auth_token, project_id=project_id, project_name=project_name, project_domain_id=args.os_project_domain_id, project_domain_name=args.os_project_domain_name) if not do_help and not any([ args.os_tenant_id, args.os_tenant_name, args.os_project_id, args.os_project_name ]): raise exc.CommandError( _("You must provide a project name or" " project id via --os-project-name," " --os-project-id, env[OS_PROJECT_ID]" " or env[OS_PROJECT_NAME]. You may" " use os-project and os-tenant" " interchangeably.")) if not os_auth_url and not do_help: raise exc.CommandError( _("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL]")) # This client is just used to discover api version. Version API needn't # microversion, so we just pass version 2 at here. #tomograph.annotate("Discover api version", "novaclient-shell") self.cs = client.Client(api_versions.APIVersion("2.0"), os_username, os_password, os_tenant_name, tenant_id=os_tenant_id, user_id=os_user_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_system=os_auth_system, auth_plugin=auth_plugin, auth_token=auth_token, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=args.debug, cacert=cacert, timeout=timeout, session=keystone_session, auth=keystone_auth) if not do_help: if not api_version.is_latest(): if api_version > api_versions.APIVersion("2.0"): if not api_version.matches(novaclient.API_MIN_VERSION, novaclient.API_MAX_VERSION): raise exc.CommandError( _("The specified version isn't supported by " "client. The valid version range is '%(min)s' " "to '%(max)s'") % { "min": novaclient.API_MIN_VERSION.get_string(), "max": novaclient.API_MAX_VERSION.get_string() }) api_version = api_versions.discover_version(self.cs, api_version) # build available subcommands based on version self.extensions = client.discover_extensions(api_version) self._run_extension_hooks('__pre_parse_args__') subcommand_parser = self.get_subcommand_parser(api_version, do_help=do_help) self.parser = subcommand_parser if args.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) self._run_extension_hooks('__post_parse_args__', args) # Short-circuit and deal with help right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if not args.service_type: service_type = (cliutils.get_service_type(args.func) or DEFAULT_NOVA_SERVICE_TYPE) if cliutils.isunauthenticated(args.func): # NOTE(alex_xu): We need authentication for discover microversion. # But the subcommands may needn't it. If the subcommand needn't, # we clear the session arguements. keystone_session = None keystone_auth = None # Recreate client object with discovered version. self.cs = client.Client(api_version, os_username, os_password, os_tenant_name, tenant_id=os_tenant_id, user_id=os_user_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_system=os_auth_system, auth_plugin=auth_plugin, auth_token=auth_token, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=args.debug, cacert=cacert, timeout=timeout, session=keystone_session, auth=keystone_auth) # Now check for the password/token of which pieces of the # identifying keyring key can come from the underlying client span_host = tomograph.getHost() span_name = ' '.join(sys.argv) # for arg in argv: # if arg[0] is not '-': # span_name = span_name + ' ' + arg tomograph.start("novaclient-shell", span_name, span_host, 0) if must_auth: helper = SecretsHelper(args, self.cs.client) self.cs.client.keyring_saver = helper if (auth_plugin and auth_plugin.opts and "os_password" not in auth_plugin.opts): use_pw = False else: use_pw = True tenant_id = helper.tenant_id # Allow commandline to override cache if not auth_token: auth_token = helper.auth_token if not management_url: management_url = helper.management_url if tenant_id and auth_token and management_url: self.cs.client.tenant_id = tenant_id self.cs.client.auth_token = auth_token self.cs.client.management_url = management_url self.cs.client.password_func = lambda: helper.password elif use_pw: # We're missing something, so auth with user/pass and save # the result in our helper. self.cs.client.password = helper.password try: # This does a couple of bits which are useful even if we've # got the token + service URL already. It exits fast in that case. if not cliutils.isunauthenticated(args.func): if not use_session: # Only call authenticate() if Nova auth plugin is used. # If keystone is used, authentication is handled as part # of session. self.cs.authenticate() except exc.Unauthorized: tomograph.tag("Exception", "Unauthorized") tomograph.stop(span_name) raise exc.CommandError(_("Invalid OpenStack Nova credentials.")) except exc.AuthorizationFailure: tomograph.tag("Exception", "AuthorizationFailure") tomograph.stop(span_name) raise exc.CommandError(_("Unable to authorize user")) args.func(self.cs, args) if args.timings: self._dump_timings(self.times + self.cs.get_timings()) tomograph.stop(span_name)
def __call__(self, req): arg_dict = req.environ['wsgiorg.routing_args'][1] action = arg_dict.pop('action') del arg_dict['controller'] # allow middleware up the stack to provide context, params and headers. context = req.environ.get(CONTEXT_ENV, {}) context['query_string'] = dict(req.params.items()) context['headers'] = dict(req.headers.items()) context['path'] = req.environ['PATH_INFO'] scheme = (None if not CONF.secure_proxy_ssl_header else req.environ.get(CONF.secure_proxy_ssl_header)) if scheme: # NOTE(andrey-mp): "wsgi.url_scheme" contains the protocol used # before the proxy removed it ('https' usually). So if # the webob.Request instance is modified in order to use this # scheme instead of the one defined by API, the call to # webob.Request.relative_url() will return a URL with the correct # scheme. req.environ['wsgi.url_scheme'] = scheme context['host_url'] = req.host_url params = req.environ.get(PARAMS_ENV, {}) # authentication and authorization attributes are set as environment # values by the container and processed by the pipeline. the complete # set is not yet know. context['environment'] = req.environ context['accept_header'] = req.accept req.environ = None params.update(arg_dict) context.setdefault('is_admin', False) # TODO(termie): do some basic normalization on methods method = getattr(self, action) # NOTE(morganfainberg): use the request method to normalize the # response code between GET and HEAD requests. The HTTP status should # be the same. LOG.info('%(req_method)s %(uri)s', { 'req_method': req.environ['REQUEST_METHOD'].upper(), 'uri': wsgiref.util.request_uri(req.environ), }) params = self._normalize_dict(params) try: ser_name = "%s[%s]" % (method.__module__, method.__name__) tomograph.start_http(ser_name, method.__name__, req) result = method(context, **params) tomograph.stop(method.__name__) except exception.Unauthorized as e: LOG.warning( _LW("Authorization failed. %(exception)s from " "%(remote_addr)s"), {'exception': e, 'remote_addr': req.environ['REMOTE_ADDR']}) return render_exception(e, context=context, user_locale=best_match_language(req)) except exception.Error as e: LOG.warning(six.text_type(e)) return render_exception(e, context=context, user_locale=best_match_language(req)) except TypeError as e: LOG.exception(six.text_type(e)) return render_exception(exception.ValidationError(e), context=context, user_locale=best_match_language(req)) except Exception as e: LOG.exception(six.text_type(e)) return render_exception(exception.UnexpectedError(exception=e), context=context, user_locale=best_match_language(req)) if result is None: return render_response(status=(204, 'No Content')) elif isinstance(result, six.string_types): return result elif isinstance(result, webob.Response): return result elif isinstance(result, webob.exc.WSGIHTTPException): return result response_code = self._get_response_code(req) return render_response(body=result, status=response_code, method=req.environ['REQUEST_METHOD'])