def client_request(self, client, method, url, **kwargs): """Send an http request using `client`'s endpoint and specified `url`. If request was rejected as unauthorized (possibly because the token is expired), issue one authorization attempt and send the request once again. :param client: instance of BaseClient descendant :param method: method of HTTP request :param url: URL of HTTP request :param kwargs: any other parameter that can be passed to `HTTPClient.request` """ filter_args = { "endpoint_type": client.endpoint_type or self.endpoint_type, "service_type": client.service_type, } token, endpoint = (self.cached_token, client.cached_endpoint) just_authenticated = False if not (token and endpoint): try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: pass if not (token and endpoint): self.authenticate() just_authenticated = True token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) if not (token and endpoint): raise exceptions.AuthorizationFailure( _("Cannot find endpoint or token for request")) old_token_endpoint = (token, endpoint) kwargs.setdefault("headers", {})["X-Auth-Token"] = token self.cached_token = token client.cached_endpoint = endpoint # Perform the request once. If we get Unauthorized, then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: return self.request(method, self.concat_url(endpoint, url), **kwargs) except exceptions.Unauthorized as unauth_ex: if just_authenticated: raise self.cached_token = None client.cached_endpoint = None if self.auth_plugin.opts.get('token'): self.auth_plugin.opts['token'] = None if self.auth_plugin.opts.get('endpoint'): self.auth_plugin.opts['endpoint'] = None self.authenticate() try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: raise unauth_ex if (not (token and endpoint) or old_token_endpoint == (token, endpoint)): raise unauth_ex self.cached_token = token client.cached_endpoint = endpoint kwargs["headers"]["X-Auth-Token"] = token return self.request(method, self.concat_url(endpoint, url), **kwargs)
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) heat_client = self.app.client_manager.orchestration try: if not parsed_args.yes and sys.stdin.isatty(): sys.stdout.write( _("Are you sure you want to delete this stack(s) [y/N]? ")) prompt_response = sys.stdin.readline().lower() if not prompt_response.startswith('y'): self.log.info(_LI('User did not confirm stack delete so ' 'taking no action.')) return except KeyboardInterrupt: # ctrl-c self.log.info(_LI('User did not confirm stack delete ' '(ctrl-c) so taking no action.')) return except EOFError: # ctrl-d self.log.info(_LI('User did not confirm stack delete ' '(ctrl-d) so taking no action.')) return failure_count = 0 stacks_waiting = [] for sid in parsed_args.stack: marker = None if parsed_args.wait: try: # find the last event to use as the marker events = event_utils.get_events(heat_client, stack_id=sid, event_args={ 'sort_dir': 'desc', 'limit': 1}) if events: marker = events[0].id except heat_exc.CommandError as ex: failure_count += 1 print(ex) continue try: heat_client.stacks.delete(sid) stacks_waiting.append((sid, marker)) except heat_exc.HTTPNotFound: failure_count += 1 print(_('Stack not found: %s') % sid) if parsed_args.wait: for sid, marker in stacks_waiting: try: stack_status, msg = event_utils.poll_for_events( heat_client, sid, action='DELETE', marker=marker) except heat_exc.CommandError: continue if stack_status == 'DELETE_FAILED': failure_count += 1 print(msg) if failure_count: msg = (_('Unable to delete %(count)d of the %(total)d stacks.') % {'count': failure_count, 'total': len(parsed_args.stack)}) raise exc.CommandError(msg)
def __init__(self, missing): self.missing = missing msg = _("Missing arguments: %s") % ", ".join(missing) super(MissingArgs, self).__init__(msg)
def _http_request(self, url, method, **kwargs): """Send an http request with the specified characteristics. Wrapper around requests.request to handle tasks such as setting headers and error handling. """ # Copy the kwargs so we can reuse the original in case of redirects kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'].setdefault('User-Agent', USER_AGENT) if self.auth_token: kwargs['headers'].setdefault('X-Auth-Token', self.auth_token) else: kwargs['headers'].update(self.credentials_headers()) if self.auth_url: kwargs['headers'].setdefault('X-Auth-Url', self.auth_url) if self.region_name: kwargs['headers'].setdefault('X-Region-Name', self.region_name) if self.include_pass and 'X-Auth-Key' not in kwargs['headers']: kwargs['headers'].update(self.credentials_headers()) if osprofiler_web: kwargs['headers'].update(osprofiler_web.get_trace_id_headers()) self.log_curl_request(method, url, kwargs) if self.cert_file and self.key_file: kwargs['cert'] = (self.cert_file, self.key_file) if self.verify_cert is not None: kwargs['verify'] = self.verify_cert if self.timeout is not None: kwargs['timeout'] = float(self.timeout) # Allow caller to specify not to follow redirects, in which case we # just return the redirect response. Useful for using stacks:lookup. redirect = kwargs.pop('redirect', True) # Since requests does not follow the RFC when doing redirection to sent # back the same method on a redirect we are simply bypassing it. For # example if we do a DELETE/POST/PUT on a URL and we get a 302 RFC says # that we should follow that URL with the same method as before, # requests doesn't follow that and send a GET instead for the method. # Hopefully this could be fixed as they say in a comment in a future # point version i.e.: 3.x # See issue: https://github.com/kennethreitz/requests/issues/1704 allow_redirects = False try: resp = requests.request(method, self.endpoint_url + url, allow_redirects=allow_redirects, **kwargs) except socket.gaierror as e: message = (_("Error finding address for %(url)s: %(e)s") % { 'url': self.endpoint_url + url, 'e': e }) raise exc.InvalidEndpoint(message=message) except (socket.error, socket.timeout) as e: endpoint = self.endpoint message = (_("Error communicating with %(endpoint)s %(e)s") % { 'endpoint': endpoint, 'e': e }) raise exc.CommunicationError(message=message) self.log_http_response(resp) if not ('X-Auth-Key' in kwargs['headers']) and ( resp.status_code == 401 or (resp.status_code == 500 and "(HTTP 401)" in resp.content)): raise exc.HTTPUnauthorized( _("Authentication failed: %s") % resp.content) elif 400 <= resp.status_code < 600: raise exc.from_response(resp) elif resp.status_code in (301, 302, 305): # Redirected. Reissue the request to the new location, # unless caller specified redirect=False if redirect: location = resp.headers.get('location') path = self.strip_endpoint(location) resp = self._http_request(path, method, **kwargs) elif resp.status_code == 300: raise exc.from_response(resp) return resp
def get_parser(self, prog_name): parser = super(CreateStack, self).get_parser(prog_name) parser.add_argument( '-t', '--template', metavar='<template>', required=True, help=_('Path to the template') ) parser.add_argument( '-e', '--environment', metavar='<environment>', action='append', help=_('Path to the environment. Can be specified multiple times') ) parser.add_argument( '--timeout', metavar='<timeout>', type=int, help=_('Stack creating timeout in minutes') ) # parser.add_argument( # '--pre-create', # metavar='<resource>', # default=None, # action='append', # help=_('Name of a resource to set a pre-create hook to. Resources ' # 'in nested stacks can be set using slash as a separator: ' # 'nested_stack/another/my_resource. You can use wildcards ' # 'to match multiple stacks or resources: ' # 'nested_stack/an*/*_resource. This can be specified ' # 'multiple times') # ) parser.add_argument( '--enable-rollback', action='store_true', help=_('Enable rollback on create/update failure') ) parser.add_argument( '--parameter', metavar='<key=value>', action='append', help=_('Parameter values used to create the stack. This can be ' 'specified multiple times') ) parser.add_argument( '--parameter-file', metavar='<key=file>', action='append', help=_('Parameter values from file used to create the stack. ' 'This can be specified multiple times. Parameter values ' 'would be the content of the file') ) parser.add_argument( '--wait', action='store_true', help=_('Wait until stack goes to CREATE_COMPLETE or CREATE_FAILED') ) parser.add_argument( '--tags', metavar='<tag1,tag2...>', help=_('A list of tags to associate with the stack') ) parser.add_argument( '--dry-run', action='store_true', help=_('Do not actually perform the stack create, but show what ' 'would be created') ) parser.add_argument( 'name', metavar='<stack-name>', help=_('Name of the stack to create') ) return parser
class HTTPRedirection(HttpError): """HTTP Redirection.""" message = _("HTTP Redirection")
def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( _("Authentication failed. Missing options: %s") % ", ".join(opt_names)) self.opt_names = opt_names
def get_parser(self, prog_name): return self._get_parser( prog_name, _('Stack(s) to resume (name or ID)'), _('Wait for resume to complete') )
def get_parser(self, prog_name): return self._get_parser( prog_name, _('Stack(s) to cancel (name or ID)'), _('Wait for check to complete') )
def main(self, argv): # Parse args once to find version parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self._setup_logging(options.debug) self._setup_verbose(options.verbose) # build available subcommands based on version api_version = options.heat_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 args and 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 if not args.os_username and not args.os_auth_token: raise exc.CommandError( _("You must provide a username via either " "--os-username or env[OS_USERNAME] " "or a token via --os-auth-token or " "env[OS_AUTH_TOKEN]")) if not args.os_password and not args.os_auth_token: raise exc.CommandError( _("You must provide a password via either " "--os-password or env[OS_PASSWORD] " "or a token via --os-auth-token or " "env[OS_AUTH_TOKEN]")) if args.os_no_client_auth: if not args.heat_url: raise exc.CommandError( _("If you specify --os-no-client-auth " "you must also specify a Heat API " "URL via either --heat-url or " "env[HEAT_URL]")) else: # Tenant/project name or ID is needed to make keystoneclient # retrieve a service catalog, it's not required if # os_no_client_auth is specified, neither is the auth URL if not (args.os_tenant_id or args.os_tenant_name or args.os_project_id or args.os_project_name): raise exc.CommandError( _("You must provide a tenant id via either " "--os-tenant-id or env[OS_TENANT_ID] or a tenant name " "via either --os-tenant-name or env[OS_TENANT_NAME] " "or a project id via either --os-project-id or " "env[OS_PROJECT_ID] or a project name via " "either --os-project-name or env[OS_PROJECT_NAME]")) if not args.os_auth_url: raise exc.CommandError( _("You must provide an auth url via " "either --os-auth-url or via " "env[OS_AUTH_URL]")) kwargs = { 'insecure': args.insecure, 'cacert': args.os_cacert, 'cert': args.os_cert, 'key': args.os_key, 'timeout': args.api_timeout } endpoint = args.heat_url service_type = args.os_service_type or 'orchestration' if args.os_no_client_auth: # Do not use session since no_client_auth means using heat to # to authenticate kwargs = { 'username': args.os_username, 'password': args.os_password, 'auth_url': args.os_auth_url, 'token': args.os_auth_token, 'include_pass': args.include_password, 'insecure': args.insecure, 'timeout': args.api_timeout } else: keystone_session = self._get_keystone_session(**kwargs) project_id = args.os_project_id or args.os_tenant_id project_name = args.os_project_name or args.os_tenant_name endpoint_type = args.os_endpoint_type or 'publicURL' kwargs = { '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, } keystone_auth = self._get_keystone_auth(keystone_session, args.os_auth_url, **kwargs) if not endpoint: svc_type = service_type region_name = args.os_region_name endpoint = keystone_auth.get_endpoint(keystone_session, service_type=svc_type, interface=endpoint_type, region_name=region_name) kwargs = { 'auth_url': args.os_auth_url, 'session': keystone_session, 'auth': keystone_auth, 'service_type': service_type, 'endpoint_type': endpoint_type, 'region_name': args.os_region_name, 'username': args.os_username, 'password': args.os_password, 'include_pass': args.include_password } client = heat_client.Client(api_version, endpoint, **kwargs) profile = osprofiler_profiler and options.profile if profile: osprofiler_profiler.init(options.profile) args.func(client, args) if profile: trace_id = osprofiler_profiler.get().get_base_id() print(_("Trace ID: %s") % trace_id) print( _("To display trace use next command:\n" "osprofiler trace show --html %s ") % trace_id)
def get_parser(self, prog_name): return self._get_parser( prog_name, _('Stack(s) to suspend (name or ID)'), _('Wait for suspend to complete') )
def _append_global_identity_args(self, parser): # FIXME(gyee): these are global identity (Keystone) arguments which # should be consistent and shared by all service clients. Therefore, # they should be provided by python-keystoneclient. We will need to # refactor this code once this functionality is available in # python-keystoneclient. parser.add_argument( '-k', '--insecure', default=False, action='store_true', help=_('Explicitly allow heatclient to perform ' '\"insecure SSL\" (https) requests. ' 'The server\'s certificate will not be verified ' 'against any certificate authorities. ' 'This option should be used with caution.')) parser.add_argument( '--os-cert', help=_('Path of certificate file to use in SSL connection. ' 'This file can optionally be prepended with ' 'the private key.')) # for backward compatibility only parser.add_argument('--cert-file', dest='os_cert', help=_('DEPRECATED! Use %(arg)s.') % {'arg': '--os-cert'}) parser.add_argument('--os-key', help=_('Path of client key to use in SSL ' 'connection. This option is not necessary ' 'if your key is prepended to your cert ' 'file.')) parser.add_argument('--key-file', dest='os_key', help=_('DEPRECATED! Use %(arg)s.') % {'arg': '--os-key'}) parser.add_argument('--os-cacert', metavar='<ca-certificate-file>', dest='os_cacert', default=utils.env('OS_CACERT'), help=_('Path of CA TLS certificate(s) used to ' 'verify the remote server\'s certificate. ' 'Without this option glance looks for the ' 'default system CA certificates.')) parser.add_argument('--ca-file', dest='os_cacert', help=_('DEPRECATED! Use %(arg)s.') % {'arg': '--os-cacert'}) parser.add_argument('--os-username', default=utils.env('OS_USERNAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USERNAME]'}) parser.add_argument('--os_username', help=argparse.SUPPRESS) parser.add_argument('--os-user-id', default=utils.env('OS_USER_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USER_ID]'}) parser.add_argument('--os_user_id', help=argparse.SUPPRESS) parser.add_argument('--os-user-domain-id', default=utils.env('OS_USER_DOMAIN_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USER_DOMAIN_ID]'}) parser.add_argument('--os_user_domain_id', help=argparse.SUPPRESS) parser.add_argument('--os-user-domain-name', default=utils.env('OS_USER_DOMAIN_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USER_DOMAIN_NAME]'}) parser.add_argument('--os_user_domain_name', help=argparse.SUPPRESS) parser.add_argument('--os-project-id', default=utils.env('OS_PROJECT_ID'), help=(_('Another way to specify tenant ID. ' 'This option is mutually exclusive with ' '%(arg)s. Defaults to %(value)s.') % { 'arg': '--os-tenant-id', 'value': 'env[OS_PROJECT_ID]' })) parser.add_argument('--os_project_id', help=argparse.SUPPRESS) parser.add_argument('--os-project-name', default=utils.env('OS_PROJECT_NAME'), help=(_('Another way to specify tenant name. ' 'This option is mutually exclusive with ' '%(arg)s. Defaults to %(value)s.') % { 'arg': '--os-tenant-name', 'value': 'env[OS_PROJECT_NAME]' })) parser.add_argument('--os_project_name', help=argparse.SUPPRESS) parser.add_argument('--os-project-domain-id', default=utils.env('OS_PROJECT_DOMAIN_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_PROJECT_DOMAIN_ID]'}) parser.add_argument('--os_project_domain_id', help=argparse.SUPPRESS) parser.add_argument('--os-project-domain-name', default=utils.env('OS_PROJECT_DOMAIN_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_PROJECT_DOMAIN_NAME]'}) parser.add_argument('--os_project_domain_name', help=argparse.SUPPRESS) parser.add_argument('--os-password', default=utils.env('OS_PASSWORD'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_PASSWORD]'}) parser.add_argument('--os_password', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-id', default=utils.env('OS_TENANT_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_TENANT_ID]'}) parser.add_argument('--os_tenant_id', default=utils.env('OS_TENANT_ID'), help=argparse.SUPPRESS) parser.add_argument('--os-tenant-name', default=utils.env('OS_TENANT_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_TENANT_NAME]'}) parser.add_argument('--os_tenant_name', default=utils.env('OS_TENANT_NAME'), help=argparse.SUPPRESS) parser.add_argument('--os-auth-url', default=utils.env('OS_AUTH_URL'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_AUTH_URL]'}) parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) parser.add_argument('--os-region-name', default=utils.env('OS_REGION_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_REGION_NAME]'}) parser.add_argument('--os_region_name', help=argparse.SUPPRESS) parser.add_argument('--os-auth-token', default=utils.env('OS_AUTH_TOKEN'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_AUTH_TOKEN]'}) parser.add_argument('--os_auth_token', help=argparse.SUPPRESS) parser.add_argument('--os-service-type', default=utils.env('OS_SERVICE_TYPE'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_SERVICE_TYPE]'}) parser.add_argument('--os_service_type', help=argparse.SUPPRESS) parser.add_argument('--os-endpoint-type', default=utils.env('OS_ENDPOINT_TYPE'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_ENDPOINT_TYPE]'}) parser.add_argument('--os_endpoint_type', help=argparse.SUPPRESS)
class HeatShell(object): def _append_global_identity_args(self, parser): # FIXME(gyee): these are global identity (Keystone) arguments which # should be consistent and shared by all service clients. Therefore, # they should be provided by python-keystoneclient. We will need to # refactor this code once this functionality is available in # python-keystoneclient. parser.add_argument( '-k', '--insecure', default=False, action='store_true', help=_('Explicitly allow heatclient to perform ' '\"insecure SSL\" (https) requests. ' 'The server\'s certificate will not be verified ' 'against any certificate authorities. ' 'This option should be used with caution.')) parser.add_argument( '--os-cert', help=_('Path of certificate file to use in SSL connection. ' 'This file can optionally be prepended with ' 'the private key.')) # for backward compatibility only parser.add_argument('--cert-file', dest='os_cert', help=_('DEPRECATED! Use %(arg)s.') % {'arg': '--os-cert'}) parser.add_argument('--os-key', help=_('Path of client key to use in SSL ' 'connection. This option is not necessary ' 'if your key is prepended to your cert ' 'file.')) parser.add_argument('--key-file', dest='os_key', help=_('DEPRECATED! Use %(arg)s.') % {'arg': '--os-key'}) parser.add_argument('--os-cacert', metavar='<ca-certificate-file>', dest='os_cacert', default=utils.env('OS_CACERT'), help=_('Path of CA TLS certificate(s) used to ' 'verify the remote server\'s certificate. ' 'Without this option glance looks for the ' 'default system CA certificates.')) parser.add_argument('--ca-file', dest='os_cacert', help=_('DEPRECATED! Use %(arg)s.') % {'arg': '--os-cacert'}) parser.add_argument('--os-username', default=utils.env('OS_USERNAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USERNAME]'}) parser.add_argument('--os_username', help=argparse.SUPPRESS) parser.add_argument('--os-user-id', default=utils.env('OS_USER_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USER_ID]'}) parser.add_argument('--os_user_id', help=argparse.SUPPRESS) parser.add_argument('--os-user-domain-id', default=utils.env('OS_USER_DOMAIN_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USER_DOMAIN_ID]'}) parser.add_argument('--os_user_domain_id', help=argparse.SUPPRESS) parser.add_argument('--os-user-domain-name', default=utils.env('OS_USER_DOMAIN_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_USER_DOMAIN_NAME]'}) parser.add_argument('--os_user_domain_name', help=argparse.SUPPRESS) parser.add_argument('--os-project-id', default=utils.env('OS_PROJECT_ID'), help=(_('Another way to specify tenant ID. ' 'This option is mutually exclusive with ' '%(arg)s. Defaults to %(value)s.') % { 'arg': '--os-tenant-id', 'value': 'env[OS_PROJECT_ID]' })) parser.add_argument('--os_project_id', help=argparse.SUPPRESS) parser.add_argument('--os-project-name', default=utils.env('OS_PROJECT_NAME'), help=(_('Another way to specify tenant name. ' 'This option is mutually exclusive with ' '%(arg)s. Defaults to %(value)s.') % { 'arg': '--os-tenant-name', 'value': 'env[OS_PROJECT_NAME]' })) parser.add_argument('--os_project_name', help=argparse.SUPPRESS) parser.add_argument('--os-project-domain-id', default=utils.env('OS_PROJECT_DOMAIN_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_PROJECT_DOMAIN_ID]'}) parser.add_argument('--os_project_domain_id', help=argparse.SUPPRESS) parser.add_argument('--os-project-domain-name', default=utils.env('OS_PROJECT_DOMAIN_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_PROJECT_DOMAIN_NAME]'}) parser.add_argument('--os_project_domain_name', help=argparse.SUPPRESS) parser.add_argument('--os-password', default=utils.env('OS_PASSWORD'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_PASSWORD]'}) parser.add_argument('--os_password', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-id', default=utils.env('OS_TENANT_ID'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_TENANT_ID]'}) parser.add_argument('--os_tenant_id', default=utils.env('OS_TENANT_ID'), help=argparse.SUPPRESS) parser.add_argument('--os-tenant-name', default=utils.env('OS_TENANT_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_TENANT_NAME]'}) parser.add_argument('--os_tenant_name', default=utils.env('OS_TENANT_NAME'), help=argparse.SUPPRESS) parser.add_argument('--os-auth-url', default=utils.env('OS_AUTH_URL'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_AUTH_URL]'}) parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) parser.add_argument('--os-region-name', default=utils.env('OS_REGION_NAME'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_REGION_NAME]'}) parser.add_argument('--os_region_name', help=argparse.SUPPRESS) parser.add_argument('--os-auth-token', default=utils.env('OS_AUTH_TOKEN'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_AUTH_TOKEN]'}) parser.add_argument('--os_auth_token', help=argparse.SUPPRESS) parser.add_argument('--os-service-type', default=utils.env('OS_SERVICE_TYPE'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_SERVICE_TYPE]'}) parser.add_argument('--os_service_type', help=argparse.SUPPRESS) parser.add_argument('--os-endpoint-type', default=utils.env('OS_ENDPOINT_TYPE'), help=_('Defaults to %(value)s.') % {'value': 'env[OS_ENDPOINT_TYPE]'}) parser.add_argument('--os_endpoint_type', help=argparse.SUPPRESS) def get_base_parser(self): parser = argparse.ArgumentParser( prog='heat', description=__doc__.strip(), epilog=_('See "%(arg)s" for help on a specific command.') % {'arg': 'heat help COMMAND'}, add_help=False, formatter_class=HelpFormatter, ) # Global arguments parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--version', action='version', version=heatclient.__version__, help=_("Shows the client version and exits.")) parser.add_argument('-d', '--debug', default=bool(utils.env('HEATCLIENT_DEBUG')), action='store_true', help=_('Defaults to %(value)s.') % {'value': 'env[HEATCLIENT_DEBUG]'}) parser.add_argument('-v', '--verbose', default=False, action="store_true", help=_("Print more verbose output.")) parser.add_argument('--api-timeout', help=_('Number of seconds to wait for an ' 'API response, ' 'defaults to system socket timeout')) # os-no-client-auth tells heatclient to use token, instead of # env[OS_AUTH_URL] parser.add_argument('--os-no-client-auth', default=utils.env('OS_NO_CLIENT_AUTH'), action='store_true', help=(_("Do not contact keystone for a token. " "Defaults to %(value)s.") % { 'value': 'env[OS_NO_CLIENT_AUTH]' })) parser.add_argument('--heat-url', default=utils.env('HEAT_URL'), help=_('Defaults to %(value)s.') % {'value': 'env[HEAT_URL]'}) parser.add_argument('--heat_url', help=argparse.SUPPRESS) parser.add_argument('--heat-api-version', default=utils.env('HEAT_API_VERSION', default='1'), help=_('Defaults to %(value)s or 1.') % {'value': 'env[HEAT_API_VERSION]'}) parser.add_argument('--heat_api_version', help=argparse.SUPPRESS) # This unused option should remain so that scripts that # use it do not break. It is suppressed so it will not # appear in the help. parser.add_argument('-t', '--token-only', default=bool(False), action='store_true', help=argparse.SUPPRESS) parser.add_argument('--include-password', default=bool(utils.env('HEAT_INCLUDE_PASSWORD')), action='store_true', help=_('Send %(arg1)s and %(arg2)s to heat.') % { 'arg1': 'os-username', 'arg2': 'os-password' }) # FIXME(gyee): this method should come from python-keystoneclient. # Will refactor this code once it is available. # https://bugs.launchpad.net/python-keystoneclient/+bug/1332337 self._append_global_identity_args(parser) if osprofiler_profiler: parser.add_argument( '--profile', metavar='HMAC_KEY', help=_('HMAC key to use for encrypting context data ' 'for performance profiling of operation. ' 'This key should be the value of HMAC key ' 'configured in osprofiler middleware in heat, ' 'it is specified in the paste configuration ' '(/etc/heat/api-paste.ini). Without the key, ' 'profiling will not be triggered ' 'even if osprofiler is enabled on server side.')) return parser def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='<subcommand>') submodule = utils.import_versioned_module(version, 'shell') self._find_actions(subparsers, submodule) self._find_actions(subparsers, self) self._add_bash_completion_subparser(subparsers) return parser def _add_bash_completion_subparser(self, subparsers): subparser = subparsers.add_parser('bash_completion', add_help=False, formatter_class=HelpFormatter) self.subcommands['bash_completion'] = subparser subparser.set_defaults(func=self.do_bash_completion) def _find_actions(self, subparsers, actions_module): for attr in (a for a in dir(actions_module) if a.startswith('do_')): # I prefer to be hyphen-separated instead of underscores. command = attr[3:].replace('_', '-') callback = getattr(actions_module, attr) desc = callback.__doc__ or '' help = desc.strip().split('\n')[0] arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser(command, help=help, description=desc, add_help=False, formatter_class=HelpFormatter) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS) self.subcommands[command] = subparser for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) def _setup_logging(self, debug): log_lvl = logging.DEBUG if debug else logging.WARNING logging.basicConfig(format="%(levelname)s (%(module)s) %(message)s", level=log_lvl) logging.getLogger('iso8601').setLevel(logging.WARNING) logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) def _setup_verbose(self, verbose): if verbose: exc.verbose = 1 def _discover_auth_versions(self, session, auth_url): # discover the API versions the server is supporting base on the # given URL v2_auth_url = None v3_auth_url = None try: ks_discover = discover.Discover(session=session, auth_url=auth_url) v2_auth_url = ks_discover.url_for('2.0') v3_auth_url = ks_discover.url_for('3.0') except ks_exc.ClientException: # Identity service may not support discover API version. # Lets trying to figure out the API version from the original URL. url_parts = urlparse.urlparse(auth_url) (scheme, netloc, path, params, query, fragment) = url_parts path = path.lower() if path.startswith('/v3'): v3_auth_url = auth_url elif path.startswith('/v2'): v2_auth_url = auth_url else: # not enough information to determine the auth version msg = _('Unable to determine the Keystone version ' 'to authenticate with using the given ' 'auth_url. Identity service may not support API ' 'version discovery. Please provide a versioned ' 'auth_url instead.') raise exc.CommandError(msg) return v2_auth_url, v3_auth_url def _get_keystone_session(self, **kwargs): # first create a Keystone session cacert = kwargs.pop('cacert', None) cert = kwargs.pop('cert', None) key = kwargs.pop('key', None) insecure = kwargs.pop('insecure', False) timeout = kwargs.pop('timeout', None) verify = kwargs.pop('verify', None) # FIXME(gyee): this code should come from keystoneclient if verify is None: if insecure: verify = False else: # TODO(gyee): should we do # heatclient.common.http.get_system_ca_fle()? verify = cacert or True if cert and key: # passing cert and key together is deprecated in favour of the # requests lib form of having the cert and key as a tuple cert = (cert, key) return kssession.Session(verify=verify, cert=cert, timeout=timeout) def _get_keystone_v3_auth(self, v3_auth_url, **kwargs): auth_token = kwargs.pop('auth_token', None) if auth_token: return v3_auth.Token(v3_auth_url, auth_token) else: return v3_auth.Password(v3_auth_url, **kwargs) def _get_keystone_v2_auth(self, v2_auth_url, **kwargs): auth_token = kwargs.pop('auth_token', None) tenant_id = kwargs.pop('project_id', None) tenant_name = kwargs.pop('project_name', None) if auth_token: return v2_auth.Token(v2_auth_url, auth_token, tenant_id=tenant_id, tenant_name=tenant_name) else: return v2_auth.Password(v2_auth_url, username=kwargs.pop('username', None), password=kwargs.pop('password', None), tenant_id=tenant_id, tenant_name=tenant_name) def _get_keystone_auth(self, session, auth_url, **kwargs): # FIXME(dhu): this code should come from keystoneclient # discover the supported keystone versions using the given url (v2_auth_url, v3_auth_url) = self._discover_auth_versions(session=session, auth_url=auth_url) # Determine which authentication plugin to use. First inspect the # auth_url to see the supported version. If both v3 and v2 are # supported, then use the highest version if possible. auth = None if v3_auth_url and v2_auth_url: user_domain_name = kwargs.get('user_domain_name', None) user_domain_id = kwargs.get('user_domain_id', None) project_domain_name = kwargs.get('project_domain_name', None) project_domain_id = kwargs.get('project_domain_id', None) # support both v2 and v3 auth. Use v3 if domain information is # provided. if (user_domain_name or user_domain_id or project_domain_name or project_domain_id): auth = self._get_keystone_v3_auth(v3_auth_url, **kwargs) else: auth = self._get_keystone_v2_auth(v2_auth_url, **kwargs) elif v3_auth_url: # support only v3 auth = self._get_keystone_v3_auth(v3_auth_url, **kwargs) elif v2_auth_url: # support only v2 auth = self._get_keystone_v2_auth(v2_auth_url, **kwargs) else: raise exc.CommandError( _('Unable to determine the Keystone ' 'version to authenticate with using the ' 'given auth_url.')) return auth def main(self, argv): # Parse args once to find version parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self._setup_logging(options.debug) self._setup_verbose(options.verbose) # build available subcommands based on version api_version = options.heat_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 args and 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 if not args.os_username and not args.os_auth_token: raise exc.CommandError( _("You must provide a username via either " "--os-username or env[OS_USERNAME] " "or a token via --os-auth-token or " "env[OS_AUTH_TOKEN]")) if not args.os_password and not args.os_auth_token: raise exc.CommandError( _("You must provide a password via either " "--os-password or env[OS_PASSWORD] " "or a token via --os-auth-token or " "env[OS_AUTH_TOKEN]")) if args.os_no_client_auth: if not args.heat_url: raise exc.CommandError( _("If you specify --os-no-client-auth " "you must also specify a Heat API " "URL via either --heat-url or " "env[HEAT_URL]")) else: # Tenant/project name or ID is needed to make keystoneclient # retrieve a service catalog, it's not required if # os_no_client_auth is specified, neither is the auth URL if not (args.os_tenant_id or args.os_tenant_name or args.os_project_id or args.os_project_name): raise exc.CommandError( _("You must provide a tenant id via either " "--os-tenant-id or env[OS_TENANT_ID] or a tenant name " "via either --os-tenant-name or env[OS_TENANT_NAME] " "or a project id via either --os-project-id or " "env[OS_PROJECT_ID] or a project name via " "either --os-project-name or env[OS_PROJECT_NAME]")) if not args.os_auth_url: raise exc.CommandError( _("You must provide an auth url via " "either --os-auth-url or via " "env[OS_AUTH_URL]")) kwargs = { 'insecure': args.insecure, 'cacert': args.os_cacert, 'cert': args.os_cert, 'key': args.os_key, 'timeout': args.api_timeout } endpoint = args.heat_url service_type = args.os_service_type or 'orchestration' if args.os_no_client_auth: # Do not use session since no_client_auth means using heat to # to authenticate kwargs = { 'username': args.os_username, 'password': args.os_password, 'auth_url': args.os_auth_url, 'token': args.os_auth_token, 'include_pass': args.include_password, 'insecure': args.insecure, 'timeout': args.api_timeout } else: keystone_session = self._get_keystone_session(**kwargs) project_id = args.os_project_id or args.os_tenant_id project_name = args.os_project_name or args.os_tenant_name endpoint_type = args.os_endpoint_type or 'publicURL' kwargs = { '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, } keystone_auth = self._get_keystone_auth(keystone_session, args.os_auth_url, **kwargs) if not endpoint: svc_type = service_type region_name = args.os_region_name endpoint = keystone_auth.get_endpoint(keystone_session, service_type=svc_type, interface=endpoint_type, region_name=region_name) kwargs = { 'auth_url': args.os_auth_url, 'session': keystone_session, 'auth': keystone_auth, 'service_type': service_type, 'endpoint_type': endpoint_type, 'region_name': args.os_region_name, 'username': args.os_username, 'password': args.os_password, 'include_pass': args.include_password } client = heat_client.Client(api_version, endpoint, **kwargs) profile = osprofiler_profiler and options.profile if profile: osprofiler_profiler.init(options.profile) args.func(client, args) if profile: trace_id = osprofiler_profiler.get().get_base_id() print(_("Trace ID: %s") % trace_id) print( _("To display trace use next command:\n" "osprofiler trace show --html %s ") % trace_id) def do_bash_completion(self, args): """Prints all of the commands and options to stdout. The heat.bash_completion script doesn't have to hard code them. """ commands = set() options = set() for sc_str, sc in self.subcommands.items(): commands.add(sc_str) for option in list(sc._optionals._option_string_actions): options.add(option) commands.remove('bash-completion') commands.remove('bash_completion') print(' '.join(commands | options)) @utils.arg('command', metavar='<subcommand>', nargs='?', help=_('Display help for <subcommand>.')) def do_help(self, args): """Display help about this program or one of its subcommands.""" if getattr(args, 'command', None): if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError("'%s' is not a valid subcommand" % args.command) else: self.parser.print_help()
def get_base_parser(self): parser = argparse.ArgumentParser( prog='heat', description=__doc__.strip(), epilog=_('See "%(arg)s" for help on a specific command.') % {'arg': 'heat help COMMAND'}, add_help=False, formatter_class=HelpFormatter, ) # Global arguments parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--version', action='version', version=heatclient.__version__, help=_("Shows the client version and exits.")) parser.add_argument('-d', '--debug', default=bool(utils.env('HEATCLIENT_DEBUG')), action='store_true', help=_('Defaults to %(value)s.') % {'value': 'env[HEATCLIENT_DEBUG]'}) parser.add_argument('-v', '--verbose', default=False, action="store_true", help=_("Print more verbose output.")) parser.add_argument('--api-timeout', help=_('Number of seconds to wait for an ' 'API response, ' 'defaults to system socket timeout')) # os-no-client-auth tells heatclient to use token, instead of # env[OS_AUTH_URL] parser.add_argument('--os-no-client-auth', default=utils.env('OS_NO_CLIENT_AUTH'), action='store_true', help=(_("Do not contact keystone for a token. " "Defaults to %(value)s.") % { 'value': 'env[OS_NO_CLIENT_AUTH]' })) parser.add_argument('--heat-url', default=utils.env('HEAT_URL'), help=_('Defaults to %(value)s.') % {'value': 'env[HEAT_URL]'}) parser.add_argument('--heat_url', help=argparse.SUPPRESS) parser.add_argument('--heat-api-version', default=utils.env('HEAT_API_VERSION', default='1'), help=_('Defaults to %(value)s or 1.') % {'value': 'env[HEAT_API_VERSION]'}) parser.add_argument('--heat_api_version', help=argparse.SUPPRESS) # This unused option should remain so that scripts that # use it do not break. It is suppressed so it will not # appear in the help. parser.add_argument('-t', '--token-only', default=bool(False), action='store_true', help=argparse.SUPPRESS) parser.add_argument('--include-password', default=bool(utils.env('HEAT_INCLUDE_PASSWORD')), action='store_true', help=_('Send %(arg1)s and %(arg2)s to heat.') % { 'arg1': 'os-username', 'arg2': 'os-password' }) # FIXME(gyee): this method should come from python-keystoneclient. # Will refactor this code once it is available. # https://bugs.launchpad.net/python-keystoneclient/+bug/1332337 self._append_global_identity_args(parser) if osprofiler_profiler: parser.add_argument( '--profile', metavar='HMAC_KEY', help=_('HMAC key to use for encrypting context data ' 'for performance profiling of operation. ' 'This key should be the value of HMAC key ' 'configured in osprofiler middleware in heat, ' 'it is specified in the paste configuration ' '(/etc/heat/api-paste.ini). Without the key, ' 'profiling will not be triggered ' 'even if osprofiler is enabled on server side.')) return parser
def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration columns = [ 'id', 'resource_status', 'resource_status_reason', 'event_time', 'physical_resource_id' ] kwargs = { 'resource_name': parsed_args.resource, 'limit': parsed_args.limit, 'marker': parsed_args.marker, # 'filters': heat_utils.format_parameters(parsed_args.filter), 'sort_dir': 'asc' } # if parsed_args.resource and parsed_args.nested_depth: # msg = _('--nested-depth cannot be specified with --resource') # raise exc.CommandError(msg) # if parsed_args.nested_depth: # # Until the API supports recursive event listing we'll have to do # # the marker/limit filtering client-side # del kwargs['marker'] # del kwargs['limit'] # columns.append('stack_name') # # nested_depth = parsed_args.nested_depth # else: # nested_depth = 0 if parsed_args.follow: if parsed_args.formatter != 'value': msg = _('--follow can only be specified with --format value') raise exc.CommandError(msg) marker = parsed_args.marker try: while True: kwargs['marker'] = marker events = event_utils.get_events( client, stack_id=parsed_args.stack, event_args=kwargs, # nested_depth=nested_depth, nested_depth=0, marker=marker) if events: marker = getattr(events[-1], 'id', None) events_log = heat_utils.event_log_formatter(events) self.app.stdout.write(events_log) self.app.stdout.write('\n') time.sleep(5) # this loop never exits except (KeyboardInterrupt, EOFError): # ctrl-c, ctrl-d return [], [] events = event_utils.get_events( client, stack_id=parsed_args.stack, event_args=kwargs, # nested_depth=nested_depth, nested_depth=0, marker=parsed_args.marker, limit=parsed_args.limit) if parsed_args.sort: events = utils.sort_items(events, ','.join(parsed_args.sort)) if parsed_args.formatter == 'value': events = heat_utils.event_log_formatter(events).split('\n') return [], [e.split(' ') for e in events] if len(events): if hasattr(events[0], 'resource_name'): columns.insert(0, 'resource_name') columns.append('logical_resource_id') else: columns.insert(0, 'logical_resource_id') return (columns, (utils.get_item_properties(s, columns) for s in events))
def get_parser(self, prog_name): parser = super(UpdateStack, self).get_parser(prog_name) parser.add_argument( '-t', '--template', metavar='<template>', help=_('Path to the template') ) parser.add_argument( '-e', '--environment', metavar='<environment>', action='append', help=_('Path to the environment. Can be specified multiple times') ) parser.add_argument( '--pre-update', metavar='<resource>', action='append', help=_('Name of a resource to set a pre-update hook to. Resources ' 'in nested stacks can be set using slash as a separator: ' 'nested_stack/another/my_resource. You can use wildcards ' 'to match multiple stacks or resources: ' 'nested_stack/an*/*_resource. This can be specified ' 'multiple times') ) parser.add_argument( '--timeout', metavar='<timeout>', type=int, help=_('Stack update timeout in minutes') ) parser.add_argument( '--rollback', metavar='<value>', help=_('Set rollback on update failure. ' 'Value "enabled" sets rollback to enabled. ' 'Value "disabled" sets rollback to disabled. ' 'Value "keep" uses the value of existing stack to be ' 'updated (default)') ) parser.add_argument( '--dry-run', action="store_true", help=_('Do not actually perform the stack update, but show what ' 'would be changed') ) parser.add_argument( '--parameter', metavar='<key=value>', help=_('Parameter values used to create the stack. ' 'This can be specified multiple times'), action='append' ) parser.add_argument( '--parameter-file', metavar='<key=file>', help=_('Parameter values from file used to create the stack. ' 'This can be specified multiple times. Parameter value ' 'would be the content of the file'), action='append' ) parser.add_argument( '--existing', action="store_true", help=_('Re-use the template, parameters and environment of the ' 'current stack. If the template argument is omitted then ' 'the existing template is used. If no %(env_arg)s is ' 'specified then the existing environment is used. ' 'Parameters specified in %(arg)s will patch over the ' 'existing values in the current stack. Parameters omitted ' 'will keep the existing values') % { 'arg': '--parameter', 'env_arg': '--environment'} ) parser.add_argument( '--clear-parameter', metavar='<parameter>', help=_('Remove the parameters from the set of parameters of ' 'current stack for the %(cmd)s. The default value in the ' 'template will be used. This can be specified multiple ' 'times') % {'cmd': 'stack-update'}, action='append' ) parser.add_argument( 'stack', metavar='<stack>', help=_('Name or ID of stack to update') ) parser.add_argument( '--tags', metavar='<tag1,tag2...>', help=_('An updated list of tags to associate with the stack') ) parser.add_argument( '--wait', action='store_true', help=_('Wait until stack goes to UPDATE_COMPLETE or ' 'UPDATE_FAILED') ) return parser
def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__(_("AmbiguousEndpoints: %r") % endpoints) self.endpoints = endpoints
def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration tpl_files, template = template_utils.process_template_path( parsed_args.template, object_request=_authenticated_fetcher(client), existing=parsed_args.existing) env_files, env = ( template_utils.process_multiple_environments_and_files( env_paths=parsed_args.environment)) parameters = heat_utils.format_all_parameters( parsed_args.parameter, parsed_args.parameter_file, parsed_args.template) if parsed_args.pre_update: template_utils.hooks_to_env(env, parsed_args.pre_update, 'pre-update') fields = { 'stack_id': parsed_args.stack, 'parameters': parameters, 'existing': parsed_args.existing, 'template': template, 'files': dict(list(tpl_files.items()) + list(env_files.items())), 'environment': env } if parsed_args.tags: fields['tags'] = parsed_args.tags if parsed_args.timeout: fields['timeout_mins'] = parsed_args.timeout if parsed_args.clear_parameter: fields['clear_parameters'] = list(parsed_args.clear_parameter) if parsed_args.rollback: rollback = parsed_args.rollback.strip().lower() if rollback not in ('enabled', 'disabled', 'keep'): msg = _('--rollback invalid value: %s') % parsed_args.rollback raise exc.CommandError(msg) if rollback != 'keep': fields['disable_rollback'] = rollback == 'disabled' if parsed_args.dry_run: changes = client.stacks.preview_update(**fields) fields = ['state', 'resource_name', 'resource_type', 'resource_identity'] columns = sorted(changes.get("resource_changes", {}).keys()) data = [heat_utils.json_formatter(changes["resource_changes"][key]) for key in columns] return columns, data if parsed_args.wait: # find the last event to use as the marker events = event_utils.get_events(client, stack_id=parsed_args.stack, event_args={'sort_dir': 'desc', 'limit': 1}) marker = events[0].id if events else None client.stacks.update(**fields) if parsed_args.wait: stack = client.stacks.get(parsed_args.stack) stack_status, msg = event_utils.poll_for_events( client, stack.stack_name, action='UPDATE', marker=marker) if stack_status == 'UPDATE_FAILED': raise exc.CommandError(msg) return _show_stack(client, parsed_args.stack, format='table', short=True)
class HTTPClientError(HttpError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = _("HTTP Client Error")
def get_parser(self, prog_name): parser = super(ListStack, self).get_parser(prog_name) parser.add_argument( '--deleted', action='store_true', help=_('Include soft-deleted stacks in the stack listing') ) # parser.add_argument( # '--nested', # action='store_true', # help=_('Include nested stacks in the stack listing') # ) # parser.add_argument( # '--hidden', # action='store_true', # help=_('Include hidden stacks in the stack listing') # ) # parser.add_argument( # '--property', # dest='properties', # metavar='<key=value>', # help=_('Filter properties to apply on returned stacks (repeat to ' # 'filter on multiple properties)'), # action=parseractions.KeyValueAction # ) parser.add_argument( '--tags', metavar='<tag1,tag2...>', help=_('List of tags to filter by. Can be combined with ' '--tag-mode to specify how to filter tags') ) parser.add_argument( '--tag-mode', metavar='<mode>', help=_('Method of filtering tags. Must be one of "any", "not", ' 'or "not-any". If not specified, multiple tags will be ' 'combined with the boolean AND expression') ) parser.add_argument( '--limit', metavar='<limit>', help=_('The number of stacks returned') ) parser.add_argument( '--marker', metavar='<id>', help=_('Only return stacks that appear after the given ID') ) parser.add_argument( '--sort', metavar='<key>[:<direction>]', help=_('Sort output by selected keys and directions (asc or desc) ' '(default: asc). Specify multiple times to sort on ' 'multiple properties') ) # parser.add_argument( # '--all-projects', # action='store_true', # help=_('Include all projects (admin only)') # ) parser.add_argument( '--short', action='store_true', help=_('List fewer fields in output') ) # parser.add_argument( # '--long', # action='store_true', # help=_('List additional fields in output, this is implied by ' # '--all-projects') # ) return parser
def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__(_("AuthSystemNotFound: %r") % auth_system) self.auth_system = auth_system
def get_parser(self, prog_name): parser = super(TemplateShowStack, self).get_parser(prog_name) parser.add_argument('stack', metavar='<stack>', help=_('Name or ID of stack to query')) return parser