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:
Julie Pichon 2016-07-20 14:13:58 +00:00
parent dbe3b866a5
commit d0b6de2b4a
5 changed files with 307 additions and 0 deletions

View File

@ -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

View File

@ -53,6 +53,10 @@ class NodeProvideError(WorkflowServiceError):
"""Node Provide failed."""
class NodeConfigurationError(WorkflowServiceError):
"""Node Configuration failed."""
class StateTransitionFailed(Exception):
"""Ironic node state transition failed"""

View File

@ -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
)

View File

@ -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)
)

View File

@ -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'])