Add 'overcloud node clean' command to run metadata cleaning on nodes

This is a manual replacement for the disabled automated cleaning.

Depends-On: I909aad89bb18bb416e9749395970617d45b247ee
Change-Id: I8e7f9b1122ff697adb4538233242c7b48420b17b
This commit is contained in:
Dmitry Tantsur 2018-04-27 14:53:56 +02:00
parent 6031cf2e80
commit 373a4b6b2e
5 changed files with 165 additions and 6 deletions

View File

@ -0,0 +1,6 @@
---
features:
- |
Adds new command to run metadata cleaning on nodes::
openstack overcloud node clean [--all-manageable|uuid1,uuid2,..]

View File

@ -64,6 +64,7 @@ openstack.tripleoclient.v1 =
overcloud_node_introspect = tripleoclient.v1.overcloud_node:IntrospectNode
overcloud_node_provide = tripleoclient.v1.overcloud_node:ProvideNode
overcloud_node_discover = tripleoclient.v1.overcloud_node:DiscoverNode
overcloud_node_clean = tripleoclient.v1.overcloud_node:CleanNode
overcloud_parameters_set = tripleoclient.v1.overcloud_parameters:SetParameters
overcloud_plan_create = tripleoclient.v1.overcloud_plan:CreatePlan
overcloud_plan_delete = tripleoclient.v1.overcloud_plan:DeletePlan

View File

@ -114,11 +114,11 @@ class TestBaremetalWorkflows(utils.TestCommand):
'node_uuids': [],
})
def test_format_provide_errors(self):
def test_format_errors(self):
payload = {'message': [{'result': 'Error1a\nError1b'},
{'result': 'Error2a\nError2b\n'}]}
error_string = baremetal._format_provide_errors(payload)
error_string = baremetal._format_errors(payload)
self.assertEqual(error_string, "Error1b\nError2b")
def test_provide_error_with_format_message(self):
@ -306,3 +306,63 @@ class TestBaremetalWorkflows(utils.TestCommand):
'tripleo.baremetal.v1.configure_manageable_nodes',
workflow_input={}
)
def test_clean_nodes_success(self):
self.websocket.wait_for_messages.return_value = self.message_success
baremetal.clean_nodes(self.app.client_manager, node_uuids=[])
self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal.v1.clean_nodes',
workflow_input={
'node_uuids': [],
})
def test_clean_nodes_error(self):
self.websocket.wait_for_messages.return_value = self.message_failed
self.assertRaises(
exceptions.NodeConfigurationError,
baremetal.clean_nodes,
self.app.client_manager,
node_uuids=[]
)
self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal.v1.clean_nodes',
workflow_input={
'node_uuids': [],
})
def test_clean_manageable_nodes_success(self):
self.websocket.wait_for_messages.return_value = iter([{
"execution": {"id": "IDID"},
"status": "SUCCESS",
"cleaned_nodes": [],
}])
baremetal.clean_manageable_nodes(
self.app.client_manager
)
self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal.v1.clean_manageable_nodes',
workflow_input={}
)
def test_clean_manageable_nodes_error(self):
self.websocket.wait_for_messages.return_value = self.message_failed
self.assertRaises(
exceptions.NodeConfigurationError,
baremetal.clean_manageable_nodes,
self.app.client_manager)
self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal.v1.clean_manageable_nodes',
workflow_input={}
)

View File

@ -122,6 +122,36 @@ class ProvideNode(command.Command):
baremetal.provide_manageable_nodes(self.app.client_manager)
class CleanNode(command.Command):
"""Run node(s) through cleaning."""
log = logging.getLogger(__name__ + ".CleanNode")
def get_parser(self, prog_name):
parser = super(CleanNode, 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 '
'cleaned'))
group.add_argument("--all-manageable",
action='store_true',
help=_("Clean all nodes currently in 'manageable'"
" state"))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
if parsed_args.node_uuids:
baremetal.clean_nodes(self.app.client_manager,
node_uuids=parsed_args.node_uuids)
else:
baremetal.clean_manageable_nodes(self.app.client_manager)
class IntrospectNode(command.Command):
"""Introspect specified nodes or all nodes in 'manageable' state."""

View File

@ -14,6 +14,8 @@
from __future__ import print_function
import six
from tripleoclient import exceptions
from tripleoclient.workflows import base
@ -78,18 +80,23 @@ def register_or_update(clients, **workflow_input):
'Exception registering nodes: {}'.format(payload['message']))
def _format_provide_errors(payload):
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(msg.get('result', '').rstrip('\n').split('\n')[-1])
errors.append(text.rstrip('\n').split('\n')[-1])
except Exception:
errors.append(msg.get('result', ''))
errors.append(text)
return '\n'.join(errors)
@ -115,7 +122,7 @@ def provide(clients, **workflow_input):
if payload['status'] != 'SUCCESS':
try:
message = _format_provide_errors(payload)
message = _format_errors(payload)
except Exception:
message = 'Failed.'
raise exceptions.NodeProvideError(
@ -330,3 +337,58 @@ def discover_and_enroll(clients, **workflow_input):
else:
raise exceptions.RegisterOrUpdateError(
'Exception discovering nodes: {}'.format(payload['message']))
def clean_nodes(clients, **workflow_input):
"""Clean Baremetal Nodes
Run the tripleo.baremetal.v1.clean_nodes Mistral workflow.
"""
workflow_client = clients.workflow_engine
tripleoclients = clients.tripleoclient
with tripleoclients.messaging_websocket() as ws:
execution = base.start_workflow(
workflow_client,
'tripleo.baremetal.v1.clean_nodes',
workflow_input={'node_uuids': workflow_input['node_uuids']}
)
for payload in base.wait_for_messages(workflow_client, ws, execution):
if payload.get('message'):
print(payload['message'])
if payload['status'] != 'SUCCESS':
message = _format_errors(payload)
raise exceptions.NodeConfigurationError(
'Error(s) cleaning nodes:\n{}'.format(message))
print('Successfully cleaned nodes')
def clean_manageable_nodes(clients, **workflow_input):
"""Clean all manageable Nodes
Run the tripleo.baremetal.v1.clean_manageable_nodes Mistral workflow.
"""
workflow_client = clients.workflow_engine
tripleoclients = clients.tripleoclient
with tripleoclients.messaging_websocket() as ws:
execution = base.start_workflow(
workflow_client,
'tripleo.baremetal.v1.clean_manageable_nodes',
workflow_input=workflow_input
)
for payload in base.wait_for_messages(workflow_client, ws, execution):
if payload.get('message'):
print(payload['message'])
if payload['status'] != 'SUCCESS':
raise exceptions.NodeConfigurationError(
'Error cleaning nodes: {}'.format(payload['message']))
print('Cleaned %d node(s)' % len(payload['cleaned_nodes']))