Add 'openstack overcloud node configure' command
Calls to the Mistral workflows to configure boot options and the root device. Change-Id: Ifd868fcdd6ed2d54b40c2e1861558d0233731be5 Depends-On: I5ba0a3710012c44822dd3b8e69662bbef04d3787 Closes-Bug: #1595205
This commit is contained in:
parent
dbe3b866a5
commit
d0b6de2b4a
|
@ -66,6 +66,7 @@ openstack.tripleoclient.v1 =
|
|||
overcloud_deploy = tripleoclient.v1.overcloud_deploy:DeployOvercloud
|
||||
overcloud_image_build = tripleoclient.v1.overcloud_image:BuildOvercloudImage
|
||||
overcloud_image_upload = tripleoclient.v1.overcloud_image:UploadOvercloudImage
|
||||
overcloud_node_configure = tripleoclient.v1.overcloud_node:ConfigureNode
|
||||
overcloud_node_delete = tripleoclient.v1.overcloud_node:DeleteNode
|
||||
overcloud_node_import = tripleoclient.v1.overcloud_node:ImportNode
|
||||
overcloud_node_introspect = tripleoclient.v1.overcloud_node:IntrospectNode
|
||||
|
|
|
@ -53,6 +53,10 @@ class NodeProvideError(WorkflowServiceError):
|
|||
"""Node Provide failed."""
|
||||
|
||||
|
||||
class NodeConfigurationError(WorkflowServiceError):
|
||||
"""Node Configuration failed."""
|
||||
|
||||
|
||||
class StateTransitionFailed(Exception):
|
||||
"""Ironic node state transition failed"""
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import tempfile
|
|||
|
||||
from openstackclient.tests import utils as test_utils
|
||||
|
||||
from tripleoclient import exceptions
|
||||
from tripleoclient.tests.v1.overcloud_node import fakes
|
||||
from tripleoclient.v1 import overcloud_node
|
||||
|
||||
|
@ -367,3 +368,176 @@ class TestImportNode(fakes.TestOvercloudNode):
|
|||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self._check_workflow_call(parsed_args, no_deploy_image=True)
|
||||
|
||||
|
||||
class TestConfigureNode(fakes.TestOvercloudNode):
|
||||
|
||||
def setUp(self):
|
||||
super(TestConfigureNode, self).setUp()
|
||||
|
||||
self.workflow = self.app.client_manager.workflow_engine
|
||||
client = self.app.client_manager.tripleoclient
|
||||
self.websocket = client.messaging_websocket()
|
||||
self.websocket.wait_for_message.return_value = {
|
||||
"status": "SUCCESS",
|
||||
"message": ""
|
||||
}
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = overcloud_node.ConfigureNode(self.app, None)
|
||||
|
||||
self.workflow_input = {'queue_name': 'UUID4',
|
||||
'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}
|
||||
|
||||
def test_configure_all_manageable_nodes(self):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
['--all-manageable'],
|
||||
[('all_manageable', True)])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure_manageable_nodes',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
def test_failed_to_configure_all_manageable_nodes(self):
|
||||
self.websocket.wait_for_message.return_value = {
|
||||
"status": "FAILED",
|
||||
"message": "Test failure."
|
||||
}
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, ['--all-manageable'], [])
|
||||
self.assertRaises(exceptions.NodeConfigurationError,
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure_manageable_nodes',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
def test_configure_specified_nodes(self):
|
||||
argslist = ['node_uuid1', 'node_uuid2']
|
||||
verifylist = [('node_uuids', ['node_uuid1', 'node_uuid2'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.workflow_input['node_uuids'] = ['node_uuid1', 'node_uuid2']
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
def test_failed_to_configure_specified_nodes(self):
|
||||
self.websocket.wait_for_message.return_value = {
|
||||
"status": "FAILED",
|
||||
"message": "Test failure."
|
||||
}
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, ['node_uuid1'], [])
|
||||
self.assertRaises(exceptions.NodeConfigurationError,
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.workflow_input['node_uuids'] = ['node_uuid1']
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
def test_configure_no_node_or_flag_specified(self):
|
||||
self.assertRaises(test_utils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, [], [])
|
||||
|
||||
def test_configure_uuids_and_all_both_specified(self):
|
||||
argslist = ['node_id1', 'node_id2', '--all-manageable']
|
||||
verifylist = [('node_uuids', ['node_id1', 'node_id2']),
|
||||
('all_manageable', True)]
|
||||
self.assertRaises(test_utils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, argslist, verifylist)
|
||||
|
||||
def test_configure_kernel_and_ram(self):
|
||||
argslist = ['--all-manageable', '--deploy-ramdisk', 'test_ramdisk',
|
||||
'--deploy-kernel', 'test_kernel']
|
||||
verifylist = [('deploy_kernel', 'test_kernel'),
|
||||
('deploy_ramdisk', 'test_ramdisk')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.workflow_input['kernel_name'] = 'test_kernel'
|
||||
self.workflow_input['ramdisk_name'] = 'test_ramdisk'
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure_manageable_nodes',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
def test_configure_instance_boot_option(self):
|
||||
argslist = ['--all-manageable', '--instance-boot-option', 'netboot']
|
||||
verifylist = [('instance_boot_option', 'netboot')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.workflow_input['instance_boot_option'] = 'netboot'
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure_manageable_nodes',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
def test_configure_root_device(self):
|
||||
argslist = ['--all-manageable',
|
||||
'--root-device', 'smallest',
|
||||
'--root-device-minimum-size', '2',
|
||||
'--overwrite-root-device-hints']
|
||||
verifylist = [('root_device', 'smallest'),
|
||||
('root_device_minimum_size', 2),
|
||||
('overwrite_root_device_hints', True)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.workflow_input['root_device'] = 'smallest'
|
||||
self.workflow_input['root_device_minimum_size'] = 2
|
||||
self.workflow_input['overwrite_root_device_hints'] = True
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure_manageable_nodes',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
def test_configure_specified_node_with_all_arguments(self):
|
||||
argslist = ['node_id',
|
||||
'--deploy-kernel', 'test_kernel',
|
||||
'--deploy-ramdisk', 'test_ramdisk',
|
||||
'--instance-boot-option', 'netboot',
|
||||
'--root-device', 'smallest',
|
||||
'--root-device-minimum-size', '2',
|
||||
'--overwrite-root-device-hints']
|
||||
verifylist = [('node_uuids', ['node_id']),
|
||||
('deploy_kernel', 'test_kernel'),
|
||||
('deploy_ramdisk', 'test_ramdisk'),
|
||||
('instance_boot_option', 'netboot'),
|
||||
('root_device', 'smallest'),
|
||||
('root_device_minimum_size', 2),
|
||||
('overwrite_root_device_hints', True)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.workflow_input.update({'node_uuids': ['node_id'],
|
||||
'kernel_name': 'test_kernel',
|
||||
'ramdisk_name': 'test_ramdisk',
|
||||
'instance_boot_option': 'netboot',
|
||||
'root_device': 'smallest',
|
||||
'root_device_minimum_size': 2,
|
||||
'overwrite_root_device_hints': True})
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.baremetal.v1.configure',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
|
|
@ -211,3 +211,81 @@ class ImportNode(command.Command):
|
|||
baremetal.provide(self.app.client_manager,
|
||||
node_uuids=nodes_uuids,
|
||||
queue_name=queue_name)
|
||||
|
||||
|
||||
class ConfigureNode(command.Command):
|
||||
"""Configure Node boot options."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ConfigureNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ConfigureNode, self).get_parser(prog_name)
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument('node_uuids',
|
||||
nargs="*",
|
||||
metavar="<node_uuid>",
|
||||
default=[],
|
||||
help=_('Baremetal Node UUIDs for the node(s) to be '
|
||||
'configured'))
|
||||
group.add_argument("--all-manageable",
|
||||
action='store_true',
|
||||
help=_("Configure all nodes currently in "
|
||||
"'manageable' state"))
|
||||
parser.add_argument('--deploy-kernel',
|
||||
default='bm-deploy-kernel',
|
||||
help=_('Image with deploy kernel.'))
|
||||
parser.add_argument('--deploy-ramdisk',
|
||||
default='bm-deploy-ramdisk',
|
||||
help=_('Image with deploy ramdisk.'))
|
||||
parser.add_argument('--instance-boot-option',
|
||||
choices=['local', 'netboot'],
|
||||
help=_('Whether to set instances for booting from '
|
||||
'local hard drive (local) or network '
|
||||
'(netboot).'))
|
||||
parser.add_argument('--root-device',
|
||||
help=_('Define the root device for nodes. '
|
||||
'Can be either a list of device names '
|
||||
'(without /dev) to choose from or one of '
|
||||
'two strategies: largest or smallest. For '
|
||||
'it to work this command should be run '
|
||||
'after the introspection.'))
|
||||
parser.add_argument('--root-device-minimum-size',
|
||||
type=int, default=4,
|
||||
help=_('Minimum size (in GiB) of the detected '
|
||||
'root device. Used with --root-device.'))
|
||||
parser.add_argument('--overwrite-root-device-hints',
|
||||
action='store_true',
|
||||
help=_('Whether to overwrite existing root device '
|
||||
'hints when --root-device is used.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)" % parsed_args)
|
||||
|
||||
queue_name = str(uuid.uuid4())
|
||||
|
||||
if parsed_args.node_uuids:
|
||||
baremetal.configure(
|
||||
self.app.client_manager,
|
||||
node_uuids=parsed_args.node_uuids,
|
||||
queue_name=queue_name,
|
||||
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)
|
||||
)
|
||||
else:
|
||||
baremetal.configure_manageable_nodes(
|
||||
self.app.client_manager,
|
||||
queue_name=queue_name,
|
||||
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)
|
||||
)
|
||||
|
|
|
@ -172,3 +172,53 @@ def provide_manageable_nodes(clients, **workflow_input):
|
|||
'Exception providing nodes:{}'.format(payload['message']))
|
||||
|
||||
print(payload['message'])
|
||||
|
||||
|
||||
def configure(clients, **workflow_input):
|
||||
"""Configure Node boot options.
|
||||
|
||||
Run the tripleo.baremetal.v1.configure Mistral workflow.
|
||||
"""
|
||||
|
||||
workflow_client = clients.workflow_engine
|
||||
ooo_client = clients.tripleoclient
|
||||
queue_name = workflow_input['queue_name']
|
||||
|
||||
execution = workflow_client.executions.create(
|
||||
'tripleo.baremetal.v1.configure',
|
||||
workflow_input=workflow_input
|
||||
)
|
||||
|
||||
with ooo_client.messaging_websocket(queue_name) as ws:
|
||||
payload = ws.wait_for_message(execution.id)
|
||||
|
||||
if payload['status'] == 'SUCCESS':
|
||||
print('Successfully configured all nodes.')
|
||||
else:
|
||||
raise exceptions.NodeConfigurationError(
|
||||
'Failed to configure nodes: {}'.format(payload['message']))
|
||||
|
||||
|
||||
def configure_manageable_nodes(clients, **workflow_input):
|
||||
"""Configure all manageable Nodes.
|
||||
|
||||
Run the tripleo.baremetal.v1.configure_manageable_nodes Mistral workflow.
|
||||
"""
|
||||
|
||||
workflow_client = clients.workflow_engine
|
||||
ooo_client = clients.tripleoclient
|
||||
queue_name = workflow_input['queue_name']
|
||||
|
||||
execution = workflow_client.executions.create(
|
||||
'tripleo.baremetal.v1.configure_manageable_nodes',
|
||||
workflow_input=workflow_input
|
||||
)
|
||||
|
||||
with ooo_client.messaging_websocket(queue_name) as ws:
|
||||
payload = ws.wait_for_message(execution.id)
|
||||
|
||||
if payload['status'] != 'SUCCESS':
|
||||
raise exceptions.NodeConfigurationError(
|
||||
'Exception configuring nodes: {}'.format(payload['message']))
|
||||
|
||||
print(payload['message'])
|
||||
|
|
Loading…
Reference in New Issue