# # Copyright 2015 Red Hat, Inc. # # 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 argparse import itertools import logging from cliff import command from cliff import lister from cliff import show from openstackclient.common import utils as oscutils from ironicclient.common.i18n import _ from ironicclient.common import utils from ironicclient import exc from ironicclient.v1 import resource_fields as res_fields class ProvisionStateBaremetalNode(command.Command): """Base provision state class""" log = logging.getLogger(__name__ + ".ProvisionStateBaremetalNode") def get_parser(self, prog_name): parser = super(ProvisionStateBaremetalNode, self).get_parser(prog_name) parser.add_argument( 'node', metavar='', help="Name or UUID of the node." ) parser.add_argument( '--provision-state', default=self.PROVISION_STATE, required=False, choices=[self.PROVISION_STATE], help=argparse.SUPPRESS) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal baremetal_client.node.set_provision_state( parsed_args.node, parsed_args.provision_state) class AbortBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'abort'""" log = logging.getLogger(__name__ + ".AbortBaremetalNode") PROVISION_STATE = 'abort' class CleanBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'clean'""" log = logging.getLogger(__name__ + ".CleanBaremetalNode") PROVISION_STATE = 'clean' def get_parser(self, prog_name): parser = super(CleanBaremetalNode, self).get_parser(prog_name) parser.add_argument( '--clean-steps', metavar='', required=True, default=None, help=("The clean steps in JSON format. May be the path to a file " "containing the clean steps; OR '-', with the clean steps " "being read from standard input; OR a string. The value " "should be a list of clean-step dictionaries; each " "dictionary should have keys 'interface' and 'step', and " "optional key 'args'.")) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal clean_steps = parsed_args.clean_steps if parsed_args.clean_steps == '-': clean_steps = utils.get_from_stdin('clean steps') if clean_steps: clean_steps = utils.handle_json_or_file_arg(clean_steps) baremetal_client.node.set_provision_state( parsed_args.node, parsed_args.provision_state, cleansteps=clean_steps) class CreateBaremetalNode(show.ShowOne): """Register a new node with the baremetal service""" log = logging.getLogger(__name__ + ".CreateBaremetalNode") def get_parser(self, prog_name): parser = super(CreateBaremetalNode, self).get_parser(prog_name) parser.add_argument( '--chassis-uuid', dest='chassis_uuid', metavar='', help='UUID of the chassis that this node belongs to.') parser.add_argument( '--driver', metavar='', required=True, help='Driver used to control the node [REQUIRED].') parser.add_argument( '--driver-info', metavar='', action='append', help='Key/value pair used by the driver, such as out-of-band ' 'management credentials. Can be specified multiple times.') parser.add_argument( '--property', dest='properties', metavar='', action='append', help='Key/value pair describing the physical characteristics of ' 'the node. This is exported to Nova and used by the ' 'scheduler. Can be specified multiple times.') parser.add_argument( '--extra', metavar='', action='append', help="Record arbitrary key/value metadata. " "Can be specified multiple times.") parser.add_argument( '--uuid', metavar='', help="Unique UUID for the node.") parser.add_argument( '--name', metavar='', help="Unique name for the node.") return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal field_list = ['chassis_uuid', 'driver', 'driver_info', 'properties', 'extra', 'uuid', 'name'] fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and not (v is None)) fields = utils.args_array_to_dict(fields, 'driver_info') fields = utils.args_array_to_dict(fields, 'extra') fields = utils.args_array_to_dict(fields, 'properties') node = baremetal_client.node.create(**fields)._info node.pop('links', None) return self.dict2columns(node) class CreateBaremetal(CreateBaremetalNode): """Register a new node with the baremetal service. DEPRECATED""" # TODO(thrash): Remove in the 'P' cycle. log = logging.getLogger(__name__ + ".CreateBaremetal") def take_action(self, parsed_args): self.log.warning("This command is deprecated. Instead, use " "'openstack baremetal node create'.") return super(CreateBaremetal, self).take_action(parsed_args) class DeleteBaremetalNode(command.Command): """Unregister a baremetal node""" log = logging.getLogger(__name__ + ".DeleteBaremetalNode") def get_parser(self, prog_name): parser = super(DeleteBaremetalNode, self).get_parser(prog_name) parser.add_argument( "nodes", metavar="", nargs="+", help="Node(s) to delete (name or UUID)") return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal failures = [] for node in parsed_args.nodes: try: baremetal_client.node.delete(node) print(_('Deleted node %s') % node) except exc.ClientException as e: failures.append(_("Failed to delete node %(node)s: %(error)s") % {'node': node, 'error': e}) if failures: raise exc.ClientException("\n".join(failures)) class DeleteBaremetal(DeleteBaremetalNode): """Unregister a baremetal node. DEPRECATED""" # TODO(thrash): Remove in the 'P' cycle. log = logging.getLogger(__name__ + ".DeleteBaremetal") def take_action(self, parsed_args): self.log.warning("This command is deprecated. Instead, use " "'openstack baremetal node delete'.") super(DeleteBaremetal, self).take_action(parsed_args) class DeployBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'deploy'""" log = logging.getLogger(__name__ + ".DeployBaremetalNode") PROVISION_STATE = 'active' def get_parser(self, prog_name): parser = super(DeployBaremetalNode, self).get_parser(prog_name) parser.add_argument( '--config-drive', metavar='', default=None, help=("A gzipped, base64-encoded configuration drive string OR " "the path to the configuration drive file OR the path to a " "directory containing the config drive files. In case it's " "a directory, a config drive will be generated from it. ")) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal baremetal_client.node.set_provision_state( parsed_args.node, parsed_args.provision_state, configdrive=parsed_args.config_drive) class InspectBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'inspect'""" log = logging.getLogger(__name__ + ".InspectBaremetalNode") PROVISION_STATE = 'inspect' class ListBaremetalNode(lister.Lister): """List baremetal nodes""" log = logging.getLogger(__name__ + ".ListBaremetalNode") PROVISION_STATES = ['active', 'deleted', 'rebuild', 'inspect', 'provide', 'manage', 'clean', 'abort'] def get_parser(self, prog_name): parser = super(ListBaremetalNode, self).get_parser(prog_name) parser.add_argument( '--limit', metavar='', type=int, help='Maximum number of nodes to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Baremetal API Service.' ) parser.add_argument( '--marker', metavar='', help='Node UUID (for example, of the last node in the list from ' 'a previous request). Returns the list of nodes after this ' 'UUID.' ) parser.add_argument( '--sort', metavar="[:]", help='Sort output by specified node fields and directions ' '(asc or desc) (default: asc). Multiple fields and ' 'directions can be specified, separated by comma.', ) parser.add_argument( '--maintenance', dest='maintenance', action='store_true', default=False, help="List nodes in maintenance mode.", ) parser.add_argument( '--associated', dest='associated', action='store_true', default=False, help="List only nodes associated with an instance." ) parser.add_argument( '--provision-state', dest='provision_state', metavar='', choices=self.PROVISION_STATES, help="Limit list to nodes in . One of %s." % ( ", ".join(self.PROVISION_STATES))) display_group = parser.add_mutually_exclusive_group(required=False) display_group.add_argument( '--long', default=False, help="Show detailed information about the nodes.", action='store_true') display_group.add_argument( '--fields', nargs='+', dest='fields', metavar='', action='append', default=[], choices=res_fields.NODE_DETAILED_RESOURCE.fields, help="One or more node fields. Only these fields will be fetched " "from the server. Can not be used when '--long' is " "specified.") return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) client = self.app.client_manager.baremetal columns = res_fields.NODE_RESOURCE.fields labels = res_fields.NODE_RESOURCE.labels params = {} if parsed_args.limit is not None and parsed_args.limit < 0: raise exc.CommandError( _('Expected non-negative --limit, got %s') % parsed_args.limit) params['limit'] = parsed_args.limit params['marker'] = parsed_args.marker if parsed_args.associated: params['associated'] = parsed_args.associated if parsed_args.maintenance: params['maintenance'] = parsed_args.maintenance if parsed_args.provision_state: params['provision_state'] = parsed_args.provision_state if parsed_args.long: params['detail'] = parsed_args.long columns = res_fields.NODE_DETAILED_RESOURCE.fields labels = res_fields.NODE_DETAILED_RESOURCE.labels elif parsed_args.fields: params['detail'] = False fields = itertools.chain.from_iterable(parsed_args.fields) resource = res_fields.Resource(list(fields)) columns = resource.fields labels = resource.labels params['fields'] = columns self.log.debug("params(%s)" % params) data = client.node.list(**params) data = oscutils.sort_items(data, parsed_args.sort) return (labels, (oscutils.get_item_properties(s, columns, formatters={ 'Properties': oscutils.format_dict},) for s in data)) class ListBaremetal(ListBaremetalNode): """List baremetal nodes. DEPRECATED""" # TODO(thrash): Remove in the 'P' cycle. log = logging.getLogger(__name__ + ".ListBaremetal") def take_action(self, parsed_args): self.log.warning("This command is deprecated. Instead, use " "'openstack baremetal node list'.") return super(ListBaremetal, self).take_action(parsed_args) class MaintenanceSetBaremetalNode(command.Command): """Set baremetal node to maintenance mode""" log = logging.getLogger(__name__ + ".MaintenanceSetBaremetalNode") def get_parser(self, prog_name): parser = super(MaintenanceSetBaremetalNode, self).get_parser(prog_name) parser.add_argument( 'node', metavar='', help="Name or UUID of the node." ) parser.add_argument( '--reason', metavar='', default=None, help=("Reason for setting maintenance mode.")) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal baremetal_client.node.set_maintenance( parsed_args.node, True, maint_reason=parsed_args.reason) class MaintenanceUnsetBaremetalNode(command.Command): """Unset baremetal node from maintenance mode""" log = logging.getLogger(__name__ + ".MaintenanceUnsetBaremetalNode") def get_parser(self, prog_name): parser = super(MaintenanceUnsetBaremetalNode, self).get_parser(prog_name) parser.add_argument( 'node', metavar='', help="Name or UUID of the node." ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal baremetal_client.node.set_maintenance( parsed_args.node, False) class ManageBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'manage'""" log = logging.getLogger(__name__ + ".ManageBaremetalNode") PROVISION_STATE = 'manage' class PowerBaremetalNode(command.Command): """Set power state of baremetal node""" log = logging.getLogger(__name__ + ".PowerBaremetalNode") def get_parser(self, prog_name): parser = super(PowerBaremetalNode, self).get_parser(prog_name) parser.add_argument( 'power_state', metavar='', choices=['on', 'off'], help="Power node on or off" ) parser.add_argument( 'node', metavar='', help="Name or UUID of the node." ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal baremetal_client.node.set_power_state( parsed_args.node, parsed_args.power_state) class ProvideBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'provide'""" log = logging.getLogger(__name__ + ".ProvideBaremetalNode") PROVISION_STATE = 'provide' class RebootBaremetalNode(command.Command): """Reboot baremetal node""" log = logging.getLogger(__name__ + ".RebootBaremetalNode") def get_parser(self, prog_name): parser = super(RebootBaremetalNode, self).get_parser(prog_name) parser.add_argument( 'node', metavar='', help="Name or UUID of the node.") return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal baremetal_client.node.set_power_state( parsed_args.node, 'reboot') class RebuildBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'rebuild'""" log = logging.getLogger(__name__ + ".RebuildBaremetalNode") PROVISION_STATE = 'rebuild' class SetBaremetalNode(command.Command): """Set baremetal properties""" log = logging.getLogger(__name__ + ".SetBaremetalNode") def get_parser(self, prog_name): parser = super(SetBaremetalNode, self).get_parser(prog_name) parser.add_argument( 'node', metavar='', help="Name or UUID of the node.", ) parser.add_argument( "--instance-uuid", metavar="", help="Set instance UUID of node to ", ) parser.add_argument( "--name", metavar="", help="Set the name of the node", ) parser.add_argument( "--driver", metavar="", help="Set the driver for the node", ) parser.add_argument( "--property", metavar="", action='append', help='Property to set on this baremetal node ' '(repeat option to set multiple properties)', ) parser.add_argument( "--extra", metavar="", action='append', help='Extra to set on this baremetal node ' '(repeat option to set multiple extras)', ) parser.add_argument( "--driver-info", metavar="", action='append', help='Driver information to set on this baremetal node ' '(repeat option to set multiple driver infos)', ) parser.add_argument( "--instance-info", metavar="", action='append', help='Instance information to set on this baremetal node ' '(repeat option to set multiple instance infos)', ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal properties = [] if parsed_args.instance_uuid: instance_uuid = ["instance_uuid=%s" % parsed_args.instance_uuid] properties.extend(utils.args_array_to_patch( 'add', instance_uuid)) if parsed_args.name: name = ["name=%s" % parsed_args.name] properties.extend(utils.args_array_to_patch( 'add', name)) if parsed_args.driver: driver = ["driver=%s" % parsed_args.driver] properties.extend(utils.args_array_to_patch( 'add', driver)) if parsed_args.property: properties.extend(utils.args_array_to_patch( 'add', ['properties/' + x for x in parsed_args.property])) if parsed_args.extra: properties.extend(utils.args_array_to_patch( 'add', ['extra/' + x for x in parsed_args.extra])) if parsed_args.driver_info: properties.extend(utils.args_array_to_patch( 'add', ['driver_info/' + x for x in parsed_args.driver_info])) if parsed_args.instance_info: properties.extend(utils.args_array_to_patch( 'add', ['instance_info/' + x for x in parsed_args.instance_info])) baremetal_client.node.update(parsed_args.node, properties) class SetBaremetal(SetBaremetalNode): """Set baremetal properties. DEPRECATED""" # TODO(thrash): Remove in the 'P' cycle. log = logging.getLogger(__name__ + ".SetBaremetal") def take_action(self, parsed_args): self.log.warning("This command is deprecated. Instead, use " "'openstack baremetal node set'.") return super(SetBaremetal, self).take_action(parsed_args) class ShowBaremetalNode(show.ShowOne): """Show baremetal node details""" log = logging.getLogger(__name__ + ".ShowBaremetalNode") LONG_FIELDS = [ 'extra', 'properties', 'ports', 'driver_info', 'driver_internal_info', 'instance_info', ] def get_parser(self, prog_name): parser = super(ShowBaremetalNode, self).get_parser(prog_name) parser.add_argument( "node", metavar="", help="Name or UUID of the node (or instance UUID if --instance " "is specified)") parser.add_argument( '--instance', dest='instance_uuid', action='store_true', default=False, help=' is an instance UUID.') parser.add_argument( '--fields', nargs='+', dest='fields', metavar='', action='append', choices=res_fields.NODE_DETAILED_RESOURCE.fields, default=[], help="One or more node fields. Only these fields will be fetched " "from the server.") return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal fields = list(itertools.chain.from_iterable(parsed_args.fields)) fields = fields if fields else None if parsed_args.instance_uuid: node = baremetal_client.node.get_by_instance_uuid( parsed_args.node, fields=fields)._info else: node = baremetal_client.node.get( parsed_args.node, fields=fields)._info node.pop("links", None) return zip(*sorted(node.items())) class ShowBaremetal(ShowBaremetalNode): """Show baremetal node details. DEPRECATED""" # TODO(thrash): Remove in the 'P' cycle. log = logging.getLogger(__name__ + ".ShowBaremetal") def take_action(self, parsed_args): self.log.warning("This command is deprecated. Instead, use " "'openstack baremetal node show'.") return super(ShowBaremetal, self).take_action(parsed_args) class UndeployBaremetalNode(ProvisionStateBaremetalNode): """Set provision state of baremetal node to 'deleted'""" log = logging.getLogger(__name__ + ".UndeployBaremetalNode") PROVISION_STATE = 'deleted' class UnsetBaremetalNode(command.Command): """Unset baremetal properties""" log = logging.getLogger(__name__ + ".UnsetBaremetalNode") def get_parser(self, prog_name): parser = super(UnsetBaremetalNode, self).get_parser(prog_name) parser.add_argument( 'node', metavar='', help="Name or UUID of the node." ) parser.add_argument( '--instance-uuid', action='store_true', default=False, help='Unset instance UUID on this baremetal node' ) parser.add_argument( "--name", action='store_true', help="Unset the name of the node", ) parser.add_argument( '--property', metavar='', action='append', help='Property to unset on this baremetal node ' '(repeat option to unset multiple properties)', ) parser.add_argument( "--extra", metavar="", action='append', help='Extra to unset on this baremetal node ' '(repeat option to unset multiple extras)', ) parser.add_argument( "--driver-info", metavar="", action='append', help='Driver information to unset on this baremetal node ' '(repeat option to unset multiple driver informations)', ) parser.add_argument( "--instance-info", metavar="", action='append', help='Instance information to unset on this baremetal node ' '(repeat option to unset multiple instance informations)', ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal properties = [] if parsed_args.instance_uuid: properties.extend(utils.args_array_to_patch('remove', ['instance_uuid'])) if parsed_args.name: properties.extend(utils.args_array_to_patch('remove', ['name'])) if parsed_args.property: properties.extend(utils.args_array_to_patch('remove', ['properties/' + x for x in parsed_args.property])) if parsed_args.extra: properties.extend(utils.args_array_to_patch('remove', ['extra/' + x for x in parsed_args.extra])) if parsed_args.driver_info: properties.extend(utils.args_array_to_patch('remove', ['driver_info/' + x for x in parsed_args.driver_info])) if parsed_args.instance_info: properties.extend(utils.args_array_to_patch('remove', ['instance_info/' + x for x in parsed_args.instance_info])) baremetal_client.node.update(parsed_args.node, properties) class UnsetBaremetal(UnsetBaremetalNode): """Unset baremetal properties. DEPRECATED""" # TODO(thrash): Remove in the 'P' cycle. log = logging.getLogger(__name__ + ".UnsetBaremetal") def take_action(self, parsed_args): self.log.warning("This command is deprecated. Instead, use " "'openstack baremetal node unset'.") super(UnsetBaremetal, self).take_action(parsed_args)