Source code for ironicclient.osc.v1.baremetal_node
#
# 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 json
import logging
import sys
from osc_lib.command import command
from osc_lib 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
from ironicclient.v1 import utils as v1_utils
CONFIG_DRIVE_ARG_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 OR a JSON object to build "
"config drive from OR the path to the JSON file. In case it's a "
"directory, a config drive will be generated from it. In case it's a JSON "
"object with optional keys `meta_data`, `user_data` and `network_data` "
"or a JSON file, a config drive will be generated on the server side "
"(see the bare metal API reference for more details).")
NETWORK_DATA_ARG_HELP = _(
"JSON string or a YAML file or '-' for stdin to read static network "
"configuration for the baremetal node associated with this ironic node. "
"Format of this file should comply with Nova network data metadata "
"(network_data.json). Depending on ironic boot interface capabilities "
"being used, network configuration may or may not been served to the "
"node for offline network configuration.")
SUPPORTED_INTERFACES = ['bios', 'boot', 'console', 'deploy', 'firmware',
'inspect', 'management', 'network', 'power', 'raid',
'rescue', 'storage', 'vendor']
[docs]
class ProvisionStateBaremetalNode(command.Command):
"""Base provision state class"""
log = logging.getLogger(__name__ + ".ProvisionStateBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ProvisionStateBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
parser.add_argument(
'--provision-state',
default=self.PROVISION_STATE,
required=False,
choices=[self.PROVISION_STATE],
help=argparse.SUPPRESS)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
runbook = getattr(parsed_args, 'runbook', None)
clean_steps = getattr(parsed_args, 'clean_steps', None)
clean_steps = utils.handle_json_arg(clean_steps, 'clean steps')
deploy_steps = getattr(parsed_args, 'deploy_steps', None)
deploy_steps = utils.handle_json_arg(deploy_steps, 'deploy steps')
service_steps = getattr(parsed_args, 'service_steps', None)
service_steps = utils.handle_json_arg(service_steps, 'service steps')
config_drive = getattr(parsed_args, 'config_drive', None)
if config_drive:
try:
config_drive_dict = json.loads(config_drive)
except (ValueError, TypeError):
pass
else:
if isinstance(config_drive_dict, dict):
config_drive = config_drive_dict
rescue_password = getattr(parsed_args, 'rescue_password', None)
disable_ramdisk = getattr(parsed_args, 'disable_ramdisk', None)
if runbook and disable_ramdisk:
raise exc.CommandError(
_("You cannot supply --runbook and --disable-ramdisk together")
)
for node in parsed_args.nodes:
baremetal_client.node.set_provision_state(
node,
parsed_args.provision_state,
configdrive=config_drive,
cleansteps=clean_steps,
deploysteps=deploy_steps,
rescue_password=rescue_password,
servicesteps=service_steps,
runbook=runbook,
disable_ramdisk=disable_ramdisk)
[docs]
class ProvisionStateWithWait(ProvisionStateBaremetalNode):
"""Provision state class adding --wait flag."""
log = logging.getLogger(__name__ + ".ProvisionStateWithWait")
[docs]
def get_parser(self, prog_name):
parser = super(ProvisionStateWithWait, self).get_parser(prog_name)
desired_state = v1_utils.PROVISION_ACTIONS.get(
self.PROVISION_STATE)['expected_state']
parser.add_argument(
'--wait',
type=int,
dest='wait_timeout',
default=None,
metavar='<time-out>',
const=0,
nargs='?',
help=_("Wait for a node to reach the desired state, %(state)s. "
"Optionally takes a timeout value (in seconds). The "
"default value is 0, meaning it will wait indefinitely.") %
{'state': desired_state})
return parser
[docs]
def take_action(self, parsed_args):
super(ProvisionStateWithWait, self).take_action(parsed_args)
self.log.debug("take_action(%s)", parsed_args)
if (parsed_args.wait_timeout is None):
return
baremetal_client = self.app.client_manager.baremetal
wait_args = v1_utils.PROVISION_ACTIONS.get(
parsed_args.provision_state)
if wait_args is None:
# This should never happen in reality, but checking just in case
raise exc.CommandError(
_("'--wait is not supported for provision state '%s'")
% parsed_args.provision_state)
print(_('Waiting for provision state %(state)s on node(s) %(node)s') %
{'state': wait_args['expected_state'],
'node': ', '.join(parsed_args.nodes)})
baremetal_client.node.wait_for_provision_state(
parsed_args.nodes,
timeout=parsed_args.wait_timeout,
**wait_args)
[docs]
class AbortBaremetalNode(ProvisionStateBaremetalNode):
"""Set provision state of baremetal node to 'abort'"""
log = logging.getLogger(__name__ + ".AbortBaremetalNode")
PROVISION_STATE = 'abort'
[docs]
class AdoptBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'adopt'"""
log = logging.getLogger(__name__ + ".AdoptBaremetalNode")
PROVISION_STATE = 'adopt'
[docs]
class BootdeviceSetBaremetalNode(command.Command):
"""Set the boot device for a node"""
log = logging.getLogger(__name__ + ".BootdeviceSetBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(BootdeviceSetBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes")
)
parser.add_argument(
'device',
metavar='<device>',
choices=v1_utils.BOOT_DEVICES,
help=_("One of %s") % (oscutils.format_list(v1_utils.BOOT_DEVICES))
)
parser.add_argument(
'--persistent',
dest='persistent',
action='store_true',
default=False,
help=_("Make changes persistent for all future boots")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_boot_device(
node, parsed_args.device, parsed_args.persistent)
[docs]
class BootdeviceShowBaremetalNode(command.ShowOne):
"""Show the boot device information for a node"""
log = logging.getLogger(__name__ + ".BootdeviceShowBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(BootdeviceShowBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
parser.add_argument(
'--supported',
dest='supported',
action='store_true',
default=False,
help=_("Show the supported boot devices")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
if parsed_args.supported:
info = baremetal_client.node.get_supported_boot_devices(
parsed_args.node)
boot_device_list = info.get('supported_boot_devices', [])
info['supported_boot_devices'] = ', '.join(boot_device_list)
else:
info = baremetal_client.node.get_boot_device(parsed_args.node)
return zip(*sorted(info.items()))
[docs]
class BootmodeSetBaremetalNode(command.Command):
"""Set the boot mode for the next baremetal node deployment"""
log = logging.getLogger(__name__ + ".BootmodeSetBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(BootmodeSetBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
parser.add_argument(
'boot_mode',
choices=['uefi', 'bios'],
metavar='<boot_mode>',
help=_('The boot mode to set for node (uefi/bios)')
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_boot_mode(node, parsed_args.boot_mode)
[docs]
class CleanBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'clean'"""
log = logging.getLogger(__name__ + ".CleanBaremetalNode")
PROVISION_STATE = 'clean'
[docs]
def get_parser(self, prog_name):
parser = super(CleanBaremetalNode, self).get_parser(prog_name)
clean_group = parser.add_mutually_exclusive_group(required=True)
clean_group.add_argument(
'--clean-steps',
metavar='<clean-steps>',
help=_("The clean steps. May be the path to a YAML file "
"containing the clean steps; OR '-', with the clean steps "
"being read from standard input; OR a JSON string. The "
"value should be a list of clean-step dictionaries; each "
"dictionary should have keys 'interface' and 'step', and "
"optional key 'args'."))
clean_group.add_argument(
'--runbook',
metavar='<runbook>',
help=_("The identifier of a predefined runbook to use for "
"cleaning."))
parser.add_argument(
'--disable-ramdisk',
action='store_true',
default=None,
help=_("ironic-python-agent will not be booted for cleaning. "
"Only steps explicitly marked as not requiring "
"ironic-python-agent can be executed with this set."))
return parser
[docs]
class ServiceBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'service'"""
log = logging.getLogger(__name__ + ".ServiceBaremetalNode")
PROVISION_STATE = 'service'
[docs]
def get_parser(self, prog_name):
parser = super(ServiceBaremetalNode, self).get_parser(prog_name)
service_group = parser.add_mutually_exclusive_group(required=True)
service_group.add_argument(
'--service-steps',
metavar='<service-steps>',
help=_("The service steps. May be the path to a YAML file "
"containing the service steps; OR '-', with the service "
" steps being read from standard input; OR a JSON string. "
"The value should be a list of service-step dictionaries; "
"each dictionary should have keys 'interface' and 'step', "
"and optional key 'args'."))
service_group.add_argument(
'--runbook',
metavar='<runbook>',
help=_("The identifier of a predefined runbook to use for "
"servicing."))
parser.add_argument(
'--disable-ramdisk',
action='store_true',
default=None,
help=_("ironic-python-agent will not be booted for cleaning. "
"Only steps explicitly marked as not requiring "
"ironic-python-agent can be executed with this set."))
return parser
[docs]
class ConsoleDisableBaremetalNode(command.Command):
"""Disable console access for a node"""
log = logging.getLogger(__name__ + ".ConsoleDisableBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ConsoleDisableBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_console_mode(node, False)
[docs]
class ConsoleEnableBaremetalNode(command.Command):
"""Enable console access for a node"""
log = logging.getLogger(__name__ + ".ConsoleEnableBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ConsoleEnableBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_console_mode(node, True)
[docs]
class ConsoleShowBaremetalNode(command.ShowOne):
"""Show console information for a node"""
log = logging.getLogger(__name__ + ".ConsoleShowBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ConsoleShowBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
info = baremetal_client.node.get_console(parsed_args.node)
return zip(*sorted(info.items()))
[docs]
class CreateBaremetalNode(command.ShowOne):
"""Register a new node with the baremetal service"""
log = logging.getLogger(__name__ + ".CreateBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(CreateBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'--chassis-uuid',
dest='chassis_uuid',
metavar='<chassis>',
help=_('UUID of the chassis that this node belongs to.'))
parser.add_argument(
'--driver',
metavar='<driver>',
required=True,
help=_('Driver used to control the node [REQUIRED].'))
parser.add_argument(
'--driver-info',
metavar='<key=value>',
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='<key=value>',
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='<key=value>',
action='append',
help=_("Record arbitrary key/value metadata. "
"Can be specified multiple times."))
parser.add_argument(
'--uuid',
metavar='<uuid>',
help=_("Unique UUID for the node."))
parser.add_argument(
'--name',
metavar='<name>',
help=_("Unique name for the node."))
parser.add_argument(
'--bios-interface',
metavar='<bios_interface>',
help=_('BIOS interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--boot-interface',
metavar='<boot_interface>',
help=_('Boot interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--console-interface',
metavar='<console_interface>',
help=_('Console interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--deploy-interface',
metavar='<deploy_interface>',
help=_('Deploy interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--inspect-interface',
metavar='<inspect_interface>',
help=_('Inspect interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--management-interface',
metavar='<management_interface>',
help=_('Management interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--network-data',
metavar="<network data>",
dest='network_data',
help=NETWORK_DATA_ARG_HELP)
parser.add_argument(
'--network-interface',
metavar='<network_interface>',
help=_('Network interface used for switching node to '
'cleaning/provisioning networks.'))
parser.add_argument(
'--power-interface',
metavar='<power_interface>',
help=_('Power interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--raid-interface',
metavar='<raid_interface>',
help=_('RAID interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--rescue-interface',
metavar='<rescue_interface>',
help=_('Rescue interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--storage-interface',
metavar='<storage_interface>',
help=_('Storage interface used by the node\'s driver.'))
parser.add_argument(
'--vendor-interface',
metavar='<vendor_interface>',
help=_('Vendor interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--resource-class',
metavar='<resource_class>',
help=_('Resource class for mapping nodes to Nova flavors'))
parser.add_argument(
'--conductor-group',
metavar='<conductor_group>',
help=_('Conductor group the node will belong to'))
clean = parser.add_mutually_exclusive_group()
clean.add_argument(
'--automated-clean',
action='store_true',
default=None,
help=_('Enable automated cleaning for the node'))
clean.add_argument(
'--no-automated-clean',
action='store_false',
dest='automated_clean',
default=None,
help=_('Explicitly disable automated cleaning for the node'))
parser.add_argument(
'--owner',
metavar='<owner>',
help=_('Owner of the node.'))
parser.add_argument(
'--lessee',
metavar='<lessee>',
help=_('Lessee of the node.'))
parser.add_argument(
'--description',
metavar='<description>',
help=_("Description for the node."))
parser.add_argument(
'--shard',
metavar='<shard>',
help=_("Shard for the node."))
parser.add_argument(
'--parent-node',
metavar='<parent_node>',
help=_('Parent node for the node being created.'))
parser.add_argument(
'--firmware-interface',
metavar='<firmware_interface>',
help=_('Firmware interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
field_list = ['automated_clean', 'chassis_uuid', 'driver',
'driver_info', 'properties', 'extra', 'uuid', 'name',
'conductor_group', 'owner', 'description', 'lessee',
'shard', 'resource_class', 'parent_node',
] + ['%s_interface' % iface
for iface in SUPPORTED_INTERFACES]
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')
if parsed_args.network_data:
fields['network_data'] = utils.handle_json_arg(
parsed_args.network_data, 'static network configuration')
fields = utils.args_array_to_dict(fields, 'properties')
node = baremetal_client.node.create(**fields)._info
node.pop('links', None)
node.pop('ports', None)
node.pop('portgroups', None)
node.pop('states', None)
node.pop('volume', None)
node.setdefault('chassis_uuid', '')
return self.dict2columns(node)
[docs]
class DeleteBaremetalNode(command.Command):
"""Unregister baremetal node(s)"""
log = logging.getLogger(__name__ + ".DeleteBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(DeleteBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
"nodes",
metavar="<node>",
nargs="+",
help=_("Node(s) to delete (name or UUID)"))
return parser
[docs]
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))
[docs]
class DeployBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'deploy'"""
log = logging.getLogger(__name__ + ".DeployBaremetalNode")
PROVISION_STATE = 'active'
[docs]
def get_parser(self, prog_name):
parser = super(DeployBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'--config-drive',
metavar='<config-drive>',
default=None,
help=CONFIG_DRIVE_ARG_HELP)
parser.add_argument(
'--deploy-steps',
metavar='<deploy-steps>',
required=False,
default=None,
help=_("The deploy steps. May be the path to a YAML file "
"containing the deploy steps; OR '-', with the deploy "
"steps being read from standard input; OR a JSON string. "
"The value should be a list of deploy-step dictionaries; "
"each dictionary should have keys 'interface' and 'step', "
"and optional key 'args'."))
return parser
[docs]
class InspectBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'inspect'"""
log = logging.getLogger(__name__ + ".InspectBaremetalNode")
PROVISION_STATE = 'inspect'
[docs]
class ListBaremetalNode(command.Lister):
"""List baremetal nodes"""
log = logging.getLogger(__name__ + ".ListBaremetalNode")
PROVISION_STATES = ['active', 'deleted', 'rebuild', 'inspect', 'provide',
'manage', 'clean', 'adopt', 'abort']
[docs]
def get_parser(self, prog_name):
parser = super(ListBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'--limit',
metavar='<limit>',
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='<node>',
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="<key>[:<direction>]",
help=_('Sort output by specified node fields and directions '
'(asc or desc) (default: asc). Multiple fields and '
'directions can be specified, separated by comma.'),
)
maint_group = parser.add_mutually_exclusive_group(required=False)
maint_group.add_argument(
'--maintenance',
dest='maintenance',
action='store_true',
default=None,
help=_("Limit list to nodes in maintenance mode"),
)
maint_group.add_argument(
'--no-maintenance',
dest='maintenance',
action='store_false',
default=None,
help=_("Limit list to nodes not in maintenance mode"),
)
retired_group = parser.add_mutually_exclusive_group(required=False)
retired_group.add_argument(
'--retired',
dest='retired',
action='store_true',
default=None,
help=_("Limit list to retired nodes.")
)
retired_group.add_argument(
'--no-retired',
dest='retired',
action='store_false',
default=None,
help=_("Limit list to not retired nodes.")
)
parser.add_argument(
'--fault',
dest='fault',
metavar='<fault>',
help=_("List nodes in specified fault."))
associated_group = parser.add_mutually_exclusive_group()
associated_group.add_argument(
'--associated',
action='store_true',
help=_("List only nodes associated with an instance."),
)
associated_group.add_argument(
'--unassociated',
action='store_true',
help=_('List only nodes not associated with an instance.'),
)
parser.add_argument(
'--provision-state',
dest='provision_state',
metavar='<provision state>',
help=_("List nodes in specified provision state."))
parser.add_argument(
'--driver',
dest='driver',
metavar='<driver>',
help=_("Limit list to nodes with driver <driver>"))
parser.add_argument(
'--resource-class',
dest='resource_class',
metavar='<resource class>',
help=_("Limit list to nodes with resource class <resource class>"))
parser.add_argument(
'--conductor-group',
metavar='<conductor_group>',
help=_("Limit list to nodes with conductor group <conductor "
"group>"))
parser.add_argument(
'--conductor',
metavar='<conductor>',
help=_("Limit list to nodes with conductor <conductor>"))
parser.add_argument(
'--chassis',
dest='chassis',
metavar='<chassis UUID>',
help=_("Limit list to nodes of this chassis"))
parser.add_argument(
'--owner',
metavar='<owner>',
help=_("Limit list to nodes with owner "
"<owner>"))
parser.add_argument(
'--lessee',
metavar='<lessee>',
help=_("Limit list to nodes with lessee "
"<lessee>"))
parser.add_argument(
'--description-contains',
metavar='<description_contains>',
help=_("Limit list to nodes with description contains "
"<description_contains>"))
sharded_group = parser.add_mutually_exclusive_group(required=False)
sharded_group.add_argument(
'--sharded',
dest='sharded',
help=_("List only nodes that are sharded."),
default=None,
action='store_true')
sharded_group.add_argument(
'--unsharded',
dest='sharded',
help=_("List only nodes that are not sharded."),
default=None,
action='store_false')
parser.add_argument(
'--shards',
nargs='+',
metavar='<shards>',
help=_("List only nodes that are in shards <shards>."))
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='<field>',
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."))
children_group = parser.add_mutually_exclusive_group(required=False)
children_group.add_argument(
'--include-children',
action='store_true',
help=_("Include children in the node list."),
)
children_group.add_argument(
'--parent-node',
dest='parent_node',
metavar="<parent_node>",
help=_('List only nodes associated with a parent node.'),
)
return parser
[docs]
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'] = True
if parsed_args.unassociated:
params['associated'] = False
for field in ['maintenance', 'fault', 'conductor_group', 'retired',
'sharded']:
if getattr(parsed_args, field) is not None:
params[field] = getattr(parsed_args, field)
for field in ['provision_state', 'driver', 'resource_class',
'chassis', 'conductor', 'owner', 'lessee',
'description_contains', 'shards', 'parent_node']:
if getattr(parsed_args, field):
params[field] = getattr(parsed_args, field)
if parsed_args.include_children:
params['include_children'] = True
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))
[docs]
class MaintenanceSetBaremetalNode(command.Command):
"""Set baremetal node to maintenance mode"""
log = logging.getLogger(__name__ + ".MaintenanceSetBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(MaintenanceSetBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
parser.add_argument(
'--reason',
metavar='<reason>',
default=None,
help=_("Reason for setting maintenance mode."))
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_maintenance(
node, True, maint_reason=parsed_args.reason)
[docs]
class MaintenanceUnsetBaremetalNode(command.Command):
"""Unset baremetal node from maintenance mode"""
log = logging.getLogger(__name__ + ".MaintenanceUnsetBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(MaintenanceUnsetBaremetalNode,
self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_maintenance(node, False)
[docs]
class ManageBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'manage'"""
log = logging.getLogger(__name__ + ".ManageBaremetalNode")
PROVISION_STATE = 'manage'
[docs]
class UnholdBaremetalNode(ProvisionStateBaremetalNode):
"""Set provision state of baremetal node to 'unhold'"""
log = logging.getLogger(__name__ + ".UnholdBaremetalNode")
PROVISION_STATE = 'unhold'
[docs]
class PassthruCallBaremetalNode(command.Command):
"""Call a vendor passthru method for a node"""
log = logging.getLogger(__name__ + ".PassthruCallBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(PassthruCallBaremetalNode, self).get_parser(
prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
parser.add_argument(
'method',
metavar='<method>',
help=_("Vendor passthru method to be executed")
)
parser.add_argument(
'--arg',
metavar='<key=value>',
action='append',
help=_("Argument to pass to the passthru method (repeat option "
"to specify multiple arguments)")
)
parser.add_argument(
'--http-method',
metavar='<http-method>',
choices=v1_utils.HTTP_METHODS,
default='POST',
help=(_("The HTTP method to use in the passthru request. One of "
"%s. Defaults to POST.") %
oscutils.format_list(v1_utils.HTTP_METHODS))
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
arguments = utils.key_value_pairs_to_dict(parsed_args.arg)
resp = baremetal_client.node.vendor_passthru(
parsed_args.node,
parsed_args.method,
http_method=parsed_args.http_method,
args=arguments)
if resp:
# Print the raw response; we don't know how it should be formatted
print(str(resp.to_dict()))
[docs]
class PassthruListBaremetalNode(command.Lister):
"""List vendor passthru methods for a node"""
log = logging.getLogger(__name__ + ".PassthruListBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(PassthruListBaremetalNode, self).get_parser(
prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
methods = baremetal_client.node.get_vendor_passthru_methods(
parsed_args.node)
data = []
for method, response in methods.items():
response['name'] = method
response['http_methods'] = oscutils.format_list(
response['http_methods'])
data.append(response)
return (
res_fields.VENDOR_PASSTHRU_METHOD_RESOURCE.labels,
(oscutils.get_dict_properties(
s, res_fields.VENDOR_PASSTHRU_METHOD_RESOURCE.fields)
for s in data))
[docs]
class PowerBaremetalNode(command.Command):
"""Base power state class, for setting the power of a node"""
log = logging.getLogger(__name__ + ".PowerBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(PowerBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
parser.add_argument(
'--power-timeout',
metavar='<power-timeout>',
default=None,
type=int,
help=_("Timeout (in seconds, positive integer) to wait for the "
"target power state before erroring out.")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
soft = getattr(parsed_args, 'soft', False)
for node in parsed_args.nodes:
baremetal_client.node.set_power_state(
node, self.POWER_STATE, soft,
timeout=parsed_args.power_timeout)
[docs]
class PowerOffBaremetalNode(PowerBaremetalNode):
"""Power off a node"""
log = logging.getLogger(__name__ + ".PowerOffBaremetalNode")
POWER_STATE = 'off'
[docs]
def get_parser(self, prog_name):
parser = super(PowerOffBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'--soft',
dest='soft',
action='store_true',
default=False,
help=_("Request graceful power-off.")
)
return parser
[docs]
class PowerOnBaremetalNode(PowerBaremetalNode):
"""Power on a node"""
log = logging.getLogger(__name__ + ".PowerOnBaremetalNode")
POWER_STATE = 'on'
[docs]
class ProvideBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'provide'"""
log = logging.getLogger(__name__ + ".ProvideBaremetalNode")
PROVISION_STATE = 'provide'
[docs]
class RebootBaremetalNode(command.Command):
"""Reboot baremetal node"""
log = logging.getLogger(__name__ + ".RebootBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(RebootBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
parser.add_argument(
'--soft',
dest='soft',
action='store_true',
default=False,
help=_("Request Graceful reboot.")
)
parser.add_argument(
'--power-timeout',
metavar='<power-timeout>',
default=None,
type=int,
help=_("Timeout (in seconds, positive integer) to wait for the "
"target power state before erroring out.")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_power_state(
node, 'reboot', parsed_args.soft,
timeout=parsed_args.power_timeout)
[docs]
class RebuildBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'rebuild'"""
log = logging.getLogger(__name__ + ".RebuildBaremetalNode")
PROVISION_STATE = 'rebuild'
[docs]
def get_parser(self, prog_name):
parser = super(RebuildBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'--config-drive',
metavar='<config-drive>',
default=None,
help=CONFIG_DRIVE_ARG_HELP)
parser.add_argument(
'--deploy-steps',
metavar='<deploy-steps>',
required=False,
default=None,
help=_("The deploy steps in JSON format. May be the path to a "
"file containing the deploy steps; OR '-', with the deploy "
"steps being read from standard input; OR a string. The "
"value should be a list of deploy-step dictionaries; each "
"dictionary should have keys 'interface', 'step', "
"'priority' and optional key 'args'."))
return parser
[docs]
class RescueBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'rescue'"""
log = logging.getLogger(__name__ + ".RescueBaremetalNode")
PROVISION_STATE = 'rescue'
[docs]
def get_parser(self, prog_name):
parser = super(RescueBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'--rescue-password',
metavar='<rescue-password>',
required=True,
default=None,
help=("The password that will be used to login to the rescue "
"ramdisk. The value should be a non-empty string."))
return parser
[docs]
class SecurebootOnBaremetalNode(command.Command):
"""Turn secure boot on"""
log = logging.getLogger(__name__ + ".SecurebootOnBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(SecurebootOnBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Name or UUID of the node")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_secure_boot(node, 'on')
[docs]
class SecurebootOffBaremetalNode(command.Command):
"""Turn secure boot off"""
log = logging.getLogger(__name__ + ".SecurebootOffBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(SecurebootOffBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Name or UUID of the node")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.set_secure_boot(node, 'off')
[docs]
class SetBaremetalNode(command.Command):
"""Set baremetal properties"""
log = logging.getLogger(__name__ + ".SetBaremetalNode")
def _add_interface_args(self, parser, iface, set_help, reset_help):
grp = parser.add_mutually_exclusive_group()
grp.add_argument(
'--%s-interface' % iface,
metavar='<%s_interface>' % iface,
help=set_help
)
grp.add_argument(
'--reset-%s-interface' % iface,
action='store_true',
help=reset_help
)
[docs]
def get_parser(self, prog_name):
parser = super(SetBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes."),
)
parser.add_argument(
"--instance-uuid",
metavar="<uuid>",
help=_("Set instance UUID of node to <uuid>"),
)
parser.add_argument(
"--name",
metavar="<name>",
help=_("Set the name of the node"),
)
parser.add_argument(
"--chassis-uuid",
metavar="<chassis UUID>",
help=_("Set the chassis for the node"),
)
parser.add_argument(
"--driver",
metavar="<driver>",
help=_("Set the driver for the node"),
)
self._add_interface_args(
parser, 'bios',
set_help=_('Set the BIOS interface for the node'),
reset_help=_('Reset the BIOS interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'boot',
set_help=_('Set the boot interface for the node'),
reset_help=_('Reset the boot interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'console',
set_help=_('Set the console interface for the node'),
reset_help=_('Reset the console interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'deploy',
set_help=_('Set the deploy interface for the node'),
reset_help=_('Reset the deploy interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'firmware',
set_help=_('Set the firmware interface for the node'),
reset_help=_('Reset the firmware interface for its hardware '
'type default'),
)
self._add_interface_args(
parser, 'inspect',
set_help=_('Set the inspect interface for the node'),
reset_help=_('Reset the inspect interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'management',
set_help=_('Set the management interface for the node'),
reset_help=_('Reset the management interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'network',
set_help=_('Set the network interface for the node'),
reset_help=_('Reset the network interface to its hardware type '
'default'),
)
parser.add_argument(
'--network-data',
metavar="<network data>",
dest='network_data',
help=NETWORK_DATA_ARG_HELP
)
self._add_interface_args(
parser, 'power',
set_help=_('Set the power interface for the node'),
reset_help=_('Reset the power interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'raid',
set_help=_('Set the RAID interface for the node'),
reset_help=_('Reset the RAID interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'rescue',
set_help=_('Set the rescue interface for the node'),
reset_help=_('Reset the rescue interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'storage',
set_help=_('Set the storage interface for the node'),
reset_help=_('Reset the storage interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'vendor',
set_help=_('Set the vendor interface for the node'),
reset_help=_('Reset the vendor interface to its hardware type '
'default'),
)
parser.add_argument(
'--reset-interfaces',
action='store_true', default=None,
help=_('Reset all interfaces not specified explicitly to their '
'default implementations. Only valid with --driver.'),
)
parser.add_argument(
'--resource-class',
metavar='<resource_class>',
help=_('Set the resource class for the node'),
)
parser.add_argument(
'--conductor-group',
metavar='<conductor_group>',
help=_('Set the conductor group for the node'),
)
clean = parser.add_mutually_exclusive_group()
clean.add_argument(
'--automated-clean',
action='store_true',
default=None,
help=_('Enable automated cleaning for the node'))
clean.add_argument(
'--no-automated-clean',
action='store_false',
dest='automated_clean',
default=None,
help=_('Explicitly disable automated cleaning for the node'))
parser.add_argument(
'--protected',
action='store_true',
help=_('Mark the node as protected'),
)
parser.add_argument(
'--protected-reason',
metavar='<protected_reason>',
help=_('Set the reason of marking the node as protected'),
)
parser.add_argument(
'--retired',
action='store_true',
help=_('Mark the node as retired'),
)
parser.add_argument(
'--retired-reason',
metavar='<retired_reason>',
help=_('Set the reason of marking the node as retired'),
)
parser.add_argument(
'--target-raid-config',
metavar='<target_raid_config>',
help=_('Set the target RAID configuration (JSON) for the node. '
'This can be one of: 1. a file containing YAML data of the '
'RAID configuration; 2. "-" to read the contents from '
'standard input; or 3. a valid JSON string.'),
)
parser.add_argument(
"--property",
metavar="<key=value>",
action='append',
help=_('Property to set on this baremetal node '
'(repeat option to set multiple properties)'),
)
parser.add_argument(
"--extra",
metavar="<key=value>",
action='append',
help=_('Extra to set on this baremetal node '
'(repeat option to set multiple extras)'),
)
parser.add_argument(
"--driver-info",
metavar="<key=value>",
action='append',
help=_('Driver information to set on this baremetal node '
'(repeat option to set multiple driver infos)'),
)
parser.add_argument(
"--instance-info",
metavar="<key=value>",
action='append',
help=_('Instance information to set on this baremetal node '
'(repeat option to set multiple instance infos)'),
)
parser.add_argument(
"--owner",
metavar='<owner>',
help=_('Set the owner for the node')),
parser.add_argument(
"--lessee",
metavar='<lessee>',
help=_('Set the lessee for the node')),
parser.add_argument(
"--description",
metavar='<description>',
help=_('Set the description for the node'),
)
parser.add_argument(
"--shard",
metavar='<shard>',
help=_('Set the shard for the node'),
)
parser.add_argument(
"--parent-node",
metavar='<parent_node>',
help=_('Set the parent node for the node'),
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
if parsed_args.name and len(parsed_args.nodes) > 1:
raise exc.CommandError(
_("--name cannot be used with more than one node"))
if parsed_args.instance_uuid and len(parsed_args.nodes) > 1:
raise exc.CommandError(
_("--instance-uuid cannot be used with more than one node"))
baremetal_client = self.app.client_manager.baremetal
# NOTE(rloo): Do this before updating the rest. Otherwise, it won't
# work if parsed_args.node is the name and the name is
# also being modified.
if parsed_args.target_raid_config:
raid_config = parsed_args.target_raid_config
raid_config = utils.handle_json_arg(raid_config,
'target_raid_config')
for node in parsed_args.nodes:
baremetal_client.node.set_target_raid_config(node, raid_config)
properties = []
for field in ['instance_uuid', 'name',
'chassis_uuid', 'driver', 'resource_class',
'conductor_group', 'protected', 'protected_reason',
'retired', 'retired_reason', 'owner', 'lessee',
'description', 'shard', 'parent_node']:
value = getattr(parsed_args, field)
if value:
properties.extend(utils.args_array_to_patch(
'add', ["%s=%s" % (field, value)]))
if parsed_args.automated_clean is not None:
properties.extend(utils.args_array_to_patch(
'add', ["automated_clean=%s" % parsed_args.automated_clean]))
if parsed_args.reset_interfaces and not parsed_args.driver:
raise exc.CommandError(
_("--reset-interfaces can only be specified with --driver"))
for iface in SUPPORTED_INTERFACES:
field = '%s_interface' % iface
if getattr(parsed_args, field):
properties.extend(utils.args_array_to_patch(
'add',
["%s_interface=%s" % (iface,
getattr(parsed_args, field))]))
elif getattr(parsed_args, 'reset_%s_interface' % iface):
properties.extend(utils.args_array_to_patch(
'remove', ['%s_interface' % iface]))
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]))
if parsed_args.network_data:
network_data = utils.handle_json_arg(
parsed_args.network_data, 'static network configuration')
network_data = ["network_data=%s" % json.dumps(network_data)]
properties.extend(utils.args_array_to_patch('add', network_data))
if properties:
for node in parsed_args.nodes:
baremetal_client.node.update(
node, properties,
reset_interfaces=parsed_args.reset_interfaces)
elif not parsed_args.target_raid_config:
self.log.warning("Please specify what to set.")
[docs]
class ShowBaremetalNode(command.ShowOne):
"""Show baremetal node details"""
log = logging.getLogger(__name__ + ".ShowBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ShowBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
"node",
metavar="<node>",
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=_('<node> is an instance UUID.'))
parser.add_argument(
'--fields',
nargs='+',
dest='fields',
metavar='<field>',
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
[docs]
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)
node.pop("ports", None)
node.pop('portgroups', None)
node.pop('states', None)
node.pop('volume', None)
if not fields or 'chassis_uuid' in fields:
node.setdefault('chassis_uuid', '')
return self.dict2columns(node)
[docs]
class UndeployBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'deleted'"""
log = logging.getLogger(__name__ + ".UndeployBaremetalNode")
PROVISION_STATE = 'deleted'
[docs]
class UnrescueBaremetalNode(ProvisionStateWithWait):
"""Set provision state of baremetal node to 'unrescue'"""
log = logging.getLogger(__name__ + ".UnrescueBaremetalNode")
PROVISION_STATE = 'unrescue'
[docs]
class UnsetBaremetalNode(command.Command):
"""Unset baremetal properties"""
log = logging.getLogger(__name__ + ".UnsetBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(UnsetBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
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(
"--resource-class",
dest='resource_class',
action='store_true',
help=_("Unset the resource class of the node"),
)
parser.add_argument(
"--target-raid-config",
action='store_true',
help=_("Unset the target RAID configuration of the node"),
)
parser.add_argument(
'--property',
metavar='<key>',
action='append',
help=_('Property to unset on this baremetal node '
'(repeat option to unset multiple properties)'),
)
parser.add_argument(
"--extra",
metavar="<key>",
action='append',
help=_('Extra to unset on this baremetal node '
'(repeat option to unset multiple extras)'),
)
parser.add_argument(
"--driver-info",
metavar="<key>",
action='append',
help=_('Driver information to unset on this baremetal node '
'(repeat option to unset multiple items '
'in driver information)'),
)
parser.add_argument(
"--instance-info",
metavar="<key>",
action='append',
help=_('Instance information to unset on this baremetal node '
'(repeat option to unset multiple instance information)'),
)
parser.add_argument(
"--chassis-uuid",
dest='chassis_uuid',
action='store_true',
help=_('Unset chassis UUID on this baremetal node'),
)
parser.add_argument(
"--bios-interface",
dest='bios_interface',
action='store_true',
help=_('Unset BIOS interface on this baremetal node'),
)
parser.add_argument(
"--boot-interface",
dest='boot_interface',
action='store_true',
help=_('Unset boot interface on this baremetal node'),
)
parser.add_argument(
"--console-interface",
dest='console_interface',
action='store_true',
help=_('Unset console interface on this baremetal node'),
)
parser.add_argument(
"--deploy-interface",
dest='deploy_interface',
action='store_true',
help=_('Unset deploy interface on this baremetal node'),
)
parser.add_argument(
"--firmware-interface",
dest='firmware_interface',
action='store_true',
help=_('Unset firmware interface on this baremetal node'),
)
parser.add_argument(
"--inspect-interface",
dest='inspect_interface',
action='store_true',
help=_('Unset inspect interface on this baremetal node'),
)
parser.add_argument(
'--network-data',
action='store_true',
help=_("Unset network data on this baremetal port.")
)
parser.add_argument(
"--management-interface",
dest='management_interface',
action='store_true',
help=_('Unset management interface on this baremetal node'),
)
parser.add_argument(
"--network-interface",
dest='network_interface',
action='store_true',
help=_('Unset network interface on this baremetal node'),
)
parser.add_argument(
"--power-interface",
dest='power_interface',
action='store_true',
help=_('Unset power interface on this baremetal node'),
)
parser.add_argument(
"--raid-interface",
dest='raid_interface',
action='store_true',
help=_('Unset RAID interface on this baremetal node'),
)
parser.add_argument(
"--rescue-interface",
dest='rescue_interface',
action='store_true',
help=_('Unset rescue interface on this baremetal node'),
)
parser.add_argument(
"--storage-interface",
dest='storage_interface',
action='store_true',
help=_('Unset storage interface on this baremetal node'),
)
parser.add_argument(
"--vendor-interface",
dest='vendor_interface',
action='store_true',
help=_('Unset vendor interface on this baremetal node'),
)
parser.add_argument(
"--conductor-group",
action="store_true",
help=_('Unset conductor group for this baremetal node (the '
'default group will be used)'),
)
parser.add_argument(
"--automated-clean",
action="store_true",
help=_('Unset automated clean option on this baremetal node '
'(the value from configuration will be used)'),
)
parser.add_argument(
"--protected",
action="store_true",
help=_('Unset the protected flag on the node'),
)
parser.add_argument(
"--protected-reason",
action="store_true",
help=_('Unset the protected reason (gets unset automatically when '
'protected is unset)'),
)
parser.add_argument(
"--retired",
action="store_true",
help=_('Unset the retired flag on the node'),
)
parser.add_argument(
"--retired-reason",
action="store_true",
help=_('Unset the retired reason (gets unset automatically when '
'retired is unset)'),
)
parser.add_argument(
"--owner",
action="store_true",
help=_('Unset the owner field of the node'),
)
parser.add_argument(
"--lessee",
action="store_true",
help=_('Unset the lessee field of the node'),
)
parser.add_argument(
"--description",
action="store_true",
help=_('Unset the description field of the node'),
)
parser.add_argument(
"--shard",
action="store_true",
help=_('Unset the shard field of the node'),
)
parser.add_argument(
"--parent-node",
action="store_true",
help=_('Unset the parent node field of the node'),
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
# NOTE(rloo): Do this before removing the rest. Otherwise, it won't
# work if parsed_args.node is the name and the name is
# also being removed.
if parsed_args.target_raid_config:
for node in parsed_args.nodes:
baremetal_client.node.set_target_raid_config(node, {})
properties = []
for field in ['instance_uuid', 'name', 'chassis_uuid',
'resource_class', 'conductor_group', 'automated_clean',
'bios_interface', 'boot_interface', 'console_interface',
'deploy_interface', 'firmware_interface',
'inspect_interface',
'management_interface', 'network_interface',
'power_interface', 'raid_interface', 'rescue_interface',
'storage_interface', 'vendor_interface',
'protected', 'protected_reason', 'retired',
'retired_reason', 'owner', 'lessee', 'description',
'shard', 'parent_node']:
if getattr(parsed_args, field):
properties.extend(utils.args_array_to_patch('remove', [field]))
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]))
if parsed_args.network_data:
properties.extend(utils.args_array_to_patch(
'remove', ["network_data"]))
if properties:
for node in parsed_args.nodes:
baremetal_client.node.update(node, properties)
elif not parsed_args.target_raid_config:
self.log.warning("Please specify what to unset.")
[docs]
class ValidateBaremetalNode(command.Lister):
"""Validate a node's driver interfaces"""
log = logging.getLogger(__name__ + ".ValidateBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ValidateBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node"))
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
interfaces = baremetal_client.node.validate(parsed_args.node)._info
data = []
for key, value in interfaces.items():
interface = {'interface': key}
interface.update(value)
data.append(interface)
field_labels = ['Interface', 'Result', 'Reason']
fields = ['interface', 'result', 'reason']
data = oscutils.sort_items(data, 'interface')
return (field_labels,
(oscutils.get_dict_properties(s, fields) for s in data))
[docs]
class VifListBaremetalNode(command.Lister):
"""Show attached VIFs for a node"""
log = logging.getLogger(__name__ + ".VifListBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(VifListBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
columns = res_fields.VIF_RESOURCE.fields
labels = res_fields.VIF_RESOURCE.labels
baremetal_client = self.app.client_manager.baremetal
data = baremetal_client.node.vif_list(parsed_args.node)
return (labels,
(oscutils.get_item_properties(s, columns) for s in data))
[docs]
class VifAttachBaremetalNode(command.Command):
"""Attach VIF to a given node"""
log = logging.getLogger(__name__ + ".VifAttachBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(VifAttachBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
parser.add_argument(
'vif_id',
metavar='<vif-id>',
help=_("Name or UUID of the VIF to attach to a node.")
)
parser.add_argument(
'--port-uuid',
metavar='<port-uuid>',
help=_("UUID of the baremetal port to attach the VIF to.")
)
parser.add_argument(
'--vif-info',
metavar='<key=value>',
action='append',
help=_("Record arbitrary key/value metadata. "
"Can be specified multiple times. The mandatory 'id' "
"parameter cannot be specified as a key."))
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
fields = utils.key_value_pairs_to_dict(parsed_args.vif_info or [])
if parsed_args.port_uuid:
fields['port_uuid'] = parsed_args.port_uuid
baremetal_client.node.vif_attach(parsed_args.node, parsed_args.vif_id,
**fields)
[docs]
class VifDetachBaremetalNode(command.Command):
"""Detach VIF from a given node"""
log = logging.getLogger(__name__ + ".VifDetachBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(VifDetachBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
parser.add_argument(
'vif_id',
metavar='<vif-id>',
help=_("Name or UUID of the VIF to detach from a node.")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
baremetal_client.node.vif_detach(parsed_args.node, parsed_args.vif_id)
[docs]
class InjectNmiBaremetalNode(command.Command):
"""Inject NMI to baremetal node"""
log = logging.getLogger(__name__ + ".InjectNmiBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(InjectNmiBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'nodes',
metavar='<node>',
nargs='+',
help=_("Names or UUID's of the nodes.")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
for node in parsed_args.nodes:
baremetal_client.node.inject_nmi(node)
[docs]
class ListTraitsBaremetalNode(command.Lister):
"""List a node's traits."""
log = logging.getLogger(__name__ + ".ListTraitsBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ListTraitsBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node"))
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
labels = res_fields.TRAIT_RESOURCE.labels
baremetal_client = self.app.client_manager.baremetal
traits = baremetal_client.node.get_traits(parsed_args.node)
return (labels, [[trait] for trait in traits])
[docs]
class AddTraitBaremetalNode(command.Command):
"""Add traits to a node."""
log = logging.getLogger(__name__ + ".AddTraitBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(AddTraitBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node"))
parser.add_argument(
'traits',
nargs='+',
metavar='<trait>',
help=_("Trait(s) to add"))
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
failures = []
for trait in parsed_args.traits:
try:
baremetal_client.node.add_trait(parsed_args.node, trait)
print(_('Added trait %s') % trait)
except exc.ClientException as e:
failures.append(_("Failed to add trait %(trait)s: %(error)s")
% {'trait': trait, 'error': e})
if failures:
raise exc.ClientException("\n".join(failures))
[docs]
class RemoveTraitBaremetalNode(command.Command):
"""Remove trait(s) from a node."""
log = logging.getLogger(__name__ + ".RemoveTraitBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(RemoveTraitBaremetalNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node"))
all_or_trait = parser.add_mutually_exclusive_group(required=True)
all_or_trait.add_argument(
'--all',
dest='remove_all',
action='store_true',
help=_("Remove all traits"))
all_or_trait.add_argument(
'traits',
metavar='<trait>',
nargs='*',
default=[],
help=_("Trait(s) to remove"))
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
failures = []
if parsed_args.remove_all:
baremetal_client.node.remove_all_traits(parsed_args.node)
else:
for trait in parsed_args.traits:
try:
baremetal_client.node.remove_trait(parsed_args.node, trait)
print(_('Removed trait %s') % trait)
except exc.ClientException as e:
failures.append(_("Failed to remove trait %(trait)s: "
"%(error)s")
% {'trait': trait, 'error': e})
if failures:
raise exc.ClientException("\n".join(failures))
[docs]
class ListBIOSSettingBaremetalNode(command.Lister):
"""List a node's BIOS settings."""
log = logging.getLogger(__name__ + ".ListBIOSSettingBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ListBIOSSettingBaremetalNode, self).get_parser(
prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
display_group = parser.add_mutually_exclusive_group(required=False)
display_group.add_argument(
'--long',
default=False,
help=_("Show detailed information about the BIOS settings."),
action='store_true')
display_group.add_argument(
'--fields',
nargs='+',
dest='fields',
metavar='<field>',
action='append',
default=[],
choices=res_fields.BIOS_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
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
labels = res_fields.BIOS_RESOURCE.labels
fields = res_fields.BIOS_RESOURCE.fields
params = {}
if parsed_args.long:
params['detail'] = parsed_args.long
fields = res_fields.BIOS_DETAILED_RESOURCE.fields
labels = res_fields.BIOS_DETAILED_RESOURCE.labels
elif parsed_args.fields:
params['detail'] = False
fields = itertools.chain.from_iterable(parsed_args.fields)
resource = res_fields.Resource(list(fields))
fields = resource.fields
labels = resource.labels
params['fields'] = fields
self.log.debug("params(%s)", params)
baremetal_client = self.app.client_manager.baremetal
settings = baremetal_client.node.list_bios_settings(parsed_args.node,
**params)
return (labels,
(oscutils.get_dict_properties(s, fields) for s in settings))
[docs]
class BIOSSettingShowBaremetalNode(command.ShowOne):
"""Show a specific BIOS setting for a node."""
log = logging.getLogger(__name__ + ".BIOSSettingShowBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(BIOSSettingShowBaremetalNode, self).get_parser(
prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
parser.add_argument(
'setting_name',
metavar='<setting name>',
help=_("Setting name to show")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
setting = baremetal_client.node.get_bios_setting(
parsed_args.node, parsed_args.setting_name)
setting.pop("links", None)
return self.dict2columns(setting)
[docs]
class NodeHistoryList(command.Lister):
"""Get history events for a baremetal node."""
log = logging.getLogger(__name__ + ".NodeHistoryList")
[docs]
def get_parser(self, prog_name):
parser = super(NodeHistoryList, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node.")
)
parser.add_argument(
'--long',
default=False,
help=_("Show detailed information about the node history events."),
action='store_true')
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
if parsed_args.long:
labels = res_fields.NODE_HISTORY_DETAILED_RESOURCE.labels
fields = res_fields.NODE_HISTORY_DETAILED_RESOURCE.fields
else:
labels = res_fields.NODE_HISTORY_RESOURCE.labels
fields = res_fields.NODE_HISTORY_RESOURCE.fields
data = baremetal_client.node.get_history_list(
parsed_args.node,
parsed_args.long)
return (labels,
(oscutils.get_dict_properties(s, fields) for s in data))
[docs]
class NodeHistoryEventGet(command.ShowOne):
"""Get history event for a baremetal node."""
log = logging.getLogger(__name__ + ".NodeHistoryEventGet")
[docs]
def get_parser(self, prog_name):
parser = super(NodeHistoryEventGet, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node.")
)
parser.add_argument(
'event',
metavar='<event>',
help=_("UUID of the event.")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
data = baremetal_client.node.get_history_event(
parsed_args.node,
parsed_args.event)
data.pop('links')
return self.dict2columns(data)
[docs]
class NodeInventorySave(command.Command):
"""Get hardware inventory of a node (in JSON format) or save it to file."""
log = logging.getLogger(__name__ + ".NodeInventorySave")
[docs]
def get_parser(self, prog_name):
parser = super(NodeInventorySave, self).get_parser(prog_name)
parser.add_argument(
"node",
metavar="<node>",
help=_("Name or UUID of the node"))
parser.add_argument("--file",
metavar="<filename>",
help="Save inspection data to file with name "
"(default: stdout).")
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
inventory = baremetal_client.node.get_inventory(parsed_args.node)
if parsed_args.file:
with open(parsed_args.file, 'w') as fp:
json.dump(inventory, fp)
else:
json.dump(inventory, sys.stdout)
[docs]
class NodeChildrenList(command.ShowOne):
"""Get a list of nodes associated as children."""
log = logging.getLogger(__name__ + ".NodeChildrenList")
[docs]
def get_parser(self, prog_name):
parser = super(NodeChildrenList, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node.")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
labels = res_fields.CHILDREN_RESOURCE.labels
data = baremetal_client.node.list_children_of_node(
parsed_args.node)
return (labels, [[node] for node in data])
[docs]
class ListFirmwareComponentBaremetalNode(command.Lister):
"""List all Firmware Components of a node"""
log = logging.getLogger(__name__ + ".ListFirmwareComponentBaremetalNode")
[docs]
def get_parser(self, prog_name):
parser = super(ListFirmwareComponentBaremetalNode, self).get_parser(
prog_name)
parser.add_argument(
'node',
metavar='<node>',
help=_("Name or UUID of the node")
)
return parser
[docs]
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
labels = res_fields.FIRMWARE_RESOURCE.labels
fields = res_fields.FIRMWARE_RESOURCE.fields
baremetal_client = self.app.client_manager.baremetal
components = baremetal_client.node.list_firmware_components(
parsed_args.node)
return (labels,
(oscutils.get_dict_properties(s, fields) for s in components))