# 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 logger = logging.getLogger(__name__) def do_build_info(sc, args): '''Retrieve build information.''' result = sc.build_info.build_info() 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.profile_types.list() utils.print_list(types, ['profile_type'], sortby_index=0) @utils.arg('profile_type', metavar='<PROFILE_TYPE>', help=_('Profile type to get the details for.')) def do_profile_type_show(sc, args): '''Show the profile type.''' try: profile_type = sc.profile_types.get(args.profile_type) 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='<PROFILE_TYPE>', help=_('Profile type to generate a template for.')) @utils.arg('-F', '--format', metavar='<FORMAT>', help=_("The template output format, one of: %s.") % ', '.join(utils.supported_formats.keys())) def do_profile_type_template(sc, args): '''Generate a template based on a profile type.''' try: template = sc.profile_types.generate_template(args.profile_type) except exc.HTTPNotFound: raise exc.CommandError( _('Profile Type %s not found.') % args.profile_type) if args.format: print(utils.format_output(template, format=args.format)) else: print(utils.format_output(template)) #### PROFILES #### POLICY TYPES def do_policy_type_list(sc, args): '''List the available policy types.''' types = sc.policy_types.list() utils.print_list(types, ['policy_type'], sortby_index=0) @utils.arg('policy_type', metavar='<POLICY_TYPE>', help=_('Policy type to get the details for.')) def do_policy_type_show(sc, args): '''Show the policy type.''' try: policy_type = sc.policy_types.get(args.policy_type) 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='<POLICY_TYPE>', help=_('Policy type to generate a template for.')) @utils.arg('-F', '--format', metavar='<FORMAT>', help=_("The template output format, one of: %s.") % ', '.join(utils.supported_formats.keys())) def do_policy_type_template(sc, args): '''Generate a template based on a policy type.''' try: template = sc.policy_types.generate_template(args.policy_type) except exc.HTTPNotFound: raise exc.CommandError( _('Policy Type %s not found.') % args.policy_type) if args.format: print(utils.format_output(template, format=args.format)) else: print(utils.format_output(template)) #### POLICIES #### 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='<KEY1=VALUE1;KEY2=VALUE2...>', 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('-l', '--limit', metavar='<LIMIT>', help=_('Limit the number of clusters returned.')) @utils.arg('-m', '--marker', metavar='<ID>', help=_('Only return clusters that appear after the given cluster ' 'ID.')) def do_cluster_list(sc, args=None): '''List the user's clusters.''' kwargs = {} fields = ['id', 'cluster_name', 'status', 'created_time'] if args: kwargs = {'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') # if args.global_tenant: # fields.insert(2, 'project') clusters = sc.clusters.list(**kwargs) utils.print_list(clusters, fields, sortby_index=3) @utils.arg('-p', '--profile', metavar='<PROFILE ID>', help=_('Profile Id used for this cluster.')) @utils.arg('-n', '--size', metavar='<NUMBER>', help=_('Initial size of the cluster.')) @utils.arg('-t', '--timeout', metavar='<TIMEOUT>', type=int, help=_('Cluster creation timeout in minutes.')) @utils.arg('-g', '--tags', metavar='<KEY1=VALUE1;KEY2=VALUE2...>', 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='<CLUSTER_NAME>', help=_('Name of the cluster to create.')) def do_cluster_create(sc, args): '''Create the cluster.''' fields = { 'name': args.name, 'profile_id': args.profile, 'tags': utils.format_parameters(args.tags), 'size': args.size, 'timeout': args.timeout } sc.clusters.create(**fields) do_cluster_list(sc) @utils.arg('id', metavar='<NAME or ID>', 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: sc.clusters.delete(cid) 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) do_cluster_list(sc) @utils.arg('-p', '--profile', metavar='<PROFILE ID>', help=_('ID of new profile to use.')) @utils.arg('-n', '--size', metavar='<NUMBER>', help=_('Initial size of the cluster.')) @utils.arg('-t', '--timeout', metavar='<TIMEOUT>', type=int, help=_('Cluster update timeout in minutes.')) @utils.arg('-g', '--tags', metavar='<KEY1=VALUE1;KEY2=VALUE2...>', 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('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to update.')) def do_cluster_update(sc, args): '''Update the cluster.''' fields = { 'profile_id': args.profile, 'size': args.size, 'tags': utils.format_parameters(args.tags), } if args.timeout: fields['timeout'] = args.timeout sc.clusters.update(**fields) do_cluster_list(sc) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to show.')) def do_cluster_show(sc, args): '''Show details of the cluster.''' try: cluster = sc.clusters.get(args.id) except exc.HTTPNotFound: raise exc.CommandError(_('Cluster not found: %s') % args.id) else: formatters = { 'profile': utils.json_formatter, 'status': utils.text_wrap_formatter, 'status_reason': utils.text_wrap_formatter, 'tags': utils.json_formatter, 'links': utils.link_formatter } utils.print_dict(cluster.to_dict(), formatters=formatters) @utils.arg('-s', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted nodes if any.')) @utils.arg('-f', '--filters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>', 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='<LIMIT>', help=_('Limit the number of nodes returned.')) @utils.arg('-m', '--marker', metavar='<ID>', help=_('Only return nodes that appear after the given node ID.')) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to nodes from.')) def do_cluster_node_list(sc, args): '''List nodes from cluster.''' fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_time'] kwargs = { 'cluster_id': args.id, 'show_deleted': args.show_deleted, 'filters': utils.format_parameters(args.filters), 'limit': args.limit, 'marker': args.marker, } try: nodes = sc.nodes.list(**kwargs) except exc.HTTPNotFound: msg = _('No node matching criteria is found') raise exc.CommandError(msg) utils.print_list(nodes, fields, sortby_index=5) @utils.arg('-n', '--nodes', metavar='<NODE_IDs>', help=_('ID of nodes to be added.')) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to operate on.')) def do_cluster_node_add(sc, args): '''Add specified nodes to cluster.''' failure_count = 0 for nid in args.nodes: try: sc.clusters.add_node(args.id, nid) except Exception as ex: failure_count += 1 print(ex) if failure_count == len(args.nodes): msg = _('Failed to add any of the specified nodes.') raise exc.CommandError(msg) do_cluster_node_list(sc, id=args.id) @utils.arg('-n', '--nodes', metavar='<NODE_IDs>', help=_('ID of nodes to be deleted.')) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to operate on.')) def do_cluster_node_del(sc, args): '''Delete specified nodes from cluster.''' failure_count = 0 for nid in args.nodes: try: sc.clusters.del_node(args.id, nid) except Exception as ex: failure_count += 1 print(ex) if failure_count == len(args.nodes): msg = _('Failed to delete any of the specified nodes.') raise exc.CommandError(msg) do_cluster_node_list(sc, id=args.id) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_list(sc, args): '''List policies from cluster.''' policies = sc.clusters.list_policy(args.id) fields = ['id', 'name', 'enabled', 'level'] utils.print_list(policies, fields, sortby_index=1) @utils.arg('-p', '--policy', metavar='<POLICY_ID>', help=_('ID of policy to be attached.')) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_attach(sc, args): '''Attach policy to cluster.''' sc.clusters.attach_policy(args.id, args.policy) do_cluster_policy_list(sc, args.id) @utils.arg('-p', '--policy', metavar='<POLICY_ID>', help=_('ID of policy to be detached.')) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_detach(sc, args): '''Detach policy from cluster.''' sc.clusters.detach_policy(args.id, args.policy) do_cluster_policy_list(sc, args.id) @utils.arg('-p', '--policy', metavar='<POLICY_ID>', help=_('ID of policy to be enabled.')) @utils.arg('-c', '--cooldown', metavar='<COOLDOWN>', help=_('Cooldown interval in seconds.')) @utils.arg('-l', '--level', metavar='<LEVEL>', help=_('Enforcement level.')) @utils.arg('-e', '--enabled', metavar='<BOOLEAN>', help=_('Specify whether to enable policy.')) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of cluster to operate on.')) def do_cluster_policy_update(sc, args): '''Enable policy on cluster.''' kwargs = { 'policy_id': args.policy, 'cooldown': args.cooldown, 'enabled': args.enabled, 'level': args.level, } sc.clusters.update_policy(args.id, **kwargs) do_cluster_policy_list(sc, args.id) #### NODES @utils.arg('-c', '--cluster', metavar='<CLUSTER_ID>', help=_('Cluster Id for this node.')) @utils.arg('-p', '--profile', metavar='<PROFILE_ID>', help=_('Profile Id used for this node.')) @utils.arg('-r', '--role', metavar='<ROLE>', help=_('Role for this node in the specific cluster.')) @utils.arg('-g', '--tags', metavar='<KEY1=VALUE1;KEY2=VALUE2...>', 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='<NODE_NAME>', help=_('Name of the node to create.')) def do_node_create(sc, args): '''Create the node.''' fields = { 'name': args.name, 'cluster_id': args.cluster, 'profile_id': args.profile, 'role': args.role, 'tags': utils.format_parameters(args.tags), } sc.nodes.create(**fields) do_node_list(sc) @utils.arg('id', metavar='<NAME or ID>', 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: sc.nodes.delete(nid) except exc.HTTPNotFound as ex: failure_count += 1 print(ex) if failure_count == len(args.id): msg = _('Failed to delete any of the specified nodes.') raise exc.CommandError(msg) do_node_list(sc) @utils.arg('-n', '--name', metavar='<NAME>', help=_('New name for the node.')) @utils.arg('-p', '--profile', metavar='<PROFILE ID>', help=_('ID of new profile to use.')) @utils.arg('-r', '--role', metavar='<ROLE>', help=_('Role for this node in the specific cluster.')) @utils.arg('-g', '--tags', metavar='<KEY1=VALUE1;KEY2=VALUE2...>', 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='<ID>', help=_('ID of node to update.')) def do_node_update(sc, args): '''Update the node.''' fields = { 'name': args.name, 'role': args.role, 'profile': args.profile, 'tags': utils.format_parameters(args.tags), } sc.nodes.update(args.id, **fields) do_node_list(sc) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of node to operate on.')) def do_node_leave(sc, args): '''Make node leave its current cluster.''' kwargs = { 'cluster_id': '', } do_node_update(args.id, **kwargs) do_node_list(sc) @utils.arg('-c', '--cluster', help=_('ID or name of cluster for node to join.')) @utils.arg('id', metavar='<NAME or ID>', help=_('Name or ID of node to operate on.')) def do_node_join(sc, args): '''Make node join the specified cluster.''' kwargs = { 'cluster_id': args.cluster, } do_node_update(args.id, **kwargs) do_node_list(sc) @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='<KEY1=VALUE1;KEY2=VALUE2...>', 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='<LIMIT>', help=_('Limit the number of nodes returned.')) @utils.arg('-m', '--marker', metavar='<ID>', help=_('Only return nodes that appear after the given node ID.')) @utils.arg('-g', '--global-tenant', action='store_true', default=False, help=_('List nodes from all tenants. Operation only authorized ' 'for users who match the policy in policy file.')) def do_node_list(sc, args): '''Show list of nodes.''' fields = ['id', 'name', 'status', 'cluster_id', 'physical_id', 'created_time'] if args.global_tenant: fields.insert(2, 'project') kwargs = { 'cluster_id': args.cluster, 'show_deleted': args.show_deleted, 'filters': utils.format_parameters(args.filters), 'limit': args.limit, 'marker': args.marker, 'global_tenant': args.global_tenant, } try: nodes = sc.nodes.list(**kwargs) except exc.HTTPNotFound: msg = _('No node matching criteria is found') raise exc.CommandError(msg) utils.print_list(nodes, fields, sortby_index=5) @utils.arg('id', metavar='<NODE ID>', 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.''' try: node = sc.nodes.get(args.id) except exc.HTTPNotFound: msg = _('Node %(id)s is not found') % args.id raise exc.CommandError(msg) formatters = { 'links': utils.link_formatter, 'required_by': utils.newline_list_formatter } utils.print_dict(node.to_dict(), formatters=formatters) ##### EVENTS @utils.arg('-i', '--id', metavar='<ID>', help=_('ID of objects to show the events for.')) @utils.arg('-n', '--name', metavar='<NAME>', help=_('Name of objects to show the events for.')) @utils.arg('-t', '--type', metavar='<OBJECT TYPE>', help=_('Types of the objects to filter events by.' 'The types can be CLUSTER, NODE, PROFILE, POLICY.')) @utils.arg('-f', '--filters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>', 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='<LIMIT>', help=_('Limit the number of events returned.')) @utils.arg('-m', '--marker', metavar='<ID>', help=_('Only return events that appear after the given event ID.')) @utils.arg('-k', '--sort-keys', metavar='<KEYS>', help=_('Name of keys used for sorting the returned events.')) @utils.arg('-d', '--sort-dir', metavar='<DIR>', help=_('Direction for sorting, where DIR can be "asc" or "desc".')) def do_event_list(sc, args): '''List events.''' fields = { 'obj_id': args.id, 'obj_name': args.name, 'obj_type': args.type, 'filters': utils.format_parameters(args.filters), 'sort_keys': args.sort_keys, 'sort_dir': args.sort_dir, 'limit': args.limit, 'marker': args.marker, } try: events = sc.events.list(**fields) except exc.HTTPNotFound as ex: raise exc.CommandError(str(ex)) fields = ['timestamp', 'obj_type', 'obj_id', 'action', 'status', 'status_reason'] utils.print_list(events, fields, sortby_index=0) @utils.arg('event', metavar='<EVENT>', help=_('ID of event to display details for.')) def do_event_show(sc, args): '''Describe the event.''' try: event = sc.events.get(args.event) except exc.HTTPNotFound as ex: raise exc.CommandError(str(ex)) utils.print_dict(event.to_dict()) #### EVENTS @utils.arg('-f', '--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.'), action='append') @utils.arg('-l', '--limit', metavar='<LIMIT>', help=_('Limit the number of actions returned.')) @utils.arg('-m', '--marker', metavar='<ID>', help=_('Only return action that appear after the given action ID.')) def do_action_list(sc, args): '''List actions.''' fields = { 'limit': args.limit, 'marker': args.marker, 'filters': utils.format_parameters(args.filters) } try: actions = sc.actions.list(**fields) except exc.HTTPNotFound as ex: raise exc.CommandError(str(ex)) fields = ['name', 'action', 'status', 'status_reason', 'depends_on', 'depended_by'] utils.print_list(actions, fields, sortby_index=0)