def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) senlin_client = self.app.client_manager.clustering try: if not parsed_args.force and sys.stdin.isatty(): sys.stdout.write( _("Are you sure you want to delete this node(s)" " [y/N]?")) prompt_response = sys.stdin.readline().lower() if not prompt_response.startswith('y'): return except KeyboardInterrupt: # Ctrl-c self.log.info(_LI('Ctrl-c detected.')) return except EOFError: # Ctrl-d self.log.info(_LI('Ctrl-d detected')) return failure_count = 0 for nid in parsed_args.node: try: senlin_client.delete_node(nid, False) except Exception as ex: failure_count += 1 print(ex) if failure_count: raise exc.CommandError(_('Failed to delete %(count)s of the ' '%(total)s specified node(s).') % {'count': failure_count, 'total': len(parsed_args.node)}) print('Request accepted')
def parse_exception(exc): """Parse exception code and yield useful information. :param details: details of the exception. """ if isinstance(exc, sdkexc.HttpException): try: record = jsonutils.loads(exc.details) except Exception: # If the exc.details is not in JSON format record = { 'error': { 'code': exc.status_code, 'message': exc.details, } } elif isinstance(exc, reqexc.RequestException): # Exceptions that are not captured by SDK record = { 'error': { 'code': exc.message[1].errno, 'message': exc.message[0], } } elif isinstance(exc, six.string_types): record = jsonutils.loads(exc) # some exception from keystoneauth1 is not shaped by SDK elif isinstance(exc, kae_http.HttpError): record = { 'error': { 'code': exc.http_status, 'message': exc.message } } elif isinstance(exc, kae_base.ClientException): record = { 'error': { # other exceptions from keystoneauth1 is an internal # error to senlin, so set status code to 500 'code': 500, 'message': exc.message } } else: print(_('Unknown exception: %s') % exc) return try: code = record['error']['code'] except KeyError as err: print(_('Malformed exception record, missing field "%s"') % err) print(_('Original error record: %s') % record) return if code in _EXCEPTION_MAP: inst = _EXCEPTION_MAP.get(code) raise inst(record) else: raise HTTPException(record)
def get_parser(self, prog_name): parser = super(ClusterPolicyList, self).get_parser(prog_name) parser.add_argument( '--filters', metavar='<key1=value1;key2=value2...>', help=_("Filter parameters to apply on returned results. " "This can be specified multiple times, or once with " "parameters separated by a semicolon. The valid filter " "keys are: ['type', 'name']"), action='append' ) parser.add_argument( '--sort', metavar='<sort_string>', help=_("Sorting option which is a string containing a list of " "keys separated by commas. Each key can be optionally " "appended by a sort direction (:asc or :desc). The valid " "sort keys are: ['type', 'name', 'created_at', " "'updated_at']") ) parser.add_argument( '--full-id', default=False, action="store_true", help=_('Print full IDs in list') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('Name or ID of cluster to query on') ) return parser
def add_global_args(parser, version): # GLOBAL ARGUMENTS parser.add_argument( '-h', '--help', action='store_true', help=argparse.SUPPRESS) parser.add_argument( '--version', action='version', version=version, help=_("Shows the client version and exits.")) parser.add_argument( '-d', '--debug', action='store_true', default=bool(utils.env('SENLINCLIENT_DEBUG')), help=_('Defaults to env[SENLINCLIENT_DEBUG].')) parser.add_argument( '-v', '--verbose', action="store_true", default=False, 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')) parser.add_argument( '--senlin-api-version', default=utils.env('SENLIN_API_VERSION', default='1'), help=_('Version number for Senlin API to use, Default to "1".'))
def process_stack_spec(spec): # Heat stack is a headache, because it demands for client side file # content processing try: tmplfile = spec.get('template', None) except AttributeError as ex: raise exc.FileFormatError( _('The specified file is not a valid ' 'YAML file: %s') % six.text_type(ex)) if not tmplfile: raise exc.FileFormatError( _('No template found in the given ' 'spec file')) tpl_files, template = template_utils.get_template_contents( template_file=tmplfile) env_files, env = template_utils.process_multiple_environments_and_files( env_paths=spec.get('environment', None)) new_spec = { # TODO(Qiming): add context support 'disable_rollback': spec.get('disable_rollback', True), 'context': spec.get('context', {}), 'parameters': spec.get('parameters', {}), 'timeout': spec.get('timeout', 60), 'template': template, 'files': dict(list(tpl_files.items()) + list(env_files.items())), 'environment': env } return new_spec
def get_parser(self, prog_name): parser = super(CreateNode, self).get_parser(prog_name) parser.add_argument( '--profile', metavar='<profile>', required=True, help=_('Profile Id or Name used for this node') ) parser.add_argument( '--cluster', metavar='<cluster>', help=_('Cluster Id or Name for this node') ) parser.add_argument( '--role', metavar='<role>', help=_('Role for this node in the specific cluster') ) parser.add_argument( '--metadata', metavar='<key1=value1;key2=value2...>', help=_('Metadata values to be attached to the node. ' 'This can be specified multiple times, or once with ' 'key-value pairs separated by a semicolon'), action='append' ) parser.add_argument( 'name', metavar='<node_name>', help=_('Name of the node to create') ) return parser
def get_parser(self, prog_name): parser = super(UpdateCluster, self).get_parser(prog_name) parser.add_argument( '--profile', metavar='<profile>', help=_('ID or name of new profile to use') ) parser.add_argument( '--timeout', metavar='<timeout>', help=_('New timeout (in seconds) value for the cluster') ) parser.add_argument( '--metadata', metavar='<"key1=value1;key2=value2...">', help=_("Metadata values to be attached to the cluster. " "This can be specified multiple times, or once with " "key-value pairs separated by a semicolon. Use '{}' " "can clean metadata "), action='append' ) parser.add_argument( '--name', metavar='<name>', help=_('New name for the cluster to update') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('Name or ID of cluster to be updated') ) return parser
def process_stack_spec(spec): # Heat stack is a headache, because it demands for client side file # content processing try: tmplfile = spec.get('template', None) except AttributeError as ex: raise exc.FileFormatError(_('The specified file is not a valid ' 'YAML file: %s') % six.text_type(ex)) if not tmplfile: raise exc.FileFormatError(_('No template found in the given ' 'spec file')) tpl_files, template = template_utils.get_template_contents( template_file=tmplfile) env_files, env = template_utils.process_multiple_environments_and_files( env_paths=spec.get('environment', None)) new_spec = { # TODO(Qiming): add context support 'disable_rollback': spec.get('disable_rollback', True), 'context': spec.get('context', {}), 'parameters': spec.get('parameters', {}), 'timeout': spec.get('timeout', 60), 'template': template, 'files': dict(list(tpl_files.items()) + list(env_files.items())), 'environment': env } return new_spec
def get_parser(self, prog_name): parser = super(UpdateCluster, self).get_parser(prog_name) parser.add_argument( '--profile', metavar='<profile>', help=_('ID or name of new profile to use') ) parser.add_argument( '--timeout', metavar='<timeout>', help=_('New timeout (in seconds) value for the cluster') ) parser.add_argument( '--metadata', metavar='<key1=value1;key2=value2...>', help=_('Metadata values to be attached to the cluster. ' 'This can be specified multiple times, or once with ' 'key-value pairs separated by a semicolon'), action='append' ) parser.add_argument( '--name', metavar='<name>', help=_('New name for the cluster to update') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('Name or ID of cluster to be updated') ) return parser
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) senlin_client = self.app.client_manager.clustering spec = senlin_utils.get_spec_content(parsed_args.spec_file) type_name = spec.get('type', None) type_version = spec.get('version', None) properties = spec.get('properties', None) if type_name is None: raise exc.CommandError(_("Missing 'type' key in spec file.")) if type_version is None: raise exc.CommandError(_("Missing 'version' key in spec file.")) if properties is None: raise exc.CommandError(_("Missing 'properties' key in spec file.")) if type_name == 'os.heat.stack': stack_properties = senlin_utils.process_stack_spec(properties) spec['properties'] = stack_properties params = { 'name': parsed_args.name, 'spec': spec, 'metadata': senlin_utils.format_parameters(parsed_args.metadata), } profile = senlin_client.create_profile(**params) return _show_profile(senlin_client, profile_id=profile.id)
def get_parser(self, prog_name): parser = super(CreateReceiver, self).get_parser(prog_name) parser.add_argument( '--type', metavar='<type>', default='webhook', help=_('Type of the receiver to create') ) parser.add_argument( '--cluster', metavar='<cluster>', required=True, help=_('Targeted cluster for this receiver') ) parser.add_argument( '--action', metavar='<action>', required=True, help=_('Name or ID of the targeted action to be triggered') ) parser.add_argument( '--params', metavar='<key1=value1;key2=value2...>', help=_('A dictionary of parameters that will be passed to target ' 'action when the receiver is triggered'), action='append' ) parser.add_argument( 'name', metavar='<name>', help=_('Name of the receiver to create') ) return parser
def get_parser(self, prog_name): parser = super(ClusterPolicyList, self).get_parser(prog_name) parser.add_argument( '--filters', metavar='<"key1=value1;key2=value2...">', help=_("Filter parameters to apply on returned results. " "This can be specified multiple times, or once with " "parameters separated by a semicolon. The valid filter " "keys are: ['is_enabled', 'policy_type', 'policy_name']"), action='append') parser.add_argument( '--sort', metavar='<key>[:<direction>]', help=_("Sorting option which is a string containing a list of " "keys separated by commas. Each key can be optionally " "appended by a sort direction (:asc or :desc). The valid " "sort keys are: ['enabled']")) parser.add_argument('--full-id', default=False, action="store_true", help=_('Print full IDs in list')) parser.add_argument('cluster', metavar='<cluster>', help=_('Name or ID of cluster to query on')) return parser
def get_parser(self, prog_name): parser = super(ListAction, self).get_parser(prog_name) parser.add_argument( '--filters', metavar='<key1=value1;key2=value2...>', help=_("Filter parameters to apply on returned actions. " "This can be specified multiple times, or once with " "parameters separated by a semicolon. The valid filter " "keys are: ['name', 'target', 'action', 'status']"), action='append') parser.add_argument( '--sort', metavar='<key:dir>', help=_("Sorting option which is a string containing a list of " "keys separated by commas. Each key can be optionally " "appended by a sort direction (:asc or :desc). The valid " "sort keys are: ['name', 'target', 'action', 'created_at'," " 'status']")) parser.add_argument('--limit', metavar='<limit>', help=_('Limit the number of actions returned')) parser.add_argument( '--marker', metavar='<id>', help=_('Only return actions that appear after the given node ID')) parser.add_argument('--full-id', default=False, action="store_true", help=_('Print full IDs in list')) return parser
def get_parser(self, prog_name): parser = super(CreateNode, self).get_parser(prog_name) parser.add_argument( '--cluster', metavar='<cluster>', help=_('Cluster Id or Name for this node') ) parser.add_argument( '--role', metavar='<role>', help=_('Role for this node in the specific cluster') ) parser.add_argument( '--metadata', metavar='<"key1=value1;key2=value2...">', help=_('Metadata values to be attached to the node. ' 'This can be specified multiple times, or once with ' 'key-value pairs separated by a semicolon'), action='append' ) parser.add_argument( '--profile', metavar='<profile>', required=True, help=_('Profile Id or Name used for this node') ) parser.add_argument( 'name', metavar='<node-name>', help=_('Name of the node to create') ) return parser
def do_profile_create(service, args): """Create a profile.""" spec = utils.get_spec_content(args.spec_file) type_name = spec.get('type', None) type_version = spec.get('version', None) properties = spec.get('properties', None) if type_name is None: raise exc.CommandError(_("Missing 'type' key in spec file.")) if type_version is None: raise exc.CommandError(_("Missing 'version' key in spec file.")) if properties is None: raise exc.CommandError(_("Missing 'properties' key in spec file.")) if type_name == 'os.heat.stack': stack_properties = utils.process_stack_spec(properties) spec['properties'] = stack_properties params = { 'name': args.name, 'spec': spec, 'metadata': utils.format_parameters(args.metadata), } profile = service.create_profile(**params) _show_profile(service, profile.id)
def get_parser(self, prog_name): parser = super(UpdateNode, self).get_parser(prog_name) parser.add_argument( '--name', metavar='<name>', help=_('New name for the node') ) parser.add_argument( '--profile', metavar='<profile>', help=_('ID or name of new profile to use') ) parser.add_argument( '--role', metavar='<role>', help=_('Role for this node in the specific cluster') ) parser.add_argument( '--metadata', metavar='<"key1=value1;key2=value2...">', help=_("Metadata values to be attached to the node. " "This can be specified multiple times, or once with " "key-value pairs separated by a semicolon. Use '{}' " "can clean metadata "), action='append' ) parser.add_argument( 'node', metavar='<node>', help=_('Name or ID of node to update') ) return parser
def get_parser(self, prog_name): parser = super(CreateReceiver, self).get_parser(prog_name) parser.add_argument( '--type', metavar='<type>', default='webhook', help=_('Type of the receiver to create. Receiver type can be ' '"webhook" or "message". Default to "webhook".')) parser.add_argument( '--params', metavar='<"key1=value1;key2=value2...">', help=_('A dictionary of parameters that will be passed to target ' 'action when the receiver is triggered'), action='append') parser.add_argument( '--cluster', metavar='<cluster>', help=_('Targeted cluster for this receiver. Required if ' 'receiver type is webhook')) parser.add_argument( '--action', metavar='<action>', help=_('Name or ID of the targeted action to be triggered. ' 'Required if receiver type is webhook')) parser.add_argument('name', metavar='<name>', help=_('Name of the receiver to create')) return parser
def parse_exception(exc): """Parse exception code and yield useful information. :param details: details of the exception. """ if isinstance(exc, sdkexc.HttpException): try: record = jsonutils.loads(exc.details) except Exception: # If the exc.details is not in JSON format record = { 'error': { 'code': exc.http_status, 'message': exc.details, } } elif isinstance(exc, reqexc.RequestException): # Exceptions that are not captured by SDK record = { 'error': { 'code': exc.message[1].errno, 'message': exc.message[0], } } elif isinstance(exc, six.string_types): record = jsonutils.loads(exc) # some exception from keystoneauth1 is not shaped by SDK elif isinstance(exc, kae_http.HttpError): record = { 'error': { 'code': exc.http_status, 'message': exc.message } } elif isinstance(exc, kae_base.ClientException): record = { 'error': { # other exceptions from keystoneauth1 is an internal # error to senlin, so set status code to 500 'code': 500, 'message': exc.message } } else: print(_('Unknown exception: %s') % exc) return try: code = record['error']['code'] except KeyError as err: print(_('Malformed exception record, missing field "%s"') % err) print(_('Original error record: %s') % record) return if code in _EXCEPTION_MAP: inst = _EXCEPTION_MAP.get(code) raise inst(record) else: raise HTTPException(record)
def add_global_args(parser, version): # GLOBAL ARGUMENTS parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--version', action='version', version=version, help=_("Shows the client version and exits.")) parser.add_argument('-d', '--debug', action='store_true', default=bool(utils.env('SENLINCLIENT_DEBUG')), help=_('Defaults to env[SENLINCLIENT_DEBUG].')) parser.add_argument('-v', '--verbose', action="store_true", default=False, 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')) parser.add_argument( '--senlin-api-version', default=utils.env('SENLIN_API_VERSION', default='1'), help=_('Version number for Senlin API to use, Default to "1".'))
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) senlin_client = self.app.client_manager.clustering try: if not parsed_args.force and sys.stdin.isatty(): sys.stdout.write( _("Are you sure you want to delete this policy(s)" " [y/N]?")) prompt_response = sys.stdin.readline().lower() if not prompt_response.startswith('y'): return except KeyboardInterrupt: # Ctrl-c self.log.info('Ctrl-c detected.') return except EOFError: # Ctrl-d self.log.info('Ctrl-d detected') return failure_count = 0 for pid in parsed_args.policy: try: senlin_client.delete_policy(pid, False) except Exception as ex: failure_count += 1 print(ex) if failure_count: raise exc.CommandError( _('Failed to delete %(count)s of the ' '%(total)s specified policy(s).') % { 'count': failure_count, 'total': len(parsed_args.policy) }) print('Policy deleted: %s' % parsed_args.policy)
def get_parser(self, prog_name): parser = super(UpdateNode, self).get_parser(prog_name) parser.add_argument( '--name', metavar='<name>', help=_('New name for the node') ) parser.add_argument( '--profile', metavar='<profile_id>', help=_('ID of new profile to use') ) parser.add_argument( '--role', metavar='<role>', help=_('Role for this node in the specific cluster') ) parser.add_argument( '--metadata', metavar='<key1=value1;key2=value2...>', help=_('Metadata values to be attached to the node. ' 'Metadata can be specified multiple times, or once with ' 'key-value pairs separated by a semicolon'), action='append' ) parser.add_argument( 'node', metavar='<node>', help=_('Name or ID of node to update') ) return parser
def get_parser(self, prog_name): parser = super(UpdateAction, self).get_parser(prog_name) parser.add_argument('--status', metavar='<status>', help=_('The new status for the action')) parser.add_argument('action', metavar='<action>', help=_('ID of the action to update')) return parser
def get_parser(self, prog_name): parser = super(UpdatePolicy, self).get_parser(prog_name) parser.add_argument('--name', metavar='<name>', help=_('New name of the policy to be updated')) parser.add_argument('policy', metavar='<policy>', help=_('Name or ID of the policy to be updated')) return parser
def __str__(self): message = self.error['error'].get('message', 'Internal Error') if verbose: traceback = self.error['error'].get('traceback', '') return (_('ERROR: %(message)s\n%(traceback)s') % {'message': message, 'traceback': traceback}) else: code = self.error['error'].get('code', 'Unknown') return _('ERROR(%(code)s): %(message)s') % {'code': code, 'message': message}
def get_parser(self, prog_name): parser = super(ClusterPolicyShow, self).get_parser(prog_name) parser.add_argument('--policy', metavar='<policy>', required=True, help=_('ID or name of the policy to query on')) parser.add_argument('cluster', metavar='<cluster>', help=_('ID or name of the cluster to query on')) return parser
def get_parser(self, prog_name): parser = super(ScaleOutCluster, self).get_parser(prog_name) parser.add_argument( '--count', metavar='<count>', help=_('Number of nodes to be added to the specified cluster')) parser.add_argument('cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on')) return parser
def get_parser(self, prog_name): parser = super(ClusterPolicyDetach, self).get_parser(prog_name) parser.add_argument('--policy', metavar='<policy>', required=True, help=_('ID or name of policy to be detached')) parser.add_argument('cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on')) return parser
def get_parser(self, prog_name): parser = super(CreatePolicy, self).get_parser(prog_name) parser.add_argument('--spec-file', metavar='<spec-file>', required=True, help=_('The spec file used to create the policy')) parser.add_argument('name', metavar='<name>', help=_('Name of the policy to create')) return parser
def get_parser(self, prog_name): parser = super(DeletePolicy, self).get_parser(prog_name) parser.add_argument('policy', metavar='<policy>', nargs='+', help=_('Name or ID of policy(s) to delete')) parser.add_argument('--force', action='store_true', help=_('Skip yes/no prompt (assume yes)')) return parser
def get_parser(self, prog_name): parser = super(ShowNode, self).get_parser(prog_name) parser.add_argument('--details', default=False, action="store_true", help=_('Include physical object details')) parser.add_argument( 'node', metavar='<node>', help=_('Name or ID of the node to show the details for')) return parser
def get_parser(self, prog_name): parser = super(ClusterRun, self).get_parser(prog_name) parser.add_argument( '--port', metavar='<port>', type=int, default=22, help=_('The TCP port to use for SSH connection') ) parser.add_argument( '--address-type', metavar='<address_type>', default='floating', help=_("The type of IP address to use. Possible values include " "'fixed' and 'floating' (the default)") ) parser.add_argument( '--network', metavar='<network>', default='', help=_("The network to use for SSH connection") ) parser.add_argument( '--ipv6', action="store_true", default=False, help=_("Whether the IPv6 address should be used for SSH. Default " "to use IPv4 address.") ) parser.add_argument( '--user', metavar='<user>', default='root', help=_("The login name to use for SSH connection. Default to " "'root'.") ) parser.add_argument( '--identity-file', metavar='<identity_file>', help=_("The private key file to use, same as the '-i' SSH option") ) parser.add_argument( '--ssh-options', metavar='<ssh_options>', default="", help=_("Extra options to pass to SSH. See: man ssh.") ) parser.add_argument( '--script', metavar='<script>', required=True, help=_("Path name of the script file to run") ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('ID or name of cluster(s) to operate on.') ) return parser
def get_parser(self, prog_name): parser = super(ClusterNodeDel, self).get_parser(prog_name) parser.add_argument( '--nodes', metavar='<nodes>', required=True, help=_('Name or ID of nodes to be deleted; multiple nodes can be ' 'separated with ","')) parser.add_argument('cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on')) return parser
def get_parser(self, prog_name): parser = super(UpdatePolicy, self).get_parser(prog_name) parser.add_argument( '--name', metavar='<name>', help=_('New name of the policy to be updated') ) parser.add_argument( 'policy', metavar='<policy>', help=_('Name or ID of the policy to be updated') ) return parser
def get_parser(self, prog_name): parser = super(ScaleOutCluster, self).get_parser(prog_name) parser.add_argument( '--count', metavar='<count>', help=_('Number of nodes to be added to the specified cluster') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on') ) return parser
def get_parser(self, prog_name): parser = super(RecoverNode, self).get_parser(prog_name) parser.add_argument( '--check', metavar='<boolean>', default=False, help=_('Whether the node(s) should check physical resource status ' 'before doing node recover. Default is false')) parser.add_argument('node', metavar='<node>', nargs='+', help=_('ID or name of node(s) to recover.')) return parser
def get_parser(self, prog_name): parser = super(DeleteNode, self).get_parser(prog_name) parser.add_argument('node', metavar='<node>', nargs='+', help=_('Name or ID of node(s) to delete.')) parser.add_argument('--force-delete', action='store_true', help=_('Force to delete node(s).')) parser.add_argument('--force', action='store_true', help=_('Skip yes/no prompt (assume yes).')) return parser
def test_do_profile_create(self, mock_get, mock_proc, mock_format, mock_show): args = copy.deepcopy(self.profile_args) args = self._make_args(args) spec = copy.deepcopy(self.profile_spec) mock_get.return_value = spec stack_properties = mock.Mock() mock_proc.return_value = stack_properties mock_format.return_value = {'user': '******'} params = { 'name': 'stack_spec', 'spec': spec, 'metadata': {'user': '******'}, } service = mock.Mock() profile = mock.Mock() profile_id = mock.Mock() profile.id = profile_id service.create_profile.return_value = profile sh.do_profile_create(service, args) mock_get.assert_called_once_with(args.spec_file) mock_proc.assert_called_once_with(self.profile_spec['properties']) mock_format.assert_called_once_with(args.metadata) service.create_profile.assert_called_once_with(**params) mock_show.assert_called_once_with(service, profile_id) # Miss 'type' key in spec file del spec['type'] ex = self.assertRaises(exc.CommandError, sh.do_profile_create, service, args) self.assertEqual(_("Missing 'type' key in spec file."), six.text_type(ex)) # Miss 'version' key in spec file spec['type'] = 'os.heat.stack' del spec['version'] ex = self.assertRaises(exc.CommandError, sh.do_profile_create, service, args) self.assertEqual(_("Missing 'version' key in spec file."), six.text_type(ex)) # Miss 'properties' key in spec file spec['version'] = 1.0 del spec['properties'] ex = self.assertRaises(exc.CommandError, sh.do_profile_create, service, args) self.assertEqual(_("Missing 'properties' key in spec file."), six.text_type(ex))
def take_action(self, args): self.log.debug("take_action(%s)", args) service = self.app.client_manager.clustering if '@' in args.cluster: user, cluster = args.cluster.split('@', 1) args.user = user args.cluster = cluster try: attributes = service.collect_cluster_attrs(args.cluster, 'details') except sdk_exc.ResourceNotFound: raise exc.CommandError(_("Cluster not found: %s") % args.cluster) script = None try: f = open(args.script, 'r') script = f.read() except Exception: raise exc.CommandError(_("Could not open script file: %s") % args.script) tasks = dict() for attr in attributes: node_id = attr.node_id addr = attr.attr_value['addresses'] output = dict() th = threading.Thread( target=self._run_script, args=(node_id, addr, args.network, args.address_type, args.port, args.user, args.ipv6, args.identity_file, script, args.ssh_options), kwargs={'output': output}) th.start() tasks[th] = (node_id, output) for t in tasks: t.join() for t in tasks: node_id, result = tasks[t] print("node: %s" % node_id) print("status: %s" % result.get('status')) if "reason" in result: print("reason: %s" % result.get('reason')) if "output" in result: print("output:\n%s" % result.get('output')) if "error" in result: print("error:\n%s" % result.get('error'))
def get_parser(self, prog_name): parser = super(ClusterPolicyDetach, self).get_parser(prog_name) parser.add_argument( '--policy', metavar='<policy>', required=True, help=_('ID or name of policy to be detached') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on') ) return parser
def get_parser(self, prog_name): parser = super(ShowNode, self).get_parser(prog_name) parser.add_argument( '--details', default=False, action="store_true", help=_('Include physical object details') ) parser.add_argument( 'node', metavar='<node>', help=_('Name or ID of the node to show the details for') ) return parser
def get_parser(self, prog_name): parser = super(ClusterPolicyShow, self).get_parser(prog_name) parser.add_argument( '--policy', metavar='<policy>', required=True, help=_('ID or name of the policy to query on') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('ID or name of the cluster to query on') ) return parser
def get_parser(self, prog_name): parser = super(DeleteNode, self).get_parser(prog_name) parser.add_argument( 'node', metavar='<node>', nargs='+', help=_('Name or ID of node(s) to delete') ) parser.add_argument( '--force', action='store_true', help=_('Skip yes/no prompt (assume yes)') ) return parser
def get_parser(self, prog_name): parser = super(ClusterPolicyUpdate, self).get_parser(prog_name) parser.add_argument('--policy', metavar='<policy>', required=True, help=_('ID or name of policy to be updated')) parser.add_argument('--enabled', metavar='<boolean>', required=True, help=_('Whether the policy should be enabled')) parser.add_argument('cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on')) return parser
def get_parser(self, prog_name): parser = super(CreatePolicy, self).get_parser(prog_name) parser.add_argument( '--spec-file', metavar='<spec_file>', required=True, help=_('The spec file used to create the policy') ) parser.add_argument( 'name', metavar='<name>', help=_('Name of the policy to create') ) return parser
def test_do_profile_create(self, mock_get, mock_proc, mock_format, mock_show): args = copy.deepcopy(self.profile_args) args = self._make_args(args) spec = copy.deepcopy(self.profile_spec) mock_get.return_value = spec stack_properties = mock.Mock() mock_proc.return_value = stack_properties mock_format.return_value = {'user': '******'} params = { 'name': 'stack_spec', 'spec': spec, 'metadata': { 'user': '******' }, } service = mock.Mock() profile = mock.Mock() profile_id = mock.Mock() profile.id = profile_id service.create_profile.return_value = profile sh.do_profile_create(service, args) mock_get.assert_called_once_with(args.spec_file) mock_proc.assert_called_once_with(self.profile_spec['properties']) mock_format.assert_called_once_with(args.metadata) service.create_profile.assert_called_once_with(**params) mock_show.assert_called_once_with(service, profile_id) # Miss 'type' key in spec file del spec['type'] ex = self.assertRaises(exc.CommandError, sh.do_profile_create, service, args) self.assertEqual(_("Missing 'type' key in spec file."), six.text_type(ex)) # Miss 'version' key in spec file spec['type'] = 'os.heat.stack' del spec['version'] ex = self.assertRaises(exc.CommandError, sh.do_profile_create, service, args) self.assertEqual(_("Missing 'version' key in spec file."), six.text_type(ex)) # Miss 'properties' key in spec file spec['version'] = 1.0 del spec['properties'] ex = self.assertRaises(exc.CommandError, sh.do_profile_create, service, args) self.assertEqual(_("Missing 'properties' key in spec file."), six.text_type(ex))
def get_parser(self, prog_name): parser = super(ClusterNodeDel, self).get_parser(prog_name) parser.add_argument( '--nodes', metavar='<nodes>', required=True, help=_('Name or ID of nodes to be deleted; multiple nodes can be ' 'separated with ","') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on') ) return parser
def do_cluster_node_list(service, args): """List nodes from cluster.""" queries = { 'cluster_id': args.id, 'limit': args.limit, 'marker': args.marker, } if args.filters: queries.update(utils.format_parameters(args.filters)) try: nodes = service.nodes(**queries) except exc.HTTPNotFound: msg = _('No node matching criteria is found') raise exc.CommandError(msg) if not args.full_id: formatters = { 'id': lambda x: x.id[:8], 'physical_id': lambda x: x.physical_id[:8] if x.physical_id else '' } else: formatters = {} fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_at'] utils.print_list(nodes, fields, formatters=formatters, sortby_index=5)
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) senlin_client = self.app.client_manager.clustering try: if not parsed_args.force and sys.stdin.isatty(): sys.stdout.write( _("Are you sure you want to delete this cluster(s)" " [y/N]?")) prompt_response = sys.stdin.readline().lower() if not prompt_response.startswith('y'): return except KeyboardInterrupt: # Ctrl-c self.log.info('Ctrl-c detected.') return except EOFError: # Ctrl-d self.log.info('Ctrl-d detected') return result = {} for cid in parsed_args.cluster: try: cluster = senlin_client.delete_cluster( cid, False, parsed_args.force_delete) result[cid] = ('OK', cluster.location.split('/')[-1]) except Exception as ex: result[cid] = ('ERROR', six.text_type(ex)) for rid, res in result.items(): senlin_utils.print_action_result(rid, res)
def test_do_cluster_node_list(self, mock_print): service = mock.Mock() args = { 'id': 'cluster_id', 'limit': 20, 'marker': 'marker_id', 'filters': ['status=ACTIVE'], } queries = copy.deepcopy(args) queries['cluster_id'] = args['id'] del queries['id'] del queries['filters'] queries['status'] = 'ACTIVE' args = self._make_args(args) args.full_id = True nodes = mock.Mock() service.nodes.return_value = nodes formatters = {} fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_at'] sh.do_cluster_node_list(service, args) service.nodes.assert_called_once_with(**queries) mock_print.assert_called_once_with(nodes, fields, formatters=formatters, sortby_index=5) # node not found service.nodes.side_effect = exc.HTTPNotFound ex = self.assertRaises(exc.CommandError, sh.do_cluster_node_list, service, args) msg = _('No node matching criteria is found') self.assertEqual(msg, six.text_type(ex))
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 format_parameters(params, parse_semicolon=True): """Reformat parameters into dict of format expected by the API.""" if not params or 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 format_output(output, format='yaml'): fmt = format.lower() try: return supported_formats[fmt](output) except KeyError: raise exc.HTTPUnsupported(_('The format(%s) is unsupported.') % fmt)
def get_parser(self, prog_name): parser = super(ResizeCluster, self).get_parser(prog_name) parser.add_argument( '--capacity', metavar='<capacity>', type=int, help=_('The desired number of nodes of the cluster') ) parser.add_argument( '--adjustment', metavar='<adjustment>', type=int, help=_('A positive integer meaning the number of nodes to add, ' 'or a negative integer indicating the number of nodes to ' 'remove') ) parser.add_argument( '--percentage', metavar='<percentage>', type=float, help=_('A value that is interpreted as the percentage of size ' 'adjustment. This value can be positive or negative') ) parser.add_argument( '--min-step', metavar='<min_step>', type=int, help=_('An integer specifying the number of nodes for adjustment ' 'when <PERCENTAGE> is specified') ) parser.add_argument( '--strict', action='store_true', default=False, help=_('A boolean specifying whether the resize should be ' 'performed on a best-effort basis when the new capacity ' 'may go beyond size constraints') ) parser.add_argument( '--min-size', metavar='min', type=int, help=_('New lower bound of cluster size') ) parser.add_argument( '--max-size', metavar='max', type=int, help=_('New upper bound of cluster size. A value of -1 indicates ' 'no upper limit on cluster size') ) parser.add_argument( 'cluster', metavar='<cluster>', help=_('Name or ID of cluster to operate on') ) return parser
def get_parser(self, prog_name): parser = super(ShowPolicy, self).get_parser(prog_name) parser.add_argument( 'policy', metavar='<policy>', help=_('Name or Id of the policy to show') ) return parser
def test_format_parameter_bad_format(self): params = ['status:ACTIVE;name:cluster1'] ex = self.assertRaises(exc.CommandError, utils.format_parameters, params) msg = _('Malformed parameter(status:ACTIVE). ' 'Use the key=value format.') self.assertEqual(msg, six.text_type(ex))
def get_parser(self, prog_name): parser = super(ShowReceiver, self).get_parser(prog_name) parser.add_argument( 'receiver', metavar='<receiver>', help=_('Name or ID of the receiver to show') ) return parser
def get_parser(self, prog_name): parser = super(ShowAction, self).get_parser(prog_name) parser.add_argument( 'action', metavar='<action>', help=_('Name or ID of the action to show the details for') ) return parser