def get_template_contents(template_file=None, template_url=None, template_object=None, object_request=None, files=None, existing=False): is_object = False # Transform a bare file path to a file:// URL. if template_file: template_url = utils.normalise_file_path_to_url(template_file) if template_url: tpl = request.urlopen(template_url).read() elif template_object: is_object = True template_url = template_object tpl = object_request and object_request('GET', template_object) elif existing: return {}, None else: raise exc.CommandError( _('Need to specify exactly one of ' '%(arg1)s, %(arg2)s or %(arg3)s') % { 'arg1': '--template-file', 'arg2': '--template-url', 'arg3': '--template-object' }) if not tpl: raise exc.CommandError( _('Could not fetch template from %s') % template_url) try: if isinstance(tpl, six.binary_type): tpl = tpl.decode('utf-8') template = template_format.parse(tpl) except ValueError as e: raise exc.CommandError( _('Error parsing template %(url)s %(error)s') % { 'url': template_url, 'error': e }) tmpl_base_url = utils.base_url_for_url(template_url) if files is None: files = {} resolve_template_get_files(template, files, tmpl_base_url, is_object, object_request) return files, template
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 take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration fields = { 'stack_id': parsed_args.stack, 'resource_name': parsed_args.resource, 'event_id': parsed_args.event } try: client.stacks.get(parsed_args.stack) client.resources.get(parsed_args.stack, parsed_args.resource) event = client.events.get(**fields) except exc.HTTPNotFound as ex: raise exc.CommandError(str(ex)) formatters = { 'links': heat_utils.link_formatter, 'resource_properties': heat_utils.json_formatter } columns = [] for key in event.to_dict(): columns.append(key) return columns, utils.get_item_properties(event, columns, formatters=formatters)
def find_resource(manager, name_or_id): """Helper for the _find_* methods.""" # first try to get entity as integer id try: if isinstance(name_or_id, int) or name_or_id.isdigit(): return manager.get(int(name_or_id)) except exc.NotFound: pass # now try to get entity as uuid try: uuid.UUID(str(name_or_id)) return manager.get(name_or_id) except (ValueError, exc.NotFound): pass # finally try to find entity by name try: return manager.find(name=name_or_id) except exc.NotFound: msg = (_("No %(name)s with a name or ID of " "'%(name_or_id)s' exists.") % { 'name': manager.resource_class.__name__.lower(), 'name_or_id': name_or_id }) raise exc.CommandError(msg)
def format_parameters(params, parse_semicolon=True): """Reformat parameters into dict of format expected by the API.""" if not params: return {} if parse_semicolon: # expect multiple invocations of --parameters but fall back # to ; delimited if only one --parameters is specified if len(params) == 1: params = params[0].split(';') parameters = {} for p in params: try: (n, v) = p.split('=', 1) except ValueError: msg = _('Malformed parameter(%s). Use the key=value format.') % p raise exc.CommandError(msg) if n not in parameters: parameters[n] = v else: if not isinstance(parameters[n], list): parameters[n] = [parameters[n]] parameters[n].append(v) return parameters
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_nested_ids(hc, stack_id): nested_ids = [] try: resources = hc.resources.list(stack_id=stack_id) except exc.HTTPNotFound: raise exc.CommandError(_('Stack not found: %s') % stack_id) for r in resources: nested_id = utils.resource_nested_identifier(r) if nested_id: nested_ids.append(nested_id) return nested_ids
def get_hook_type_via_status(hc, stack_id): # Figure out if the hook should be pre-create or pre-update based # on the stack status, also sanity assertions that we're in-progress. try: stack = hc.stacks.get(stack_id=stack_id) except exc.HTTPNotFound: raise exc.CommandError(_('Stack not found: %s') % stack_id) else: if 'IN_PROGRESS' not in stack.stack_status: raise exc.CommandError(_('Stack status %s not IN_PROGRESS') % stack.stack_status) if 'CREATE' in stack.stack_status: hook_type = 'pre-create' elif 'UPDATE' in stack.stack_status: hook_type = 'pre-update' else: raise exc.CommandError(_('Unexpected stack status %s, ' 'only create/update supported') % stack.stack_status) return hook_type
def build_signal_id(hc, args): if args.signal_transport != 'TEMP_URL_SIGNAL': return if args.os_no_client_auth: raise exc.CommandError( _('Cannot use --os-no-client-auth, auth required to create ' 'a Swift TempURL.')) swift_client = create_swift_client(hc.http_client.auth, hc.http_client.session, args) return create_temp_url(swift_client, args.name, args.timeout)
def read_url_content(url): try: content = request.urlopen(url).read() except error.URLError: raise exc.CommandError(_('Could not fetch contents for %s') % url) if content: try: content.decode('utf-8') except ValueError: content = base64.encodestring(content) return content
def _get_stack_events(hc, stack_id, event_args): event_args['stack_id'] = stack_id try: events = hc.events.list(**event_args) except exc.HTTPNotFound as ex: # it could be the stack or resource that is not found # just use the message that the server sent us. raise exc.CommandError(str(ex)) else: # Show which stack the event comes from (for nested events) for e in events: e.stack_name = stack_id.split("/")[0] return events
def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration version = parsed_args.template_version try: functions = client.template_versions.get(version) except exc.HTTPNotFound: msg = _('Template version not found: %s') % version raise exc.CommandError(msg) fields = ['functions', 'description'] return (fields, (utils.get_item_properties(s, fields) for s in functions))
def get_hook_events(hc, stack_id, event_args, nested_depth=0, hook_type='pre-create'): if hook_type == 'pre-create': stack_action_reason = 'Stack CREATE started' hook_event_reason = 'CREATE paused until Hook pre-create is cleared' hook_clear_event_reason = 'Hook pre-create is cleared' elif hook_type == 'pre-update': stack_action_reason = 'Stack UPDATE started' hook_event_reason = 'UPDATE paused until Hook pre-update is cleared' hook_clear_event_reason = 'Hook pre-update is cleared' else: raise exc.CommandError(_('Unexpected hook type %s') % hook_type) events = get_events(hc, stack_id=stack_id, event_args=event_args, nested_depth=nested_depth) # Get the most recent event associated with this action, which gives us the # event when we moved into IN_PROGRESS for the hooks we're interested in. stack_name = stack_id.split("/")[0] action_start_event = [ e for e in enumerate(events) if e[1].resource_status_reason == stack_action_reason and e[1].stack_name == stack_name ][-1] # Slice the events with the index from the enumerate action_start_index = action_start_event[0] events = events[action_start_index:] # Get hook events still pending by some list filtering/comparison # We build a map hook events per-resource, and remove any event # for which there is a corresponding hook-clear event. resource_event_map = {} for e in events: stack_resource = (e.stack_name, e.resource_name) if e.resource_status_reason == hook_event_reason: resource_event_map[(e.stack_name, e.resource_name)] = e elif e.resource_status_reason == hook_clear_event_reason: if resource_event_map.get(stack_resource): del (resource_event_map[(e.stack_name, e.resource_name)]) return list(resource_event_map.values())
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 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 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 build_derived_config_params(action, source, name, input_values, server_id, signal_transport, signal_id=None): if isinstance(source, SoftwareConfig): source = source.to_dict() input_values = input_values or {} inputs = copy.deepcopy(source.get('inputs')) or [] for inp in inputs: input_key = inp['name'] inp['value'] = input_values.pop(input_key, inp.get('default')) # for any input values that do not have a declared input, add # a derived declared input so that they can be used as config # inputs for inpk, inpv in input_values.items(): inputs.append({'name': inpk, 'type': 'String', 'value': inpv}) inputs.extend([{ 'name': 'deploy_server_id', 'description': _('ID of the server being deployed to'), 'type': 'String', 'value': server_id }, { 'name': 'deploy_action', 'description': _('Name of the current action being deployed'), 'type': 'String', 'value': action }, { 'name': 'deploy_signal_transport', 'description': _('How the server should signal to heat with ' 'the deployment output values.'), 'type': 'String', 'value': signal_transport }]) if signal_transport == 'TEMP_URL_SIGNAL': inputs.append({ 'name': 'deploy_signal_id', 'description': _('ID of signal to use for signaling ' 'output values'), 'type': 'String', 'value': signal_id }) inputs.append({ 'name': 'deploy_signal_verb', 'description': _('HTTP verb to use for signaling ' 'output values'), 'type': 'String', 'value': 'PUT' }) elif signal_transport != 'NO_SIGNAL': raise exc.CommandError( _('Unsupported signal transport %s') % signal_transport) return { 'group': source.get('group') or 'Heat::Ungrouped', 'config': source.get('config') or '', 'options': source.get('options') or {}, 'inputs': inputs, 'outputs': source.get('outputs') or [], 'name': name }