def take_action(self, parsed_args): masakari_client = self.app.client_manager.ha uuid = masakariclient_utils.get_uuid_by_name(masakari_client, parsed_args.segment) attrs = { 'name': parsed_args.name, 'description': parsed_args.description, 'recovery_method': parsed_args.recovery_method, 'service_type': parsed_args.service_type, } if masakari_client.default_microversion: api_version = api_versions.APIVersion( masakari_client.default_microversion) if (api_version >= api_versions.APIVersion("1.2") and parsed_args.is_enabled is not None): attrs['is_enabled'] = strutils.bool_from_string( parsed_args.is_enabled, strict=True) # Remove not specified keys attrs = masakariclient_utils.remove_unspecified_items(attrs) try: masakari_client.update_segment(segment=uuid, **attrs) # Reraise. To unify exceptions with other functions. except sdk_exc.NotFoundException: LOG.debug(_("Segment is not found: %s"), parsed_args) raise sdk_exc.ResourceNotFound(_('No Segment found for %s') % uuid) except Exception as ex: LOG.debug(_("Failed to update segment: %s"), parsed_args) raise ex return _show_segment(masakari_client, uuid)
def get_parser(self, prog_name): parser = super(ListNotification, self).get_parser(prog_name) parser.add_argument( '--limit', metavar='<limit>', help=_('Limit the number of notifications returned') ) parser.add_argument( '--marker', metavar='<id>', help=_('Only return notifications that appear after the given ' 'notification ID') ) 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: ['type', 'created_at', 'updated_at']") ) parser.add_argument( '--filters', metavar='<"key1=value1;key2=value2...">', help=_("Filter parameters to apply on returned notifications. " "This can be specified multiple times, or once with " "parameters separated by a semicolon. The valid filter " "keys are: ['source_host_uuid', 'type', 'status', " "generated-since]"), action='append' ) return parser
def take_action(self, parsed_args): masakari_client = self.app.client_manager.ha uuid = masakariclient_utils.get_uuid_by_name(masakari_client, parsed_args.segment) attrs = { 'name': parsed_args.name, 'description': parsed_args.description, 'recovery_method': parsed_args.recovery_method, 'service_type': parsed_args.service_type, } # Remove not specified keys attrs = masakariclient_utils.remove_unspecified_items(attrs) try: masakari_client.update_segment(segment=uuid, **attrs) # Reraise. To unify exceptions with other functions. except sdk_exc.NotFoundException: LOG.debug(_("Segment is not found: %s"), parsed_args) raise sdk_exc.ResourceNotFound(_('No Segment found for %s') % uuid) except Exception as ex: LOG.debug(_("Failed to update segment: %s"), parsed_args) raise ex return _show_segment(masakari_client, uuid)
def take_action(self, parsed_args): masakari_client = self.app.client_manager.ha segment_id = masakariclient_utils.get_uuid_by_name( masakari_client, parsed_args.segment_id) uuid = masakariclient_utils.get_uuid_by_name(masakari_client, parsed_args.host, segment=segment_id) attrs = { 'name': parsed_args.name, 'type': parsed_args.type, 'control_attributes': parsed_args.control_attributes, 'reserved': parsed_args.reserved, 'on_maintenance': parsed_args.on_maintenance, } # Remove not specified keys attrs = masakariclient_utils.remove_unspecified_items(attrs) try: masakari_client.update_host(uuid, segment_id=segment_id, **attrs) except sdk_exc.NotFoundException: # Reraise. To unify exceptions with other functions. LOG.debug(_("Segment host is not found: %s"), parsed_args) raise sdk_exc.ResourceNotFound(_('No Host found for %s') % uuid) except Exception as ex: LOG.debug(_("Failed to update segment host: %s"), parsed_args) raise ex return _show_host(masakari_client, segment_id, uuid)
def get_parser(self, prog_name): parser = super(ListHost, self).get_parser(prog_name) parser.add_argument('segment_id', metavar='<segment_id>', help=_('Name or ID of segment.')) parser.add_argument('--limit', metavar='<limit>', help=_('Limit the number of hosts returned')) parser.add_argument( '--marker', metavar='<id>', help=_('Only return hosts that appear after the given host ID')) 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: ['type', 'name', 'created_at', " "'updated_at']")) parser.add_argument( '--filters', metavar='<"key1=value1;key2=value2...">', help=_("Filter parameters to apply on returned hosts. " "This can be specified multiple times, or once with " "parameters separated by a semicolon. The valid filter " "keys are: ['failover_segment_id', 'type', " "'on_maintenance', 'reserved']"), action='append') return parser
def get_parser(self, prog_name): parser = super(CreateNotification, self).get_parser(prog_name) parser.add_argument( 'type', metavar='<type>', choices=['COMPUTE_HOST', 'VM', 'PROCESS'], help=_('Type of failure. The supported options are: ' 'COMPUTE_HOST, VM, PROCESS.') ) parser.add_argument( 'hostname', metavar='<hostname>', help=_('Hostname of notification.') ) parser.add_argument( 'generated_time', metavar='<generated_time>', help=_('Timestamp for notification. e.g. 2016-01-01T01:00:00.000') ) parser.add_argument( 'payload', metavar='<payload>', help=_('JSON string about failure event.') ) return parser
def test_do_host_delete_with_non_existing_uuid(self, mock_get_uuid_by_name, mock_delete_host): mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid') host_id = self.hosts_vals.get('uuid') expected_msg = _("Host '%(host_id)s' under failover_segment " "'" "%(seg_id)s' " "could not be found") % { 'host_id': host_id, 'seg_id': mock_get_uuid_by_name } def fake_delete_host(host_id, mock_get_uuid_by_name, ignore_missing=False): if not ignore_missing: raise sdk_exc.ResourceNotFound(expected_msg) mock_delete_host.side_effect = fake_delete_host service = mock.Mock() args = mock.Mock() original = sys.stdout sys.stdout = six.StringIO() ms.do_host_delete(service, args) output = sys.stdout.getvalue() sys.stdout = original self.assertIn(expected_msg, output)
def _show_notification(masakari_client, notification_uuid): try: notification = masakari_client.get_notification(notification_uuid) except sdk_exc.ResourceNotFound: raise exceptions.CommandError(_('Notification not found: %s' ) % notification_uuid) formatters = {} columns = [ 'created_at', 'updated_at', 'notification_uuid', 'type', 'status', 'source_host_uuid', 'generated_time', 'payload' ] if masakari_client.default_microversion: api_version = api_versions.APIVersion( masakari_client.default_microversion) if api_version >= api_versions.APIVersion("1.1"): columns.append('recovery_workflow_details') return columns, utils.get_dict_properties(notification.to_dict(), columns, formatters=formatters)
def __init__(self, version_str=None): """Create an API version object. :param version_str: String representation of APIVersionRequest. Correct format is 'X.Y', where 'X' and 'Y' are int values. None value should be used to create Null APIVersionRequest, which is equal to 0.0 """ self.ver_major = 0 self.ver_minor = 0 if version_str is not None: match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0|latest)$", version_str) if match: self.ver_major = int(match.group(1)) if match.group(2) == "latest": # Infinity allows to easily determine latest version and # doesn't require any additional checks in comparison # methods. self.ver_minor = float("inf") else: self.ver_minor = int(match.group(2)) else: msg = _("Invalid format of client version '%s'. " "Expected format 'X.Y', where X is a major part and Y " "is a minor part of version.") % version_str raise exception.UnsupportedVersion(msg)
def matches(self, min_version, max_version): """Matches the version object. Returns whether the version object represents a version greater than or equal to the minimum version and less than or equal to the maximum version. :param min_version: Minimum acceptable version. :param max_version: Maximum acceptable version. :returns: boolean If min_version is null then there is no minimum limit. If max_version is null then there is no maximum limit. If self is null then raise ValueError """ if self.is_null(): raise ValueError(_("Null APIVersion doesn't support 'matches'.")) if max_version.is_null() and min_version.is_null(): return True elif max_version.is_null(): return min_version <= self elif min_version.is_null(): return self <= max_version else: return min_version <= self <= max_version
def format_sort_filter_params(parsed_args): queries = {} limit = parsed_args.limit marker = parsed_args.marker sort = parsed_args.sort if limit: queries['limit'] = limit if marker: queries['marker'] = marker sort_keys = [] sort_dirs = [] if sort: for sort_param in sort.split(','): sort_key, _sep, sort_dir = sort_param.partition(':') if not sort_dir: sort_dir = 'desc' elif sort_dir not in ('asc', 'desc'): raise exc.CommandError(_( 'Unknown sort direction: %s') % sort_dir) sort_keys.append(sort_key) sort_dirs.append(sort_dir) queries['sort_key'] = sort_keys queries['sort_dir'] = sort_dirs if parsed_args.filters: queries.update(_format_parameters(parsed_args.filters)) return queries
def take_action(self, parsed_args): masakari_client = self.app.client_manager.ha attrs = { 'name': parsed_args.name, 'description': parsed_args.description, 'recovery_method': parsed_args.recovery_method, 'service_type': parsed_args.service_type, } if masakari_client.default_microversion: api_version = api_versions.APIVersion( masakari_client.default_microversion) if (api_version >= api_versions.APIVersion("1.2") and parsed_args.is_enabled is not None): attrs['is_enabled'] = strutils.bool_from_string( parsed_args.is_enabled, strict=True) # Remove not specified keys attrs = masakariclient_utils.remove_unspecified_items(attrs) try: segment = masakari_client.create_segment(**attrs) except Exception as ex: LOG.debug(_("Failed to create segment: %s"), parsed_args) raise ex return _show_segment(masakari_client, segment.uuid)
def get_parser(self, prog_name): parser = super(DeleteSegment, self).get_parser(prog_name) parser.add_argument('segment', metavar='<segment>', nargs='+', help=_('Name or ID of segment(s) to delete')) return parser
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 _show_segment(masakari_client, segment_uuid): try: segment = masakari_client.get_segment(segment_uuid) except sdk_exc.ResourceNotFound: raise exceptions.CommandError( _('Segment is not found: %s') % segment_uuid) formatters = {} columns = [ 'created_at', 'updated_at', 'uuid', 'name', 'description', 'id', 'service_type', 'recovery_method', ] if masakari_client.default_microversion: api_version = api_versions.APIVersion( masakari_client.default_microversion) if api_version >= api_versions.APIVersion("1.2"): columns.append('is_enabled') return columns, utils.get_dict_properties(segment.to_dict(), columns, formatters=formatters)
def main(args=None): try: print(_("Deprecated: masakari CLI is deprecated and will be removed " "after Stein is released. Use openstack CLI instead."), file=sys.stderr) if args is None: args = sys.argv[1:] MasakariShell().main(args) except KeyboardInterrupt: print(_("KeyboardInterrupt masakari client"), sys.stderr) return 130 except Exception as e: if '--debug' in args or '-d' in args: raise else: print(encodeutils.safe_encode(six.text_type(e)), sys.stderr) return 1
def get_parser(self, prog_name): parser = super(CreateSegment, self).get_parser(prog_name) parser.add_argument('name', metavar='<name>', help=_('Name of segment.')) parser.add_argument( 'recovery_method', metavar='<recovery_method>', choices=['auto', 'reserved_host', 'auto_priority', 'rh_priority'], help=_('Recovery method of segment. The supported options are: ' 'auto, reserved_host, auto_priority, rh_priority.')) parser.add_argument('service_type', metavar='<service_type>', help=_('Service type of segment.')) parser.add_argument('--description', metavar='<description>', help=_('Description of segment.')) return parser
def add_global_args(parser, version): # GLOBAL ARGUMENTS parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS) parser.add_argument( '--masakari-api-version', action='version', version=version, default=utils.env('MASAKARI_API_VERSION', default='1'), help=_('Version number for Masakari API to use, Default to "1".')) parser.add_argument('-d', '--debug', action='store_true', default=False, help=_('Print debugging output.'))
def get_parser(self, prog_name): parser = super(ShowHost, self).get_parser(prog_name) parser.add_argument('segment_id', metavar='<segment_id>', help=_('Name or ID of segment.')) parser.add_argument( 'host', metavar='<host>', help='Name or ID of the Host', ) return parser
def get_string(self): """Version string representation. Converts object to string representation which if used to create an APIVersion object results in the same version. """ if self.is_null(): raise ValueError( _("Null APIVersion cannot be converted to string.")) elif self.is_latest(): return "%s.%s" % (self.ver_major, "latest") return "%s.%s" % (self.ver_major, self.ver_minor)
def get_parser(self, prog_name): parser = super(UpdateHost, self).get_parser(prog_name) parser.add_argument('segment_id', metavar='<segment_id>', help=_('Name or ID of segment.')) parser.add_argument( 'host', metavar='<host>', help='Name or ID of the Host', ) parser.add_argument('--reserved', metavar='<reserved>', choices=['True', 'False'], help=_( 'Host reservation. The supported options are: ' 'True, False.')) parser.add_argument( '--on_maintenance', metavar='<on_maintenance>', choices=['True', 'False'], help=_('Maintenance status of host. The supported options are: ' 'True, False.')) parser.add_argument('--name', metavar='<name>', help=_('Name of host.')) parser.add_argument('--type', metavar='<type>', help=_('Type of host.')) parser.add_argument('--control_attributes', metavar='<control_attributes>', help=_('Attributes about control.')) return parser
def get_parser(self, prog_name): parser = super(UpdateSegment, self).get_parser(prog_name) parser.add_argument('segment', metavar='<segment>', help=_('Name or ID of the segment to update.')) parser.add_argument('--name', metavar='<name>', help=_('Name of segment.')) parser.add_argument( '--recovery_method', metavar='<recovery_method>', choices=['auto', 'reserved_host', 'auto_priority', 'rh_priority'], help=_('Recovery method of segment. The supported options are: ' 'auto, reserved_host, auto_priority, rh_priority')) parser.add_argument('--service_type', metavar='<service_type>', help=_('Service type of segment.')) parser.add_argument('--is_enabled', metavar='<boolean>', help=_('The enabled flag of this segment. ' 'Supported after microversion 1.2.')) parser.add_argument('--description', metavar='<description>', help=_('Description of segment.')) return parser
def main(args=None): try: if args is None: args = sys.argv[1:] MasakariShell().main(args) except KeyboardInterrupt: print(_("KeyboardInterrupt masakari client"), sys.stderr) return 130 except Exception as e: if '--debug' in args or '-d' in args: raise else: print(encodeutils.safe_encode(six.text_type(e)), sys.stderr) return 1
def take_action(self, parsed_args): masakari_client = self.app.client_manager.ha attrs = { 'name': parsed_args.name, 'description': parsed_args.description, 'recovery_method': parsed_args.recovery_method, 'service_type': parsed_args.service_type, } # Remove not specified keys attrs = masakariclient_utils.remove_unspecified_items(attrs) try: segment = masakari_client.create_segment(**attrs) except Exception as ex: LOG.debug(_("Failed to create segment: %s"), parsed_args) raise ex return _show_segment(masakari_client, segment.uuid)
def take_action(self, parsed_args): masakari_client = self.app.client_manager.ha segment_id = masakariclient_utils.get_uuid_by_name( masakari_client, parsed_args.segment_id) attrs = { 'name': parsed_args.name, 'type': parsed_args.type, 'control_attributes': parsed_args.control_attributes, 'reserved': parsed_args.reserved, 'on_maintenance': parsed_args.on_maintenance, } # Remove not specified keys attrs = masakariclient_utils.remove_unspecified_items(attrs) try: host = masakari_client.create_host(segment_id=segment_id, **attrs) except Exception as ex: LOG.debug(_("Failed to create segment host: %s"), parsed_args) raise ex return _show_host(masakari_client, segment_id, host.uuid)
def _show_segment(masakari_client, segment_uuid): try: segment = masakari_client.get_segment(segment_uuid) except sdk_exc.ResourceNotFound: raise exceptions.CommandError( _('Segment is not found: %s') % segment_uuid) formatters = {} columns = [ 'created_at', 'updated_at', 'uuid', 'name', 'description', 'id', 'service_type', 'recovery_method', ] return columns, utils.get_dict_properties(segment.to_dict(), columns, formatters=formatters)
def _show_notification(masakari_client, notification_uuid): try: notification = masakari_client.get_notification(notification_uuid) except sdk_exc.ResourceNotFound: raise exceptions.CommandError( _('Notification not found: %s') % notification_uuid) formatters = {} columns = [ 'created_at', 'updated_at', 'notification_uuid', 'type', 'status', 'source_host_uuid', 'generated_time', 'payload', ] return columns, utils.get_dict_properties(notification.to_dict(), columns, formatters=formatters)
def _show_host(masakari_client, segment_id, uuid): try: host = masakari_client.get_host(uuid, segment_id=segment_id) except sdk_exc.ResourceNotFound: raise exceptions.CommandError( _('Segment host is not found: %s') % uuid) formatters = {} columns = [ 'created_at', 'updated_at', 'uuid', 'name', 'type', 'control_attributes', 'reserved', 'on_maintenance', 'failover_segment_id', ] return columns, utils.get_dict_properties(host.to_dict(), columns, formatters=formatters)
def test_do_segment_delete_with_non_existing_uuid(self, mock_get_uuid_by_name, mock_delete_segment): mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid') expected_msg = _("No failover segment with " "id (%s)") % mock_get_uuid_by_name def fake_delete_segment(self, mock_get_uuid_by_name, ignore_missing=False): if not ignore_missing: raise sdk_exc.ResourceNotFound(expected_msg) mock_delete_segment.side_effect = fake_delete_segment service = mock.Mock() args = mock.Mock() original = sys.stdout sys.stdout = six.StringIO() ms.do_segment_delete(service, args) output = sys.stdout.getvalue() sys.stdout = original self.assertIn(expected_msg, output)
class MasakariShell(object): def __init__(self): pass def do_bash_completion(self, args): """All of the commands and options to stdout.""" commands = set() options = set() for sc_str, sc in self.subcommands.items(): if sc_str == 'bash_completion' or sc_str == 'bash-completion': continue commands.add(sc_str) for option in list(sc._optionals._option_string_actions): options.add(option) print(' '.join(commands | options)) def _add_bash_completion_subparser(self, subparsers): subparser = subparsers.add_parser('bash_completion', add_help=False, formatter_class=HelpFormatter) subparser.set_defaults(func=self.do_bash_completion) self.subcommands['bash_completion'] = subparser def _get_subcommand_parser(self, base_parser, version): parser = base_parser self.subcommands = {} subparsers = parser.add_subparsers(metavar='<subcommand>') submodule = utils.import_versioned_module(version, 'shell') self._find_actions(subparsers, submodule) self._add_bash_completion_subparser(subparsers) return parser def _find_actions(self, subparsers, actions_module): for attr in (a for a in dir(actions_module) if a.startswith('do_')): 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) for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) self.subcommands[command] = subparser def _setup_masakari_client(self, api_ver, args): """Create masakari client using given args.""" kwargs = { 'auth_plugin': args.auth_plugin or 'password', 'auth_url': args.auth_url, 'project_name': args.project_name or args.tenant_name, 'project_id': args.project_id or args.tenant_id, 'domain_name': args.domain_name, 'domain_id': args.domain_id, 'project_domain_name': args.project_domain_name, 'project_domain_id': args.project_domain_id, 'user_domain_name': args.user_domain_name, 'user_domain_id': args.user_domain_id, 'username': args.username, 'user_id': args.user_id, 'password': args.password, 'verify': args.verify, 'token': args.token, 'trust_id': args.trust_id, 'interface': args.interface, 'region_name': args.region_name, } return masakari_client.Client(api_ver, user_agent=USER_AGENT, **kwargs) def _setup_logging(self, debug): if debug: log_level = logging.DEBUG else: log_level = logging.WARNING log_format = "%(levelname)s (%(module)s) %(message)s" logging.basicConfig(format=log_format, level=log_level) logging.getLogger('iso8601').setLevel(logging.WARNING) logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) def main(self, argv): parser = argparse.ArgumentParser( prog='masakari', description="masakari shell", epilog='Type "masakari help <COMMAND>" for help on a specific ' 'command.', add_help=False, ) # add add arguments cliargs.add_global_args(parser, masakariclient.__version__) cliargs.add_global_identity_args(parser) # parse main arguments (options, args) = parser.parse_known_args(argv) self._setup_logging(options.debug) base_parser = parser api_ver = options.masakari_api_version # add subparser subcommand_parser = self._get_subcommand_parser(base_parser, api_ver) self.parser = subcommand_parser # --help/-h or no arguments if not args and options.help or not argv: self.do_help(options) return 0 args = subcommand_parser.parse_args(argv) sc = self._setup_masakari_client(api_ver, args) # call specified function args.func(sc.service, args) @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()