python-tripleoclient/tripleoclient/workflows/baremetal.py

581 lines
17 KiB
Python

# -*- coding: utf-8 -*-
# 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 six
from tripleo_common.actions import baremetal
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import utils
def validate_nodes(clients, nodes_json):
"""Validate nodes.
:param clients: Application client object.
:type clients: Object
:param nodes_json:
:type nodes_json: Object
:returns: Boolean
"""
context = clients.tripleoclient.create_mistral_context()
nodes = baremetal.ValidateNodes(nodes_json=nodes_json)
validated_nodes = nodes.run(context=context)
if not validated_nodes:
return True
else:
raise exceptions.RegisterOrUpdateError(validated_nodes)
def register_or_update(clients, nodes_json, kernel_name=None,
ramdisk_name=None, instance_boot_option=None):
"""Node Registration or Update
:param clients: Application client object.
:type clients: Object
:param nodes_json:
:type nodes_json: Object
:param kernel_name: Kernel to use
:type kernel_name: String
:param ramdisk_name: RAMDISK to use
:type ramdisk_name: String
:param instance_boot_option: Whether to set instances for booting from
local hard drive (local) or network
(netboot).
:type instance_boot_option: String
:returns: List
"""
context = clients.tripleoclient.create_mistral_context()
nodes = baremetal.RegisterOrUpdateNodes(
nodes_json=nodes_json,
ramdisk_name=ramdisk_name,
kernel_name=kernel_name,
instance_boot_option=instance_boot_option
)
registered_nodes = nodes.run(context=context)
if not isinstance(registered_nodes, list):
raise exceptions.RegisterOrUpdateError(registered_nodes)
else:
for node in registered_nodes:
if node.provision_state == 'enroll':
clients.baremetal.node.set_provision_state(
node_uuid=node.uuid,
state='manage'
)
print('Successfully registered node UUID {}'.format(node.uuid))
else:
print('Node UUID {} is already registered'.format(node.uuid))
return registered_nodes
def _format_errors(payload):
errors = []
messages = payload.get('message', [])
for msg in messages:
# Adapt for different formats
if isinstance(msg, six.string_types):
text = msg
else:
text = msg.get('result') or msg.get('message', '')
try:
# With multiple workflows, the error message can become
# quite large and unreadable as it gets passed from task to
# task. This attempts to keep only the last, and hopefully
# useful part.
errors.append(text.rstrip('\n').split('\n')[-1])
except Exception:
errors.append(text)
return '\n'.join(errors)
def provide(verbosity, node_uuids):
"""Provide Baremetal Nodes
:param verbosity: Verbosity level
:type verbosity: Integer
:param node_uuids: List of instance UUID(s).
:type node_uuids: List
"""
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-overcloud-node-provide.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
'node_uuids': node_uuids
}
)
print('Successfully provided nodes: {}'.format(node_uuids))
def provide_manageable_nodes(clients, verbosity=0):
"""Provide all manageable Nodes
:param clients: Application client object.
:type clients: Object
:param verbosity: Verbosity level
:type verbosity: Integer
"""
provide(
verbosity=verbosity,
node_uuids=[
i.uuid for i in clients.baremetal.node.list()
if i.provision_state == "manageable" and not i.maintenance
]
)
def introspect(clients, node_uuids, run_validations, concurrency,
node_timeout, max_retries, retry_timeout, verbosity=0):
"""Introspect Baremetal Nodes
:param clients: Application client object.
:type clients: Object
:param node_uuids: List of instance UUID(s).
:type node_uuids: List
:param run_validations: Enable or disable validations
:type run_validations: Boolean
:param concurrency: concurrency level
:type concurrency: Integer
:param verbosity: Verbosity level
:type verbosity: Integer
"""
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-baremetal-introspect.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
"node_uuids": node_uuids,
"run_validations": run_validations,
"concurrency": concurrency,
"node_timeout": node_timeout,
"max_retries": max_retries,
"retry_timeout": retry_timeout,
}
)
print('Successfully introspected nodes: {}'.format(node_uuids))
def introspect_manageable_nodes(clients, run_validations, concurrency,
node_timeout, max_retries, retry_timeout,
verbosity=0):
"""Introspect all manageable nodes
:param clients: Application client object.
:type clients: Object
:param run_validations: Enable or disable validations
:type run_validations: Boolean
:param concurrency: Concurrency level
:type concurrency: Integer
:param node_timeout: Node timeout for introspection
:type node_timeout: Integer
:param max_retries: Max retries for introspection
:type max_retries: Integer
:param retry_timeout: Max timeout to wait between retries
:type retry_timeout: Integer
:param verbosity: Verbosity level
:type verbosity: Integer
"""
introspect(
clients=clients,
node_uuids=[
i.uuid for i in clients.baremetal.node.list()
if i.provision_state == "manageable" and not i.maintenance
],
run_validations=run_validations,
concurrency=concurrency,
node_timeout=node_timeout,
max_retries=max_retries,
retry_timeout=retry_timeout,
verbosity=verbosity
)
def configure(clients, node_uuids, kernel_name='bm-deploy-kernel',
ramdisk_name='bm-deploy-ramdisk', instance_boot_option=None,
root_device=None, root_device_minimum_size=4,
overwrite_root_device_hints=False):
"""Configure Node boot options.
:param node_uuids: List of instance UUID(s).
:type node_uuids: List
:param kernel_name: Kernel to use
:type kernel_name: String
:param ramdisk_name: RAMDISK to use
:type ramdisk_name: String
:param instance_boot_option: Boot options to use
:type instance_boot_option: String
:param root_device: Path (name) of the root device.
:type root_device: String
:param root_device_minimum_size: Size of the given root device.
:type root_device_minimum_size: Integer
:param overwrite_root_device_hints: Whether to overwrite existing root
device hints when `root_device` is
used.
:type overwrite_root_device_hints: Boolean
"""
context = clients.tripleoclient.create_mistral_context()
for node_uuid in node_uuids:
boot_action = baremetal.ConfigureBootAction(
node_uuid=node_uuid,
kernel_name=kernel_name,
ramdisk_name=ramdisk_name,
instance_boot_option=instance_boot_option
).run(context=context)
if boot_action:
raise RuntimeError(boot_action)
root_device_action = baremetal.ConfigureRootDeviceAction(
node_uuid=node_uuid,
root_device=root_device,
minimum_size=root_device_minimum_size,
overwrite=overwrite_root_device_hints
)
root_device_action.run(context=context)
else:
print('Successfully configured the nodes.')
def configure_manageable_nodes(clients, kernel_name='bm-deploy-kernel',
ramdisk_name='bm-deploy-ramdisk',
instance_boot_option=None,
root_device=None, root_device_minimum_size=4,
overwrite_root_device_hints=False):
"""Configure all manageable Nodes.
kernel_name=parsed_args.deploy_kernel,
ramdisk_name=parsed_args.deploy_ramdisk,
instance_boot_option=parsed_args.instance_boot_option,
root_device=parsed_args.root_device,
root_device_minimum_size=parsed_args.root_device_minimum_size,
overwrite_root_device_hints=(parsed_args.overwrite_root_device_hints)
:param kernel_name: Kernel to use
:type kernel_name: String
:param ramdisk_name: RAMDISK to use
:type ramdisk_name: String
:param instance_boot_option: Boot options to use
:type instance_boot_option: String
:param root_device: Path (name) of the root device.
:type root_device: String
:param root_device_minimum_size: Size of the given root device.
:type root_device_minimum_size: Integer
:param overwrite_root_device_hints: Whether to overwrite existing root
device hints when `root_device` is
used.
:type overwrite_root_device_hints: Boolean
"""
configure(
clients=clients,
node_uuids=[
i.uuid for i in clients.baremetal.node.list()
if i.provision_state == "manageable" and not i.maintenance
],
kernel_name=kernel_name,
ramdisk_name=ramdisk_name,
instance_boot_option=instance_boot_option,
root_device=root_device,
root_device_minimum_size=root_device_minimum_size,
overwrite_root_device_hints=overwrite_root_device_hints
)
def create_raid_configuration(clients, node_uuids, configuration,
verbosity=0):
"""Create RAID configuration on nodes.
:param clients: application client object.
:type clients: Object
:param node_uuids: List of instance UUID(s).
:type node_uuids: List
:param configuration: RAID configuration object.
:type configuration: Object
:param verbosity: Verbosity level
:type verbosity: Integer
"""
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-baremetal-raid.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
'node_uuids': node_uuids,
'raid_configuration': configuration
}
)
print('Successfully configured RAID for nodes: {}'.format(node_uuids))
def discover_and_enroll(clients, ip_addresses, credentials, kernel_name,
ramdisk_name, instance_boot_option,
existing_nodes=None, ports=None):
"""Discover nodes and enroll baremetal nodes.
:param clients: application client object.
:type clients: Object
:param ip_addresses: List of IP addresses.
:type ip_addresses: List || String
:param credentials: Credential information object
:type credentials: Tuple
:param kernel_name: Kernel to use
:type kernel_name: String
:param ramdisk_name: RAMDISK to use
:type ramdisk_name: String
:param instance_boot_option: Boot options to use
:type instance_boot_option: String
:param existing_nodes: List of nodes already discovered. If this is
undefined this object will be set to an empty
array.
:type existing_nodes: List
:param ports: List of ports, if no ports are provided the list of ports
will be limted to [623].
:type ports: List
:returns: List
"""
if not ports:
ports = [623]
if not existing_nodes:
existing_nodes = list()
context = clients.tripleoclient.create_mistral_context()
get_candiate_nodes = baremetal.GetCandidateNodes(
ip_addresses,
ports,
credentials,
existing_nodes
)
probed_nodes = list()
for node in get_candiate_nodes.run(context=context):
probed_nodes.append(
baremetal.ProbeNode(**node).run(context=context)
)
print('Successfully probed node IP {}'.format(node['ip']))
return register_or_update(
clients=clients,
nodes_json=probed_nodes,
instance_boot_option=instance_boot_option,
kernel_name=kernel_name,
ramdisk_name=ramdisk_name
)
def clean_nodes(node_uuids, verbosity=0):
"""Clean Baremetal Nodes
:param node_uuids: List of instance UUID(s).
:type node_uuids: List
:param verbosity: Verbosity level
:type verbosity: Integer
"""
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-baremetal-clean.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
'node_uuids': node_uuids
}
)
print('Successfully cleaned nodes: {}'.format(node_uuids))
def clean_manageable_nodes(clients, verbosity=0):
"""Clean all manageable Nodes
:param clients: application client object.
:type clients: Object
:param verbosity: Verbosity level
:type verbosity: Integer
"""
clean_nodes(
node_uuids=[
i.uuid for i in clients.baremetal.node.list()
if i.provision_state == "manageable" and not i.maintenance
],
verbosity=verbosity
)
def apply_bios_configuration(node_uuids, configuration, verbosity=0):
"""Apply BIOS settings on nodes.
:param node_uuids: List of instance UUID(s).
:type node_uuids: List
:param configuration: BIOS configuration object.
:type configuration: Object
:param verbosity: Verbosity level
:type verbosity: Integer
"""
print('Applying BIOS settings for given nodes, this may take time')
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-baremetal-bios.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
'node_uuids': node_uuids,
'bios_configuration': configuration
}
)
print('Successfully applied the BIOS for nodes: {}'.format(node_uuids))
def apply_bios_configuration_on_manageable_nodes(clients, configuration,
verbosity=0):
"""Apply BIOS settings on manageable nodes.
:param clients: application client object.
:type clients: Object
:param configuration: BIOS configuration object.
:type configuration: Object
:param verbosity: Verbosity level
:type verbosity: Integer
"""
apply_bios_configuration(
node_uuids=[
i.uuid for i in clients.baremetal.node.list()
if i.provision_state == "manageable" and not i.maintenance
],
configuration=configuration,
verbosity=verbosity
)
def reset_bios_configuration(node_uuids, verbosity=0):
"""Reset BIOS settings on nodes.
:param node_uuids: List of instance UUID(s).
:type node_uuids: List
:param verbosity: Verbosity level
:type verbosity: Integer
"""
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-baremetal-bios-reset.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
'node_uuids': node_uuids
}
)
print('Successfully reset the BIOS for nodes: {}'.format(node_uuids))
def reset_bios_configuration_on_manageable_nodes(clients, verbosity=0):
"""Reset BIOS settings on manageable nodes.
:param clients: application client object.
:type clients: Object
:param verbosity: Verbosity level
:type verbosity: Integer
"""
reset_bios_configuration(
node_uuids=[
i.uuid for i in clients.baremetal.node.list()
if i.provision_state == "manageable" and not i.maintenance
],
verbosity=verbosity
)