def _from_json(self, datastring): try: return jsonutils.loads(datastring) except ValueError: msg = _("Cannot understand JSON") LOG.exception(_("InvalidJSON: %s"), msg) raise exceptions.MalformedRequestBody(reason=msg)
def run(self, parsed_args): self.log.debug('run(%s)', parsed_args) hypernet_client = self.get_client() hypernet_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) _merge_args(self, parsed_args, _extra_values, self.values_specs) body = self.args2body(parsed_args) if self.resource in body: body[self.resource].update(_extra_values) else: body[self.resource] = _extra_values if not body[self.resource]: raise exceptions.CommandError( _("Must specify new values to update %s") % self.resource) if self.allow_names: _id = find_resourceid_by_name_or_id(hypernet_client, self.resource, parsed_args.id) else: _id = find_resourceid_by_id(hypernet_client, self.resource, parsed_args.id) obj_updator = getattr(hypernet_client, "update_%s" % self.resource) obj_updator(_id, body) print((_('Updated %(resource)s: %(id)s') % { 'id': parsed_args.id, 'resource': self.resource }), file=self.app.stdout) return
def _load_all_extensions_from_path(self, path): # Sorting the extension list makes the order in which they # are loaded predictable across a cluster of load-balanced # Hypernet Servers for f in sorted(os.listdir(path)): try: LOG.debug(_('Loading extension file: %s'), f) mod_name, file_ext = os.path.splitext(os.path.split(f)[-1]) ext_path = os.path.join(path, f) if file_ext.lower() == '.py' and not mod_name.startswith('_'): mod = imp.load_source(mod_name, ext_path) ext_name = mod_name[0].upper() + mod_name[1:] new_ext_class = getattr(mod, ext_name, None) if not new_ext_class: LOG.warning(_('Did not find expected name ' '"%(ext_name)s" in %(file)s'), {'ext_name': ext_name, 'file': ext_path}) continue new_ext = new_ext_class() self.add_extension(new_ext) except Exception as exception: LOG.warning(_("Extension file %(f)s wasn't loaded due to " "%(exception)s"), {'f': f, 'exception': exception})
def retry_request(self, method, action, body=None, headers=None, params=None): """Call do_request with the default retry configuration. Only idempotent requests should retry failed connection attempts. :raises: ConnectionFailed if the maximum # of retries is exceeded """ max_attempts = self.retries + 1 for i in range(max_attempts): try: return self.do_request(method, action, body=body, headers=headers, params=params) except exceptions.ConnectionFailed: # Exception has already been logged by do_request() if i < self.retries: _logger.debug('Retrying connection to Tacker service') time.sleep(self.retry_interval) elif self.raise_errors: raise if self.retries: msg = (_("Failed to connect to Tacker server after %d attempts") % max_attempts) else: msg = _("Failed to connect Tacker server") raise exceptions.ConnectionFailed(reason=msg)
def check_non_negative_int(value): try: value = int(value) except ValueError: raise argparse.ArgumentTypeError(_("invalid int value: %r") % value) if value < 0: raise argparse.ArgumentTypeError( _("input value %d is negative") % value) return value
def get_parser(self, prog_name): parser = super(DeleteCommand, self).get_parser(prog_name) if self.allow_names: help_str = _('IDs or names of %s to delete') else: help_str = _('IDs of %s to delete') parser.add_argument('ids', nargs='+', metavar=self.resource.upper(), help=help_str % self.resource) return parser
def get_parser(self, prog_name): parser = super(ShowCommand, self).get_parser(prog_name) add_show_list_common_argument(parser) if self.allow_names: help_str = _('ID or name of %s to look up') else: help_str = _('ID of %s to look up') parser.add_argument('id', metavar=self.get_id(), help=help_str % self.resource) return parser
def validate_head_file(config): script = alembic_script.ScriptDirectory.from_config(config) if len(script.get_heads()) > 1: alembic_util.err(_('Timeline branches unable to generate timeline')) head_path = os.path.join(script.versions, HEAD_FILENAME) if (os.path.isfile(head_path) and open(head_path).read().strip() == script.get_current_head()): return else: alembic_util.err(_('HEAD file does not match migration timeline head'))
def build_option_parser(self, description, version): """Return an argparse option parser for this application. Subclasses may override this method to extend the parser with more global options. :param description: full description of the application :paramtype description: str :param version: version number for the application :paramtype version: str """ parser = argparse.ArgumentParser( description=description, add_help=False, ) parser.add_argument( '--version', action='version', version=__version__, ) parser.add_argument( '-v', '--verbose', '--debug', action='count', dest='verbose_level', default=self.DEFAULT_VERBOSE_LEVEL, help=_('Increase verbosity of output and show tracebacks on' ' errors. You can repeat this option.')) parser.add_argument( '-q', '--quiet', action='store_const', dest='verbose_level', const=0, help=_('Suppress output except warnings and errors.')) parser.add_argument( '-h', '--help', action=HelpAction, nargs=0, default=self, # tricky help=_("Show this help message and exit.")) parser.add_argument( '-r', '--retries', metavar="NUM", type=check_non_negative_int, default=0, help=_("How many times the request to the Hypernet server should " "be retried if it fails.")) # FIXME(bklei): this method should come from python-keystoneclient self._append_global_identity_args(parser) return parser
def __init__(self, application, ext_mgr=None): self.ext_mgr = (ext_mgr or ExtensionManager(get_extensions_path())) mapper = routes.Mapper() # extended resources for resource in self.ext_mgr.get_resources(): path_prefix = resource.path_prefix if resource.parent: path_prefix = (resource.path_prefix + "/%s/{%s_id}" % (resource.parent["collection_name"], resource.parent["member_name"])) LOG.debug(_('Extended resource: %s'), resource.collection) for action, method in six.iteritems(resource.collection_actions): conditions = dict(method=[method]) path = "/%s/%s" % (resource.collection, action) with mapper.submapper(controller=resource.controller, action=action, path_prefix=path_prefix, conditions=conditions) as submap: submap.connect(path) submap.connect("%s.:(format)" % path) mapper.resource(resource.collection, resource.collection, controller=resource.controller, member=resource.member_actions, parent_resource=resource.parent, path_prefix=path_prefix) # extended actions action_controllers = self._action_ext_controllers(application, self.ext_mgr, mapper) for action in self.ext_mgr.get_actions(): LOG.debug(_('Extended action: %s'), action.action_name) controller = action_controllers[action.collection] controller.add_action(action.action_name, action.handler) # extended requests req_controllers = self._request_ext_controllers(application, self.ext_mgr, mapper) for request_ext in self.ext_mgr.get_request_extensions(): LOG.debug(_('Extended request: %s'), request_ext.key) controller = req_controllers[request_ext.key] controller.add_handler(request_ext.handler) self._router = routes.middleware.RoutesMiddleware(self._dispatch, mapper) super(ExtensionMiddleware, self).__init__(application)
def extend_resources(self, version, attr_map): """Extend resources with additional resources or attributes. :param: attr_map, the existing mapping from resource name to attrs definition. After this function, we will extend the attr_map if an extension wants to extend this map. """ update_exts = [] processed_exts = set() exts_to_process = self.extensions.copy() # Iterate until there are unprocessed extensions or if no progress # is made in a whole iteration while exts_to_process: processed_ext_count = len(processed_exts) for ext_name, ext in exts_to_process.items(): if not hasattr(ext, 'get_extended_resources'): del exts_to_process[ext_name] continue if hasattr(ext, 'update_attributes_map'): update_exts.append(ext) if hasattr(ext, 'get_required_extensions'): # Process extension only if all required extensions # have been processed already required_exts_set = set(ext.get_required_extensions()) if required_exts_set - processed_exts: continue try: extended_attrs = ext.get_extended_resources(version) for resource, resource_attrs in six.iteritems( extended_attrs): if attr_map.get(resource): attr_map[resource].update(resource_attrs) else: attr_map[resource] = resource_attrs except AttributeError: LOG.exception(_("Error fetching extended attributes for " "extension '%s'"), ext.get_name()) processed_exts.add(ext_name) del exts_to_process[ext_name] if len(processed_exts) == processed_ext_count: # Exit loop as no progress was made break if exts_to_process: # NOTE(salv-orlando): Consider whether this error should be fatal LOG.error(_("It was impossible to process the following " "extensions: %s because of missing requirements."), ','.join(exts_to_process.keys())) # Extending extensions' attributes map. for ext in update_exts: ext.update_attributes_map(attr_map)
def show(self, request, id): # NOTE(dprince): the extensions alias is used as the 'id' for show ext = self.extension_manager.extensions.get(id) if not ext: raise webob.exc.HTTPNotFound( _("Extension with alias %s does not exist") % id) return dict(extension=self._translate(ext))
def __call__(self, req): """Call the method specified in req.environ by RoutesMiddleware.""" arg_dict = req.environ['wsgiorg.routing_args'][1] action = arg_dict['action'] method = getattr(self, action) del arg_dict['controller'] del arg_dict['action'] if 'format' in arg_dict: del arg_dict['format'] arg_dict['request'] = req result = method(**arg_dict) if isinstance(result, dict) or result is None: if result is None: status = 204 content_type = '' body = None else: status = 200 content_type = req.best_match_content_type() body = self._serialize(result, content_type) response = webob.Response(status=status, content_type=content_type, body=body) msg_dict = dict(url=req.url, status=response.status_int) msg = _("%(url)s returned with HTTP %(status)d") % msg_dict LOG.debug(msg) return response else: return result
def __call__(self, environ, start_response): r"""Subclasses will probably want to implement __call__ like this: @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): # Any of the following objects work as responses: # Option 1: simple string res = 'message\n' # Option 2: a nicely formatted HTTP exception page res = exc.HTTPForbidden(explanation='Nice try') # Option 3: a webob Response object (in case you need to play with # headers, or you want to be treated like an iterable, or or or) res = Response(); res.app_iter = open('somefile') # Option 4: any wsgi app to be run next res = self.application # Option 5: you can get a Response object for a wsgi app, too, to # play with headers etc res = req.get_response(self.application) # You can then just return your response... return res # ... or set req.response and return None. req.response = res See the end of http://pythonpaste.org/webob/modules/dec.html for more info. """ raise NotImplementedError(_('You must implement __call__'))
def make_client(instance): """Returns an hypernet client.""" hypernet_client = utils.get_client_class( API_NAME, instance._api_version[API_NAME], API_VERSIONS, ) instance.initialize() url = instance._url url = url.rstrip("/") if '1.0' == instance._api_version[API_NAME]: client = hypernet_client(username=instance._username, tenant_name=instance._tenant_name, password=instance._password, region_name=instance._region_name, auth_url=instance._auth_url, endpoint_url=url, endpoint_type=instance._endpoint_type, token=instance._token, auth_strategy=instance._auth_strategy, insecure=instance._insecure, ca_cert=instance._ca_cert, retries=instance._retries, raise_errors=instance._raise_errors, session=instance._session, auth=instance._auth) return client else: raise exceptions.UnsupportedVersion(_("API version %s is not " "supported") % instance._api_version[API_NAME])
class InvalidInput(BadRequest): """A bad request due to invalid input. A specialization of the BadRequest error indicating bad input was specified. :param error_message: Details on the operation that failed due to bad input. """ message = _("Invalid input for operation: %(error_message)s.")
def get_parser(self, prog_name): parser = super(UpdateCommand, self).get_parser(prog_name) parser.add_argument('id', metavar=self.resource.upper(), help=_('ID or name of %s to update') % self.resource) self.add_known_arguments(parser) return parser
def update_head_file(config): script = alembic_script.ScriptDirectory.from_config(config) if len(script.get_heads()) > 1: alembic_util.err(_('Timeline branches unable to generate timeline')) head_path = os.path.join(script.versions, HEAD_FILENAME) with open(head_path, 'w+') as f: f.write(script.get_current_head())
def authenticate(self): if self.auth_strategy == 'keystone': self._authenticate_keystone() elif self.auth_strategy == 'noauth': self._authenticate_noauth() else: err_msg = _('Unknown auth strategy: %s') % self.auth_strategy raise exceptions.Unauthorized(message=err_msg)
def deserialize(self, datastring, content_type): """Deserialize a string to a dictionary. The string must be in the format of a supported MIME type. """ try: return self.get_deserialize_handler(content_type).deserialize( datastring) except Exception: raise webob.exc.HTTPBadRequest(_("Could not deserialize data"))
def get_content_type(self): allowed_types = ("application/json") if "Content-Type" not in self.headers: LOG.debug(_("Missing Content-Type")) return None _type = self.content_type if _type in allowed_types: return _type return None
def run(self, parsed_args): failure = False deleted_ids = [] failed_items = {} hypernet_client = self.get_client() hypernet_client.format = parsed_args.request_format obj_deleter = getattr(hypernet_client, "delete_%s" % self.resource) for resource_id in parsed_args.ids: try: if self.allow_names: _id = find_resourceid_by_name_or_id( hypernet_client, self.resource, resource_id) else: _id = resource_id obj_deleter(_id) deleted_ids.append(resource_id) except Exception as e: failure = True failed_items[resource_id] = e if failure: msg = '' if deleted_ids: status_msg = self.deleted_msg.get(self.resource, 'deleted') msg = (_('Successfully %(status_msg)s %(resource)s(s):' ' %(deleted_list)s') % { 'status_msg': status_msg, 'deleted_list': ', '.join(deleted_ids), 'resource': self.resource }) err_msg = _("\n\nUnable to delete the below" " %s(s):") % self.resource for failed_id, error in failed_items.iteritems(): err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s') % { 'failed_id': failed_id, 'error': error }) msg += err_msg raise exceptions.CommandError(msg) else: print((_('All %(resource)s(s) %(msg)s successfully') % { 'msg': self.deleted_msg.get(self.resource, 'deleted'), 'resource': self.resource })) return
def _run_wsgi(app_name): app = config.load_paste_app(app_name) if not app: LOG.error(_('No known API applications configured.')) return server = wsgi.Server(cfg.CONF, app_name, app, host=cfg.CONF.bind_host, port=cfg.CONF.bind_port, use_ssl=sslutils.is_enabled(cfg.CONF)) server.start() # Dump all option values here after all options are parsed cfg.CONF.log_opt_values(LOG, std_logging.DEBUG) LOG.info(_("Hypernet service started, listening on %(host)s:%(port)s"), { 'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port }) return server
def add_pagination_argument(parser): parser.add_argument( '-P', '--page-size', dest='page_size', metavar='SIZE', type=int, help=_("Specify retrieve unit of each request, then split one request " "to several requests"), default=None)
def get_parser(self, prog_name): parser = super(CreateCommand, self).get_parser(prog_name) parser.add_argument( '--tenant-id', metavar='TENANT_ID', help=_('The owner tenant ID'), ) parser.add_argument('--tenant_id', help=argparse.SUPPRESS) self.add_known_arguments(parser) return parser
def serve_wsgi(cls): try: service = cls.create() except Exception: with excutils.save_and_reraise_exception(): LOG.exception( _('Unrecoverable error: please check log ' 'for details.')) return service
def add_extension(self, ext): # Do nothing if the extension doesn't check out if not self._check_extension(ext): return alias = ext.get_alias() LOG.info(_('Loaded extension: %s'), alias) if alias in self.extensions: raise exceptions.DuplicatedExtension(alias=alias) self.extensions[alias] = ext
def __call__(self, request): """WSGI method that controls (de)serialization and method dispatch.""" LOG.info(_("%(method)s %(url)s"), {"method": request.method, "url": request.url}) try: action, args, accept = self.deserializer.deserialize(request) except exceptions.InvalidContentType: msg = _("Unsupported Content-Type") LOG.exception(_("InvalidContentType: %s"), msg) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except exceptions.MalformedRequestBody: msg = _("Malformed request body") LOG.exception(_("MalformedRequestBody: %s"), msg) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) try: action_result = self.dispatch(request, action, args) except webob.exc.HTTPException as ex: LOG.info(_("HTTP exception thrown: %s"), six.text_type(ex)) action_result = Fault(ex, self._fault_body_function) except Exception: LOG.exception(_("Internal error")) # Do not include the traceback to avoid returning it to clients. action_result = Fault(webob.exc.HTTPServerError(), self._fault_body_function) if isinstance(action_result, dict) or action_result is None: response = self.serializer.serialize(action_result, accept, action=action) else: response = action_result try: msg_dict = dict(url=request.url, status=response.status_int) msg = _("%(url)s returned with HTTP %(status)d") % msg_dict except AttributeError as e: msg_dict = dict(url=request.url, exception=e) msg = _("%(url)s returned a fault: %(exception)s") % msg_dict LOG.info(msg) return response
def add_sorting_argument(parser): parser.add_argument( '--sort-key', dest='sort_key', metavar='FIELD', action='append', help=_("Sorts the list by the specified fields in the specified " "directions. You can repeat this option, but you must " "specify an equal number of sort_dir and sort_key values. " "Extra sort_dir options are ignored. Missing sort_dir options " "use the default asc value."), default=[]) parser.add_argument( '--sort-dir', dest='sort_dir', metavar='{asc,desc}', help=_("Sorts the list in the specified direction. You can repeat " "this option."), action='append', default=[], choices=['asc', 'desc'])
def do_upgrade_downgrade(config, cmd): if not CONF.command.revision and not CONF.command.delta: raise SystemExit(_('You must provide a revision or relative delta')) revision = CONF.command.revision if CONF.command.delta: sign = '+' if CONF.command.name == 'upgrade' else '-' revision = sign + str(CONF.command.delta) else: revision = CONF.command.revision do_alembic_command(config, cmd, revision, sql=CONF.command.sql)