# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from oslo_serialization import jsonutils from senlinclient.common import exc from senlinclient.common.i18n import _ from senlinclient.common import utils from senlinclient.v1 import models logger = logging.getLogger(__name__) def do_build_info(sc, args): '''Retrieve build information.''' result = sc.get(models.BuildInfo) formatters = { 'api': utils.json_formatter, 'engine': utils.json_formatter, } utils.print_dict(result, formatters=formatters) # PROFILE TYPES def do_profile_type_list(sc, args): '''List the available profile types.''' types = sc.list_short(models.ProfileType, {}) utils.print_list(types, ['name'], sortby_index=0) @utils.arg('profile_type', metavar='', help=_('Profile type to get the details for.')) def do_profile_type_show(sc, args): '''Show the profile type.''' try: params = {'profile_type': args.profile_type} profile_type = sc.get(models.ProfileTypeSchema, params) except exc.HTTPNotFound: raise exc.CommandError( _('Profile Type not found: %s') % args.profile_type) else: print(jsonutils.dumps(profile_type, indent=2)) @utils.arg('profile_type', metavar='', help=_('Profile type to generate a template for.')) @utils.arg('-F', '--format', metavar='', help=_("The template output format, one of: %s.") % ', '.join(utils.supported_formats.keys())) def do_profile_type_schema(sc, args): '''Get the spec of a profile type.''' try: params = {'profile_type': args.profile_type} schema = sc.get(models.ProfileTypeSchema, params) except exc.HTTPNotFound: raise exc.CommandError( _('Profile Type %s not found.') % args.profile_type) schema = dict(schema) if args.format: print(utils.format_output(schema, format=args.format)) else: print(utils.format_output(schema)) # PROFILES @utils.arg('-d', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted profiles if any.')) @utils.arg('-l', '--limit', metavar='', help=_('Limit the number of profiles returned.')) @utils.arg('-m', '--marker', metavar='', help=_('Only return profiles that appear after the given ID.')) @utils.arg('-F', '--full-id', default=False, action="store_true", help=_('Print full IDs in list.')) def do_profile_list(sc, args=None): '''List profiles that meet the criteria.''' def _short_id(obj): return obj.id[:8] + ' ...' fields = ['id', 'name', 'type', 'permission', 'created_time'] queries = { 'show_deleted': args.show_deleted, 'limit': args.limit, 'marker': args.marker, } profiles = sc.list(models.Profile, **queries) formatters = {} if not args.full_id: formatters = { 'id': _short_id, } utils.print_list(profiles, fields, formatters=formatters, sortby_index=1) def _show_profile(sc, profile_id): try: params = {'id': profile_id} profile = sc.get(models.Profile, params) except exc.HTTPNotFound: raise exc.CommandError(_('Profile not found: %s') % profile_id) formatters = { 'tags': utils.json_formatter, } if profile.type == 'os.heat.stack': formatters['spec'] = utils.nested_dict_formatter( ['disable_rollback', 'environment', 'files', 'parameters', 'template', 'timeout'], ['property', 'value']) utils.print_dict(profile.to_dict(), formatters=formatters) @utils.arg('-t', '--profile-type', metavar='', required=True, help=_('Profile type used for this profile.')) @utils.arg('-s', '--spec-file', metavar='', required=True, help=_('The spec file used to create the profile.')) @utils.arg('-p', '--permission', metavar='', default='', help=_('A string format permission for this profile.')) @utils.arg('-g', '--tags', metavar='', help=_('Tag values to be attached to the profile. ' 'This can be specified multiple times, or once with tags ' 'separated by a semicolon.'), action='append') @utils.arg('name', metavar='', help=_('Name of the profile to create.')) def do_profile_create(sc, args): '''Create a profile.''' spec = utils.get_spec_content(args.spec_file) if args.profile_type == 'os.heat.stack': spec = utils.process_stack_spec(spec) params = { 'name': args.name, 'type': args.profile_type, 'spec': spec, 'permission': args.permission, 'tags': utils.format_parameters(args.tags), } profile = sc.create(models.Profile, params) _show_profile(sc, profile.id) @utils.arg('id', metavar='', help=_('Name or ID of profile to show.')) def do_profile_show(sc, args): '''Show the profile details.''' _show_profile(sc, args.id) @utils.arg('-n', '--name', metavar='', help=_('The new name for the profile.')) @utils.arg('-s', '--spec-file', metavar='', help=_('The new spec file for the profile.')) @utils.arg('-p', '--permission', metavar='', default='', help=_('A string format permission for this profile.')) @utils.arg('-g', '--tags', metavar='', help=_('Tag values to be attached to the profile. ' 'This can be specified multiple times, or once with tags ' 'separated by a semicolon.'), action='append') @utils.arg('id', metavar='', help=_('Name or ID of the profile to update.')) def do_profile_update(sc, args): '''Update a profile.''' spec = None if args.spec_file: spec = utils.get_spec_content(args.spec_file) if args.profile_type == 'os.heat.stack': spec = utils.process_stack_spec(spec) params = { 'name': args.name, 'spec': spec, 'permission': args.permission, 'tags': utils.format_parameters(args.tags), } # Find the profile first, we need its id try: profile = sc.get(models.Profile, {'id': args.id}) except exc.HTTPNotFound: raise exc.CommandError(_('Profile not found: %s') % args.id) params['id'] = profile.id sc.update(models.Profile, params) _show_profile(sc, args.id) @utils.arg('-f', '--force', default=False, action="store_true", help=_('Delete the profile completely from database.')) @utils.arg('id', metavar='', nargs='+', help=_('Name or ID of profile(s) to delete.')) def do_profile_delete(sc, args): '''Delete profile(s).''' failure_count = 0 for cid in args.id: try: query = { 'id': cid, 'force': args.force } sc.delete(models.Profile, query) except exc.HTTPNotFound as ex: failure_count += 1 print(ex) if failure_count == len(args.id): msg = _('Failed to delete any of the specified profile(s).') raise exc.CommandError(msg) print('Profile deleted: %s' % args.id) # POLICY TYPES def do_policy_type_list(sc, args): '''List the available policy types.''' types = sc.list_short(models.PolicyType, {}) utils.print_list(types, ['name'], sortby_index=0) @utils.arg('policy_type', metavar='', help=_('Policy type to get the details for.')) def do_policy_type_show(sc, args): '''Show the policy type.''' try: params = {'policy_type': args.policy_type} policy_type = sc.get(models.PolicyTypeSchema, params) except exc.HTTPNotFound: raise exc.CommandError( _('Policy Type not found: %s') % args.policy_type) else: print(jsonutils.dumps(policy_type, indent=2)) @utils.arg('policy_type', metavar='', help=_('Policy type to generate a template for.')) @utils.arg('-F', '--format', metavar='', help=_("The template output format, one of: %s.") % ', '.join(utils.supported_formats.keys())) def do_policy_type_schema(sc, args): '''Get the spec of a policy type.''' try: params = {'policy_type': args.policy_type} schema = sc.get(models.PolicyTypeSchema, params) except exc.HTTPNotFound: raise exc.CommandError( _('Policy type %s not found.') % args.policy_type) schema = dict(schema) if args.format: print(utils.format_output(schema, format=args.format)) else: print(utils.format_output(schema)) # POLICIES @utils.arg('-d', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted policies if any.')) @utils.arg('-l', '--limit', metavar='', help=_('Limit the number of policies returned.')) @utils.arg('-m', '--marker', metavar='', help=_('Only return policies that appear after the given ID.')) @utils.arg('-F', '--full-id', default=False, action="store_true", help=_('Print full IDs in list.')) def do_policy_list(sc, args=None): '''List policies that meet the criteria.''' def _short_id(obj): return obj.id[:8] + ' ...' fields = ['id', 'name', 'type', 'level', 'cooldown', 'created_time'] queries = { 'show_deleted': args.show_deleted, 'limit': args.limit, 'marker': args.marker, } policies = sc.list(models.Policy, **queries) formatters = {} if not args.full_id: formatters = { 'id': _short_id, } utils.print_list(policies, fields, formatters=formatters, sortby_index=1) def _show_policy(sc, policy_id=None, policy=None): if policy is None: try: params = {'id': policy_id} policy = sc.get(models.Policy, params) except exc.HTTPNotFound: raise exc.CommandError(_('Policy not found: %s') % policy_id) formatters = { 'tags': utils.json_formatter, 'spec': utils.json_formatter, } utils.print_dict(policy.to_dict(), formatters=formatters) @utils.arg('-t', '--policy-type', metavar='', required=True, help=_('Policy type used for this policy.')) @utils.arg('-s', '--spec-file', metavar='', required=True, help=_('The spec file used to create the policy.')) @utils.arg('-c', '--cooldown', metavar='', default=0, help=_('An integer indicating the cooldown seconds once the ' 'policy is effected. Default to 0.')) @utils.arg('-l', '--enforcement-level', metavar='', default=0, help=_('An integer beteen 0 and 100 representing the enforcement ' 'level. Default to 0.')) @utils.arg('name', metavar='', help=_('Name of the policy to create.')) def do_policy_create(sc, args): '''Create a policy.''' spec = utils.get_spec_content(args.spec_file) params = { 'name': args.name, 'type': args.policy_type, 'spec': spec, 'cooldown': args.cooldown, 'level': args.enforcement_level, } policy = sc.create(models.Policy, params) _show_policy(sc, policy=policy) @utils.arg('id', metavar='', help=_('Name of the policy to be updated.')) def do_policy_show(sc, args): '''Show the policy details.''' _show_policy(sc, policy_id=args.id) @utils.arg('-c', '--cooldown', metavar='', help=_('An integer indicating the cooldown seconds once the ' 'policy is effected. Default to 0.')) @utils.arg('-l', '--enforcement-level', metavar='', help=_('An integer beteen 0 and 100 representing the enforcement ' 'level. Default to 0.')) @utils.arg('-n', '--name', metavar='', help=_('New name of the policy to be updated.')) @utils.arg('id', metavar='', help=_('Name of the policy to be updated.')) def do_policy_update(sc, args): '''Update a policy.''' params = { 'name': args.name, 'cooldown': args.cooldown, 'level': args.enforcement_level, } policy = sc.get(models.Policy, {'id': args.id}) if policy is not None: params['id'] = policy.id sc.update(models.Policy, params) _show_policy(sc, policy_id=policy.id) @utils.arg('-f', '--force', default=False, action="store_true", help=_('Delete the policy completely from database.')) @utils.arg('id', metavar='', nargs='+', help=_('Name or ID of policy(s) to delete.')) def do_policy_delete(sc, args): '''Delete policy(s).''' failure_count = 0 for cid in args.id: try: query = { 'id': cid, 'force': args.force } sc.delete(models.Policy, query) except exc.HTTPNotFound as ex: failure_count += 1 print(ex) if failure_count == len(args.id): msg = _('Failed to delete any of the specified policy(s).') raise exc.CommandError(msg) print('Policy deleted: %s' % args.id) # CLUSTERS @utils.arg('-s', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted clusters if any.')) @utils.arg('-n', '--show-nested', default=False, action="store_true", help=_('Include nested clusters if any.')) @utils.arg('-f', '--filters', metavar='', help=_('Filter parameters to apply on returned clusters. ' 'This can be specified multiple times, or once with ' 'parameters separated by a semicolon.'), action='append') @utils.arg('-k', '--sort-keys', metavar='', help=_('Name of keys used for sorting the returned events.')) @utils.arg('-d', '--sort-dir', metavar='', help=_('Direction for sorting, where DIR can be "asc" or "desc".')) @utils.arg('-l', '--limit', metavar='', help=_('Limit the number of clusters returned.')) @utils.arg('-m', '--marker', metavar='', help=_('Only return clusters that appear after the given cluster ' 'ID.')) @utils.arg('-F', '--full-id', default=False, action="store_true", help=_('Print full IDs in list.')) def do_cluster_list(sc, args=None): '''List the user's clusters.''' def _short_id(obj): return obj.id[:8] + ' ...' fields = ['id', 'name', 'status', 'created_time'] queries = { 'limit': args.limit, 'marker': args.marker, 'filters': utils.format_parameters(args.filters), 'show_deleted': args.show_deleted, 'show_nested': args.show_nested } if args.show_nested: fields.append('parent') clusters = sc.list(models.Cluster, **queries) formatters = {} if not args.full_id: formatters = { 'id': _short_id, } utils.print_list(clusters, fields, formatters=formatters, sortby_index=3) def _show_cluster(sc, cluster_id): try: query = {'id': cluster_id} cluster = sc.get(models.Cluster, query) except exc.HTTPNotFound: raise exc.CommandError(_('Cluster %s is not found') % cluster_id) formatters = { 'tags': utils.json_formatter, 'nodes': utils.list_formatter, } utils.print_dict(cluster.to_dict(), formatters=formatters) @utils.arg('-p', '--profile', metavar='', required=True, help=_('Profile Id used for this cluster.')) @utils.arg('-n', '--size', metavar='', default=0, help=_('Initial size of the cluster. Default to 0.')) @utils.arg('-o', '--parent', metavar='', help=_('ID of the parent cluster, if exists.')) @utils.arg('-t', '--timeout', metavar='', type=int, help=_('Cluster creation timeout in minutes.')) @utils.arg('-g', '--tags', metavar='', help=_('Tag values to be attached to the cluster. ' 'This can be specified multiple times, or once with tags ' 'separated by a semicolon.'), action='append') @utils.arg('name', metavar='', help=_('Name of the cluster to create.')) def do_cluster_create(sc, args): '''Create the cluster.''' params = { 'name': args.name, 'profile_id': args.profile, 'size': args.size, 'parent': args.parent, 'tags': utils.format_parameters(args.tags), 'timeout': args.timeout } cluster = sc.create(models.Cluster, params) _show_cluster(sc, cluster.id) @utils.arg('id', metavar='', nargs='+', help=_('Name or ID of cluster(s) to delete.')) def do_cluster_delete(sc, args): '''Delete the cluster(s).''' failure_count = 0 for cid in args.id: try: query = {'id': cid} sc.delete(models.Cluster, query) except exc.HTTPNotFound as ex: failure_count += 1 print(ex) if failure_count == len(args.id): msg = _('Failed to delete any of the specified clusters.') raise exc.CommandError(msg) print('Request accepted') @utils.arg('-p', '--profile', metavar='', help=_('ID of new profile to use.')) @utils.arg('-t', '--timeout', metavar='', help=_('New timeout (in minutes) value for the cluster.')) @utils.arg('-r', '--parent', metavar='', help=_('ID of parent cluster for the cluster.')) @utils.arg('-g', '--tags', metavar='', help=_('Tag values to be attached to the cluster. ' 'This can be specified multiple times, or once with tags ' 'separated by a semicolon.'), action='append') @utils.arg('-n', '--name', metavar='', help=_('New name for the cluster to update.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to be updated.')) def do_cluster_update(sc, args): '''Update the cluster.''' cluster = sc.get(models.Cluster, {'id': args.id}) params = { 'id': cluster.id, 'name': args.name, 'profile_id': args.profile, 'parent': args.parent, 'tags': utils.format_parameters(args.tags), 'timeout': args.timeout, } sc.update(models.Cluster, params) _show_cluster(sc, cluster.id) @utils.arg('id', metavar='', help=_('Name or ID of cluster to show.')) def do_cluster_show(sc, args): '''Show details of the cluster.''' _show_cluster(sc, args.id) @utils.arg('-s', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted nodes if any.')) @utils.arg('-f', '--filters', metavar='', help=_('Filter parameters to apply on returned nodes. ' 'This can be specified multiple times, or once with ' 'parameters separated by a semicolon.'), action='append') @utils.arg('-l', '--limit', metavar='', help=_('Limit the number of nodes returned.')) @utils.arg('-m', '--marker', metavar='', help=_('Only return nodes that appear after the given node ID.')) @utils.arg('-F', '--full-id', default=False, action="store_true", help=_('Print full IDs in list.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to nodes from.')) def do_cluster_node_list(sc, args): '''List nodes from cluster.''' def _short_id(obj): return obj.id[:8] + ' ...' def _short_physical_id(obj): return obj.physical_id[:8] + ' ...' query = { 'cluster_id': args.id, 'show_deleted': args.show_deleted, 'filters': utils.format_parameters(args.filters), 'limit': args.limit, 'marker': args.marker, } try: nodes = sc.list(models.Node, **query) except exc.HTTPNotFound: msg = _('No node matching criteria is found') raise exc.CommandError(msg) if not args.full_id: formatters = { 'id': _short_id, 'physical_id': _short_physical_id, } else: formatters = {} fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_time'] utils.print_list(nodes, fields, formatters=formatters, sortby_index=5) @utils.arg('-n', '--nodes', metavar='', required=True, help=_('ID of nodes to be added; multiple nodes can be separated ' 'with ","')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_node_add(sc, args): '''Add specified nodes to cluster.''' node_ids = args.nodes.split(',') params = { 'id': args.id, 'action': 'add_nodes', 'action_args': { 'nodes': node_ids, } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-n', '--nodes', metavar='', required=True, help=_('ID of nodes to be deleted; multiple nodes can be separated' 'with ",".')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_node_del(sc, args): '''Delete specified nodes from cluster.''' node_ids = args.nodes.split(',') params = { 'id': args.id, 'action': 'del_nodes', 'action_args': { 'nodes': node_ids, } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-c', '--count', metavar='', help=_('Number of nodes to be added.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_scale_out(sc, args): '''Scale out a cluster by the specified number of nodes.''' params = { 'id': args.id, 'action': 'scale_out', 'action_args': { 'count': args.count } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-c', '--count', metavar='', help=_('Number of nodes to be added.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_scale_in(sc, args): '''Scale in a cluster by the specified number of nodes.''' if args.count is not None: action_args = {'count': args.count} else: action_args = {} params = { 'id': args.id, 'action': 'scale_in', 'action_args': action_args, } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-f', '--filters', metavar='', help=_('Filter parameters to apply on returned results. ' 'This can be specified multiple times, or once with ' 'parameters separated by a semicolon.'), action='append') @utils.arg('-k', '--sort-keys', metavar='', help=_('Name of keys used for sorting the returned events.')) @utils.arg('-d', '--sort-dir', metavar='', help=_('Direction for sorting, where DIR can be "asc" or "desc".')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to query on.')) def do_cluster_policy_list(sc, args): '''List policies from cluster.''' query = {'id': args.id} cluster = sc.get(models.Cluster, query) queries = { 'filters': utils.format_parameters(args.filters), 'sort_keys': args.sort_keys, 'sort_dir': args.sort_dir, } policies = sc.list(models.ClusterPolicy, path_args={'cluster_id': cluster.id}, **queries) fields = ['policy_id', 'policy', 'type', 'priority', 'level', 'cooldown', 'enabled'] utils.print_list(policies, fields, sortby_index=3) @utils.arg('-p', '--policy', metavar='', required=True, help=_('ID or name of the policy to query on.')) @utils.arg('id', metavar='', help=_('ID or name of the cluster to query on.')) def do_cluster_policy_show(sc, args): '''Show a specific policy that is bound to the specified cluster.''' queries = { 'cluster_id': args.id, 'policy_id': args.policy } binding = sc.get(models.ClusterPolicy, queries) utils.print_dict(binding.to_dict()) @utils.arg('-p', '--policy', metavar='', required=True, help=_('ID or name of policy to be attached.')) @utils.arg('-r', '--priority', metavar='', default=50, help=_('An integer specifying the relative priority among ' 'all policies attached to a cluster. The lower the ' 'value, the higher the priority. Default is 50.')) @utils.arg('-l', '--enforcement-level', metavar='', default=50, help=_('An integer beteen 0 and 100 representing the enforcement ' 'level. Default to enforcement level of policy.')) @utils.arg('-c', '--cooldown', metavar='', default=0, help=_('An integer indicating the cooldown seconds once the ' 'policy is effected. Default to cooldown of policy.')) @utils.arg('-e', '--enabled', default=True, action="store_true", help=_('Whether the policy should be enabled once attached. ' 'Default to enabled.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_attach(sc, args): '''Attach policy to cluster.''' params = { 'id': args.id, 'action': 'policy_attach', 'action_args': { 'policy_id': args.policy, 'priority': args.priority, 'level': args.enforcement_level, 'cooldown': args.cooldown, 'enabled': args.enabled, } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-p', '--policy', metavar='', required=True, help=_('ID or name of policy to be detached.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_detach(sc, args): '''Detach policy from cluster.''' params = { 'id': args.id, 'action': 'policy_detach', 'action_args': { 'policy_id': args.policy, } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-p', '--policy', metavar='', required=True, help=_('ID or name of policy to be updated.')) @utils.arg('-r', '--priority', metavar='', help=_('An integer specifying the relative priority among ' 'all policies attached to a cluster. The lower the ' 'value, the higher the priority. Default is 50.')) @utils.arg('-l', '--enforcement-level', metavar='', help=_('New enforcement level.')) @utils.arg('-c', '--cooldown', metavar='', help=_('Cooldown interval in seconds.')) @utils.arg('-e', '--enabled', metavar='', help=_('Whether the policy should be enabled.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_update(sc, args): '''Update a policy on cluster.''' params = { 'id': args.id, 'action': 'policy_update', 'action_args': { 'policy_id': args.policy, 'priority': args.priority, 'level': args.enforcement_level, 'cooldown': args.cooldown, 'enabled': args.enabled, } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-p', '--policy', metavar='', required=True, help=_('ID or name of policy to be enabled.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_enable(sc, args): '''Enable a policy on cluster.''' params = { 'id': args.id, 'action': 'policy_enable', 'action_args': { 'policy_id': args.policy, } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) @utils.arg('-p', '--policy', metavar='', required=True, help=_('ID or name of policy to be disabled.')) @utils.arg('id', metavar='', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_disable(sc, args): '''Disable a policy on a cluster.''' params = { 'id': args.id, 'action': 'policy_disable', 'action_args': { 'policy_id': args.policy, } } resp = sc.action(models.Cluster, params) print('Request accepted by action %s' % resp['action']) # NODES @utils.arg('-c', '--cluster', default=None, help=_('ID or name of cluster for nodes to list.')) @utils.arg('-s', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted nodes if any.')) @utils.arg('-f', '--filters', metavar='', help=_('Filter parameters to apply on returned nodes. ' 'This can be specified multiple times, or once with ' 'parameters separated by a semicolon.'), action='append') @utils.arg('-k', '--sort-keys', metavar='', help=_('Name of keys used for sorting the returned events.')) @utils.arg('-d', '--sort-dir', metavar='', help=_('Direction for sorting, where DIR can be "asc" or "desc".')) @utils.arg('-l', '--limit', metavar='', help=_('Limit the number of nodes returned.')) @utils.arg('-m', '--marker', metavar='', help=_('Only return nodes that appear after the given node ID.')) @utils.arg('-g', '--global-tenant', default=False, action="store_true", help=_('Indicate that this node list should include nodes from ' 'all tenants. This option is subject to access policy ' 'checking. Default is False.')) @utils.arg('-F', '--full-id', default=False, action="store_true", help=_('Print full IDs in list.')) def do_node_list(sc, args): '''Show list of nodes.''' def _short_id(obj): return obj.id[:8] + ' ...' def _short_cluster_id(obj): return obj.cluster_id[:8] + ' ...' if obj.cluster_id else '' def _short_physical_id(obj): return obj.physical_id[:8] + ' ...' if obj.physical_id else '' fields = ['id', 'name', 'status', 'cluster_id', 'physical_id', 'profile_name', 'created_time', 'updated_time'] queries = { 'show_deleted': args.show_deleted, 'cluster_id': args.cluster, 'filters': utils.format_parameters(args.filters), 'sort_keys': args.sort_keys, 'sort_dir': args.sort_dir, 'limit': args.limit, 'marker': args.marker, 'global_tenant': args.global_tenant, } if args.show_deleted: fields.append('deleted_time') nodes = sc.list(models.Node, **queries) if not args.full_id: formatters = { 'id': _short_id, 'cluster_id': _short_cluster_id, 'physical_id': _short_physical_id, } else: formatters = {} utils.print_list(nodes, fields, formatters=formatters, sortby_index=6) def _show_node(sc, node_id): '''Show detailed info about the specified node.''' try: query = {'id': node_id} node = sc.get(models.Node, query) except exc.HTTPNotFound: msg = _('Node %s is not found') % node_id raise exc.CommandError(msg) formatters = { 'tags': utils.json_formatter, 'data': utils.json_formatter, } utils.print_dict(node.to_dict(), formatters=formatters) @utils.arg('-p', '--profile', metavar='', required=True, help=_('Profile Id used for this node.')) @utils.arg('-c', '--cluster', metavar='', help=_('Cluster Id for this node.')) @utils.arg('-r', '--role', metavar='', help=_('Role for this node in the specific cluster.')) @utils.arg('-g', '--tags', metavar='', help=_('Tag values to be attached to the cluster. ' 'This can be specified multiple times, or once with tags ' 'separated by a semicolon.'), action='append') @utils.arg('name', metavar='', help=_('Name of the node to create.')) def do_node_create(sc, args): '''Create the node.''' params = { 'name': args.name, 'cluster_id': args.cluster, 'profile_id': args.profile, 'role': args.role, 'tags': utils.format_parameters(args.tags), } node = sc.create(models.Node, params) _show_node(sc, node.id) @utils.arg('id', metavar='', help=_('Name or ID of the node to show the details for.')) def do_node_show(sc, args): '''Show detailed info about the specified node.''' _show_node(sc, args.id) @utils.arg('id', metavar='', nargs='+', help=_('Name or ID of node(s) to delete.')) def do_node_delete(sc, args): '''Delete the node(s).''' failure_count = 0 for nid in args.id: try: query = {'id': nid} sc.delete(models.Node, query) except exc.HTTPNotFound: failure_count += 1 print('Node id "%s" not found' % nid) if failure_count == len(args.id): msg = _('Failed to delete any of the specified nodes.') raise exc.CommandError(msg) print('Request accepted') @utils.arg('-n', '--name', metavar='', help=_('New name for the node.')) @utils.arg('-p', '--profile', metavar='', help=_('ID of new profile to use.')) @utils.arg('-r', '--role', metavar='', help=_('Role for this node in the specific cluster.')) @utils.arg('-g', '--tags', metavar='', help=_('Tag values to be attached to the node. ' 'This can be specified multiple times, or once with tags ' 'separated by a semicolon.'), action='append') @utils.arg('id', metavar='', help=_('Name or ID of node to update.')) def do_node_update(sc, args): '''Update the node.''' # Find the node first, we need its UUID try: node = sc.get(models.Node, {'id': args.id}) except exc.HTTPNotFound: raise exc.CommandError(_('Node not found: %s') % args.id) params = { 'id': node.id, 'name': args.name, 'role': args.role, 'profile': args.profile, 'tags': utils.format_parameters(args.tags), } sc.update(models.Node, params) _show_node(sc, node.id) @utils.arg('-c', '--cluster', required=True, help=_('ID or name of cluster for node to join.')) @utils.arg('id', metavar='', help=_('Name or ID of node to operate on.')) def do_node_join(sc, args): '''Make node join the specified cluster.''' params = { 'id': args.id, 'action': 'join', 'action_args': { 'cluster_id': args.cluster, } } resp = sc.action(models.Node, params) print('Request accepted by action %s' % resp['action']) _show_node(sc, args.id) @utils.arg('id', metavar='', help=_('Name or ID of node to operate on.')) def do_node_leave(sc, args): '''Make node leave its current cluster.''' params = { 'id': args.id, 'action': 'leave', } resp = sc.action(models.Node, params) print('Request accepted by action %s' % resp['action']) _show_node(sc, args.id) # EVENTS @utils.arg('-f', '--filters', metavar='', help=_('Filter parameters to apply on returned events. ' 'This can be specified multiple times, or once with ' 'parameters separated by a semicolon.'), action='append') @utils.arg('-l', '--limit', metavar='', help=_('Limit the number of events returned.')) @utils.arg('-m', '--marker', metavar='', help=_('Only return events that appear after the given event ID.')) @utils.arg('-k', '--sort-keys', metavar='', help=_('Name of keys used for sorting the returned events.')) @utils.arg('-d', '--sort-dir', metavar='', help=_('Direction for sorting, where DIR can be "asc" or "desc".')) @utils.arg('-g', '--global-tenant', default=False, action="store_true", help=_('Whether events from all projects(tenants) should be ' 'listed. Default to False. Setting this to True may demand ' 'for an admin privilege.')) @utils.arg('-D', '--show-deleted', default=False, action="store_true", help=_('Whether deleted events should be listed as well. ' 'Default to False.')) def do_event_list(sc, args): '''List events.''' queries = { 'filters': utils.format_parameters(args.filters), 'sort_keys': args.sort_keys, 'sort_dir': args.sort_dir, 'limit': args.limit, 'marker': args.marker, 'global_tenant': args.global_tenant, 'show_deleted': args.show_deleted, } try: events = sc.list(models.Event, **queries) except exc.HTTPNotFound as ex: raise exc.CommandError(str(ex)) fields = ['id', 'timestamp', 'obj_type', 'obj_id', 'action', 'status', 'status_reason'] utils.print_list(events, fields, sortby_index=0) @utils.arg('event', metavar='', help=_('ID of event to display details for.')) def do_event_show(sc, args): '''Describe the event.''' try: query = {'id': args.event} event = sc.get(models.Event, query) except exc.HTTPNotFound as ex: raise exc.CommandError(str(ex)) utils.print_dict(event.to_dict()) # ACTIONS @utils.arg('-f', '--filters', metavar='', help=_('Filter parameters to apply on returned actions. ' 'This can be specified multiple times, or once with ' 'parameters separated by a semicolon.'), action='append') @utils.arg('-k', '--sort-keys', metavar='', help=_('Name of keys used for sorting the returned events.')) @utils.arg('-d', '--sort-dir', metavar='', help=_('Direction for sorting, where DIR can be "asc" or "desc".')) @utils.arg('-l', '--limit', metavar='', help=_('Limit the number of nodes returned.')) @utils.arg('-m', '--marker', metavar='', help=_('Only return nodes that appear after the given node ID.')) @utils.arg('-s', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted nodes if any.')) @utils.arg('-F', '--full-id', default=False, action="store_true", help=_('Print full IDs in list.')) def do_action_list(sc, args): '''List actions.''' def _short_id(obj): return obj.id[:8] + ' ...' def _short_target(obj): return obj.target[:8] + ' ...' queries = { 'show_deleted': args.show_deleted, 'filters': utils.format_parameters(args.filters), 'sort_keys': args.sort_keys, 'sort_dir': args.sort_dir, 'limit': args.limit, 'marker': args.marker, } actions = sc.list(models.Action, **queries) fields = ['id', 'name', 'action', 'status', 'target', 'depends_on', 'depended_by'] if not args.full_id: formatters = { 'id': _short_id, 'target': _short_target, } else: formatters = {} utils.print_list(actions, fields, formatters=formatters, sortby_index=0) @utils.arg('id', metavar='', help=_('Name or ID of the action to show the details for.')) def do_action_show(sc, args): '''Show detailed info about the specified action.''' try: query = {'id': args.id} action = sc.get(models.Action, query) except exc.HTTPNotFound: msg = _('Action %(id)s is not found') % args.id raise exc.CommandError(msg) formatters = { 'inputs': utils.json_formatter, 'outputs': utils.json_formatter, 'tags': utils.json_formatter, 'data': utils.json_formatter, } utils.print_dict(action.to_dict(), formatters=formatters)