Remove mistral when running the register_or_update workflow

This change removes all of mistral from the register_or_update workflow
by calling the required functions directly.

Story: 2007212
Task: 38442

Closes-Bug: #1866637
Change-Id: Ie85adc64cd4fcec469d6979a424d8f01b00f34f2
Signed-off-by: Kevin Carter <kecarter@redhat.com>
This commit is contained in:
Kevin Carter 2020-02-24 12:28:58 -06:00
parent ed75570496
commit ad9c7a7504
No known key found for this signature in database
GPG Key ID: CE94BD890A47B20A
10 changed files with 196 additions and 617 deletions

View File

@ -84,24 +84,26 @@ class FakeFile(FakeHandle):
self.contents = None self.contents = None
class FakeWebSocket(FakeHandle):
def wait_for_messages(self, timeout=None):
yield {
'execution_id': 'IDID',
'status': 'SUCCESS',
}
class FakeClientWrapper(object): class FakeClientWrapper(object):
def __init__(self): def __init__(self):
self.ws = FakeWebSocket()
self.object_store = FakeObjectClient()
self._instance = mock.Mock() self._instance = mock.Mock()
self.object_store = FakeObjectClient()
self._mock_websocket = mock.Mock()
self._mock_websocket.__enter__ = mock.Mock(
return_value=self._mock_websocket)
# Return False to avoid silencing exceptions
self._mock_websocket.__exit__ = mock.Mock(return_value=False)
self._mock_websocket.wait_for_messages = mock.Mock(
return_value=iter([{
"status": "SUCCESS",
"message": "Success",
"execution_id": "IDID"
}])
)
def messaging_websocket(self): def messaging_websocket(self):
return self.ws return self._mock_websocket
class FakeRunnerConfig(object): class FakeRunnerConfig(object):
@ -169,7 +171,8 @@ class FakePlaybookExecution(utils.TestCommand):
self.app.client_manager.image = mock.Mock() self.app.client_manager.image = mock.Mock()
self.app.client_manager.network = mock.Mock() self.app.client_manager.network = mock.Mock()
tc = self.app.client_manager.tripleoclient = FakeClientWrapper() tc = self.app.client_manager.tripleoclient = FakeClientWrapper()
self.app.client_manager.workflow_engine = mock.Mock() self.tripleoclient = mock.Mock()
self.workflow = self.app.client_manager.workflow_engine = mock.Mock()
stack = self.app.client_manager.orchestration = mock.Mock() stack = self.app.client_manager.orchestration = mock.Mock()
stack.stacks.get.return_value = FakeStackObject stack.stacks.get.return_value = FakeStackObject
tc.create_mistral_context = plugin.ClientWrapper( tc.create_mistral_context = plugin.ClientWrapper(
@ -177,10 +180,9 @@ class FakePlaybookExecution(utils.TestCommand):
).create_mistral_context ).create_mistral_context
# NOTE(cloudnull): When mistral is gone this should be removed. # NOTE(cloudnull): When mistral is gone this should be removed.
workflow = execution = mock.Mock() self.execution = mock.Mock()
execution.id = "IDID" self.execution.id = "IDID"
workflow.executions.create.return_value = execution self.workflow.executions.create.return_value = self.execution
self.app.client_manager.workflow_engine = workflow
config_mock = mock.patch( config_mock = mock.patch(
'tripleo_common.actions.config.GetOvercloudConfig', 'tripleo_common.actions.config.GetOvercloudConfig',
@ -207,6 +209,14 @@ class FakePlaybookExecution(utils.TestCommand):
get_key.return_value = 'keyfile-path' get_key.return_value = 'keyfile-path'
self.addCleanup(get_key.stop) self.addCleanup(get_key.stop)
self.register_or_update = mock.patch(
'tripleo_common.actions.baremetal.RegisterOrUpdateNodes.run',
autospec=True,
return_value=[mock.Mock(uuid='MOCK_NODE_UUID')]
)
self.register_or_update.start()
self.addCleanup(self.register_or_update.stop)
if ansible_mock: if ansible_mock:
get_stack = mock.patch('tripleoclient.utils.get_stack') get_stack = mock.patch('tripleoclient.utils.get_stack')
get_stack.start() get_stack.start()

View File

@ -13,46 +13,16 @@
# under the License. # under the License.
# #
import mock
from osc_lib.tests import utils
from tripleoclient import plugin
from tripleoclient.tests import fakes from tripleoclient.tests import fakes
class FakeClientWrapper(object): class TestDeleteNode(fakes.FakePlaybookExecution):
def __init__(self):
self._instance = mock.Mock()
self._mock_websocket = mock.Mock()
self._mock_websocket.__enter__ = mock.Mock(
return_value=self._mock_websocket)
# Return False to avoid silencing exceptions
self._mock_websocket.__exit__ = mock.Mock(return_value=False)
def messaging_websocket(self):
return self._mock_websocket
class TestDeleteNode(utils.TestCommand):
def setUp(self): def setUp(self):
super(TestDeleteNode, self).setUp() super(TestDeleteNode, self).setUp()
self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN")
self.app.client_manager.orchestration = mock.Mock()
self.app.client_manager.tripleoclient = FakeClientWrapper()
class TestOvercloudNode(fakes.FakePlaybookExecution):
class TestOvercloudNode(utils.TestCommand):
def setUp(self): def setUp(self):
super(TestOvercloudNode, self).setUp() super(TestOvercloudNode, self).setUp()
self.app.client_manager.baremetal = mock.Mock()
self.app.client_manager.workflow_engine = mock.Mock()
tc = self.app.client_manager.tripleoclient = FakeClientWrapper()
tc.create_mistral_context = plugin.ClientWrapper(
instance=fakes.FakeInstanceData
).create_mistral_context

View File

@ -15,21 +15,23 @@
import collections import collections
import copy import copy
import fixtures
import json import json
import mock import mock
import os import os
import tempfile import tempfile
import fixtures
from osc_lib import exceptions as oscexc from osc_lib import exceptions as oscexc
from osc_lib.tests import utils as test_utils from osc_lib.tests import utils as test_utils
import yaml import yaml
from tripleoclient import constants
from tripleoclient import exceptions from tripleoclient import exceptions
from tripleoclient import plugin from tripleoclient import plugin
from tripleoclient.tests import fakes as ooofakes from tripleoclient.tests import fakes as ooofakes
from tripleoclient.tests.v1.overcloud_node import fakes from tripleoclient.tests.v1.overcloud_node import fakes
from tripleoclient.v1 import overcloud_node from tripleoclient.v1 import overcloud_node
from tripleoclient.v2 import overcloud_node as overcloud_node_v2
class TestDeleteNode(fakes.TestDeleteNode): class TestDeleteNode(fakes.TestDeleteNode):
@ -45,31 +47,7 @@ class TestDeleteNode(fakes.TestDeleteNode):
self.websocket = mock.Mock() self.websocket = mock.Mock()
self.websocket.__enter__ = lambda s: self.websocket self.websocket.__enter__ = lambda s: self.websocket
self.websocket.__exit__ = lambda s, *exc: None self.websocket.__exit__ = lambda s, *exc: None
self.tripleoclient = mock.Mock()
self.tripleoclient.messaging_websocket.return_value = self.websocket self.tripleoclient.messaging_websocket.return_value = self.websocket
tc = self.app.client_manager.tripleoclient = self.tripleoclient
tc.create_mistral_context = plugin.ClientWrapper(
instance=ooofakes.FakeInstanceData
).create_mistral_context
self.gcn = mock.patch(
'tripleo_common.actions.config.DownloadConfigAction',
autospec=True
)
self.gcn.start()
self.addCleanup(self.gcn.stop)
self.ansible = mock.patch(
'tripleo_common.actions.ansible.AnsibleGenerateInventoryAction',
autospec=True
)
self.ansible.start()
self.addCleanup(self.ansible.stop)
config_mock = mock.patch(
'tripleo_common.actions.config.GetOvercloudConfig',
autospec=True
)
config_mock.start()
self.addCleanup(config_mock.stop)
self.workflow = self.app.client_manager.workflow_engine self.workflow = self.app.client_manager.workflow_engine
self.stack_name = self.app.client_manager.orchestration.stacks.get self.stack_name = self.app.client_manager.orchestration.stacks.get
stack = self.stack_name.return_value = mock.Mock( stack = self.stack_name.return_value = mock.Mock(
@ -95,6 +73,7 @@ class TestDeleteNode(fakes.TestDeleteNode):
wait_stack.start() wait_stack.start()
wait_stack.return_value = None wait_stack.return_value = None
self.addCleanup(wait_stack.stop) self.addCleanup(wait_stack.stop)
self.app.client_manager.compute.servers.get.return_value = None
# TODO(someone): This test does not pass with autospec=True, it should # TODO(someone): This test does not pass with autospec=True, it should
# probably be fixed so that it can pass with that. # probably be fixed so that it can pass with that.
@ -125,24 +104,24 @@ class TestDeleteNode(fakes.TestDeleteNode):
self.cmd.take_action, self.cmd.take_action,
parsed_args) parsed_args)
def test_node_wrong_stack(self): @mock.patch('tripleoclient.utils.run_ansible_playbook',
autospec=True,
side_effect=exceptions.InvalidConfiguration)
def test_node_wrong_stack(self, mock_playbook):
argslist = ['instance1', '--templates', argslist = ['instance1', '--templates',
'--stack', 'overcast', '--yes'] '--stack', 'overcast', '--yes']
verifylist = [ verifylist = [
('stack', 'overcast'), ('stack', 'overcast'),
('nodes', ['instance1', ]) ('nodes', ['instance1', ])
] ]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.stack_name.return_value = None self.stack_name.return_value = None
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.assertRaises(exceptions.InvalidConfiguration, self.assertRaises(exceptions.InvalidConfiguration,
self.cmd.take_action, self.cmd.take_action,
parsed_args) parsed_args)
# Verify
self.workflow.executions.create.assert_not_called()
@mock.patch('tripleoclient.utils.run_ansible_playbook', @mock.patch('tripleoclient.utils.run_ansible_playbook',
autospec=True) autospec=True)
def test_node_delete_without_stack(self, mock_playbook): def test_node_delete_without_stack(self, mock_playbook):
@ -461,116 +440,6 @@ class TestProvideNode(fakes.TestOvercloudNode):
self.cmd, argslist, verifylist) self.cmd, argslist, verifylist)
class TestIntrospectNode(fakes.TestOvercloudNode):
def setUp(self):
super(TestIntrospectNode, self).setUp()
self.workflow = self.app.client_manager.workflow_engine
execution = mock.Mock()
execution.id = "IDID"
self.workflow.executions.create.return_value = execution
client = self.app.client_manager.tripleoclient
self.websocket = client.messaging_websocket()
# Get the command object to test
self.cmd = overcloud_node.IntrospectNode(self.app, None)
def _check_introspect_all_manageable(self, parsed_args, provide=False):
self.websocket.wait_for_messages.return_value = iter([{
"status": "SUCCESS",
"message": "Success",
"introspected_nodes": {},
"execution_id": "IDID"
}] * 2)
self.cmd.take_action(parsed_args)
call_list = [mock.call(
'tripleo.baremetal.v1.introspect_manageable_nodes',
workflow_input={'run_validations': False, 'concurrency': 20}
)]
if provide:
call_list.append(mock.call(
'tripleo.baremetal.v1.provide_manageable_nodes',
workflow_input={}
))
self.workflow.executions.create.assert_has_calls(call_list)
self.assertEqual(self.workflow.executions.create.call_count,
2 if provide else 1)
def _check_introspect_nodes(self, parsed_args, nodes, provide=False):
self.websocket.wait_for_messages.return_value = [{
"status": "SUCCESS",
"message": "Success",
"execution_id": "IDID",
}]
self.cmd.take_action(parsed_args)
call_list = [mock.call(
'tripleo.baremetal.v1.introspect', workflow_input={
'node_uuids': nodes,
'run_validations': False,
'concurrency': 20}
)]
if provide:
call_list.append(mock.call(
'tripleo.baremetal.v1.provide', workflow_input={
'node_uuids': nodes}
))
self.workflow.executions.create.assert_has_calls(call_list)
self.assertEqual(self.workflow.executions.create.call_count,
2 if provide else 1)
def test_introspect_all_manageable_nodes_without_provide(self):
parsed_args = self.check_parser(self.cmd,
['--all-manageable'],
[('all_manageable', True)])
self._check_introspect_all_manageable(parsed_args, provide=False)
def test_introspect_all_manageable_nodes_with_provide(self):
parsed_args = self.check_parser(self.cmd,
['--all-manageable', '--provide'],
[('all_manageable', True),
('provide', True)])
self._check_introspect_all_manageable(parsed_args, provide=True)
def test_introspect_nodes_without_provide(self):
nodes = ['node_uuid1', 'node_uuid2']
parsed_args = self.check_parser(self.cmd,
nodes,
[('node_uuids', nodes)])
self._check_introspect_nodes(parsed_args, nodes, provide=False)
def test_introspect_nodes_with_provide(self):
nodes = ['node_uuid1', 'node_uuid2']
argslist = nodes + ['--provide']
parsed_args = self.check_parser(self.cmd,
argslist,
[('node_uuids', nodes),
('provide', True)])
self._check_introspect_nodes(parsed_args, nodes, provide=True)
def test_introspect_no_node_or_flag_specified(self):
self.assertRaises(test_utils.ParserException,
self.check_parser,
self.cmd, [], [])
def test_introspect_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)
class TestCleanNode(fakes.TestOvercloudNode): class TestCleanNode(fakes.TestOvercloudNode):
def setUp(self): def setUp(self):
@ -666,156 +535,6 @@ class TestCleanNode(fakes.TestOvercloudNode):
self._check_clean_nodes(parsed_args, nodes, provide=True) self._check_clean_nodes(parsed_args, nodes, provide=True)
class TestImportNode(fakes.TestOvercloudNode):
def setUp(self):
super(TestImportNode, self).setUp()
self.nodes_list = [{
"pm_user": "stack",
"pm_addr": "192.168.122.1",
"pm_password": "KEY1",
"pm_type": "pxe_ssh",
"mac": [
"00:0b:d0:69:7e:59"
],
}, {
"pm_user": "stack",
"pm_addr": "192.168.122.2",
"pm_password": "KEY2",
"pm_type": "pxe_ssh",
"mac": [
"00:0b:d0:69:7e:58"
]
}]
self.json_file = tempfile.NamedTemporaryFile(
mode='w', delete=False, suffix='.json')
json.dump(self.nodes_list, self.json_file)
self.json_file.close()
self.addCleanup(os.unlink, self.json_file.name)
self.workflow = self.app.client_manager.workflow_engine
execution = mock.Mock()
execution.id = "IDID"
self.workflow.executions.create.return_value = execution
client = self.app.client_manager.tripleoclient
self.websocket = client.messaging_websocket()
# Get the command object to test
self.cmd = overcloud_node.ImportNode(self.app, None)
image = collections.namedtuple('image', ['id', 'name'])
self.app.client_manager.image = mock.Mock()
self.app.client_manager.image.images.list.return_value = [
image(id=3, name='overcloud-full'),
]
self.http_boot = '/var/lib/ironic/httpboot'
self.useFixture(fixtures.MockPatch(
'os.path.exists', autospec=True,
side_effect=lambda path: path in [os.path.join(self.http_boot, i)
for i in ('agent.kernel',
'agent.ramdisk')]))
def _check_workflow_call(self, parsed_args, introspect=False,
provide=False, local=None, no_deploy_image=False):
self.websocket.wait_for_messages.return_value = [{
"status": "SUCCESS",
"message": "Success",
"registered_nodes": [{
"uuid": "MOCK_NODE_UUID"
}],
"execution_id": "IDID"
}]
self.cmd.take_action(parsed_args)
nodes_list = copy.deepcopy(self.nodes_list)
if not no_deploy_image:
for node in nodes_list:
node.update({
'kernel_id': 'file://%s/agent.kernel' % self.http_boot,
'ramdisk_id': 'file://%s/agent.ramdisk' % self.http_boot,
})
call_count = 1
call_list = [mock.call(
'tripleo.baremetal.v1.register_or_update', workflow_input={
'nodes_json': nodes_list,
'instance_boot_option': ('local' if local is True else
'netboot' if local is False else None)
}
)]
if introspect:
call_count += 1
call_list.append(mock.call(
'tripleo.baremetal.v1.introspect', workflow_input={
'node_uuids': ['MOCK_NODE_UUID'],
'run_validations': False,
'concurrency': 20}
))
if provide:
call_count += 1
call_list.append(mock.call(
'tripleo.baremetal.v1.provide', workflow_input={
'node_uuids': ['MOCK_NODE_UUID']
}
))
self.workflow.executions.create.assert_has_calls(call_list)
self.assertEqual(self.workflow.executions.create.call_count,
call_count)
def test_import_only(self):
argslist = [self.json_file.name]
verifylist = [('introspect', False),
('provide', False)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args)
def test_import_and_introspect(self):
argslist = [self.json_file.name, '--introspect']
verifylist = [('introspect', True),
('provide', False)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args, introspect=True)
def test_import_and_provide(self):
argslist = [self.json_file.name, '--provide']
verifylist = [('introspect', False),
('provide', True)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args, provide=True)
def test_import_and_introspect_and_provide(self):
argslist = [self.json_file.name, '--introspect', '--provide']
verifylist = [('introspect', True),
('provide', True)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args, introspect=True, provide=True)
def test_import_with_netboot(self):
arglist = [self.json_file.name, '--instance-boot-option', 'netboot']
verifylist = [('instance_boot_option', 'netboot')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self._check_workflow_call(parsed_args, local=False)
def test_import_with_no_deployed_image(self):
arglist = [self.json_file.name, '--no-deploy-image']
verifylist = [('no_deploy_image', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self._check_workflow_call(parsed_args, no_deploy_image=True)
class TestImportNodeMultiArch(fakes.TestOvercloudNode): class TestImportNodeMultiArch(fakes.TestOvercloudNode):
def setUp(self): def setUp(self):
@ -863,7 +582,7 @@ class TestImportNodeMultiArch(fakes.TestOvercloudNode):
self.websocket = client.messaging_websocket() self.websocket = client.messaging_websocket()
# Get the command object to test # Get the command object to test
self.cmd = overcloud_node.ImportNode(self.app, None) self.cmd = overcloud_node_v2.ImportNode(self.app, None)
image = collections.namedtuple('image', ['id', 'name']) image = collections.namedtuple('image', ['id', 'name'])
self.app.client_manager.image = mock.Mock() self.app.client_manager.image = mock.Mock()
@ -894,6 +613,8 @@ class TestImportNodeMultiArch(fakes.TestOvercloudNode):
"execution_id": "IDID" "execution_id": "IDID"
}] }]
with mock.patch('tripleoclient.utils.run_ansible_playbook',
autospec=True):
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
nodes_list = copy.deepcopy(self.nodes_list) nodes_list = copy.deepcopy(self.nodes_list)
@ -911,14 +632,8 @@ class TestImportNodeMultiArch(fakes.TestOvercloudNode):
nodes_list[2]['ramdisk_id'] = ( nodes_list[2]['ramdisk_id'] = (
'file://%s/SNB-x86_64/agent.ramdisk' % self.http_boot) 'file://%s/SNB-x86_64/agent.ramdisk' % self.http_boot)
call_count = 1 call_count = 0
call_list = [mock.call( call_list = []
'tripleo.baremetal.v1.register_or_update', workflow_input={
'nodes_json': nodes_list,
'instance_boot_option': ('local' if local is True else
'netboot' if local is False else None)
}
)]
if introspect: if introspect:
call_count += 1 call_count += 1
@ -949,13 +664,26 @@ class TestImportNodeMultiArch(fakes.TestOvercloudNode):
parsed_args = self.check_parser(self.cmd, argslist, verifylist) parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args) self._check_workflow_call(parsed_args)
def test_import_and_introspect(self): @mock.patch('tripleoclient.utils.run_ansible_playbook',
argslist = [self.json_file.name, '--introspect'] autospec=True)
verifylist = [('introspect', True), def test_import_and_introspect(self, mock_playbook):
('provide', False)] parsed_args = self.check_parser(self.cmd,
[self.json_file.name,
parsed_args = self.check_parser(self.cmd, argslist, verifylist) '--introspect'],
self._check_workflow_call(parsed_args, introspect=True) [('introspect', True),
('provide', False)])
self.cmd.take_action(parsed_args)
mock_playbook.assert_called_once_with(
workdir=mock.ANY,
playbook=mock.ANY,
inventory=mock.ANY,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
'node_uuids': ['MOCK_NODE_UUID'],
'run_validations': False,
'concurrency': 20
}
)
def test_import_and_provide(self): def test_import_and_provide(self):
argslist = [self.json_file.name, '--provide'] argslist = [self.json_file.name, '--provide']
@ -965,13 +693,27 @@ class TestImportNodeMultiArch(fakes.TestOvercloudNode):
parsed_args = self.check_parser(self.cmd, argslist, verifylist) parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args, provide=True) self._check_workflow_call(parsed_args, provide=True)
def test_import_and_introspect_and_provide(self): @mock.patch('tripleoclient.utils.run_ansible_playbook',
argslist = [self.json_file.name, '--introspect', '--provide'] autospec=True)
verifylist = [('introspect', True), def test_import_and_introspect_and_provide(self, mock_playbook):
('provide', True)] parsed_args = self.check_parser(self.cmd,
[self.json_file.name,
parsed_args = self.check_parser(self.cmd, argslist, verifylist) '--introspect',
self._check_workflow_call(parsed_args, introspect=True, provide=True) '--provide'],
[('introspect', True),
('provide', True)])
self.cmd.take_action(parsed_args)
mock_playbook.assert_called_once_with(
workdir=mock.ANY,
playbook=mock.ANY,
inventory=mock.ANY,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
'node_uuids': ['MOCK_NODE_UUID'],
'run_validations': False,
'concurrency': 20
}
)
def test_import_with_netboot(self): def test_import_with_netboot(self):
arglist = [self.json_file.name, '--instance-boot-option', 'netboot'] arglist = [self.json_file.name, '--instance-boot-option', 'netboot']
@ -1193,12 +935,6 @@ class TestDiscoverNode(fakes.TestOvercloudNode):
) )
self.gcn.start() self.gcn.start()
self.addCleanup(self.gcn.stop) self.addCleanup(self.gcn.stop)
self.roun = mock.patch(
'tripleo_common.actions.baremetal.RegisterOrUpdateNodes',
autospec=True
)
self.roun.start()
self.addCleanup(self.roun.stop)
self.websocket.wait_for_messages.return_value = [{ self.websocket.wait_for_messages.return_value = [{
"status": "SUCCESS", "status": "SUCCESS",
@ -1252,11 +988,11 @@ class TestDiscoverNode(fakes.TestOvercloudNode):
workflows_calls = [ workflows_calls = [
mock.call('tripleo.baremetal.v1.introspect', mock.call('tripleo.baremetal.v1.introspect',
workflow_input={'node_uuids': [], workflow_input={'node_uuids': ['MOCK_NODE_UUID'],
'run_validations': True, 'run_validations': True,
'concurrency': 10}), 'concurrency': 10}),
mock.call('tripleo.baremetal.v1.provide', mock.call('tripleo.baremetal.v1.provide',
workflow_input={'node_uuids': []} workflow_input={'node_uuids': ['MOCK_NODE_UUID']}
) )
] ]
self.workflow.executions.create.assert_has_calls(workflows_calls) self.workflow.executions.create.assert_has_calls(workflows_calls)

View File

@ -13,39 +13,16 @@
# under the License. # under the License.
# #
import mock from tripleoclient.tests import fakes
from osc_lib.tests import utils
class FakeClientWrapper(object): class TestDeleteNode(fakes.FakePlaybookExecution):
def __init__(self):
self._instance = mock.Mock()
self._mock_websocket = mock.Mock()
self._mock_websocket.__enter__ = mock.Mock(
return_value=self._mock_websocket)
# Return False to avoid silencing exceptions
self._mock_websocket.__exit__ = mock.Mock(return_value=False)
def messaging_websocket(self):
return self._mock_websocket
class TestDeleteNode(utils.TestCommand):
def setUp(self): def setUp(self):
super(TestDeleteNode, self).setUp() super(TestDeleteNode, self).setUp()
self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN")
self.app.client_manager.orchestration = mock.Mock()
self.app.client_manager.tripleoclient = FakeClientWrapper()
class TestOvercloudNode(fakes.FakePlaybookExecution):
class TestOvercloudNode(utils.TestCommand):
def setUp(self): def setUp(self):
super(TestOvercloudNode, self).setUp() super(TestOvercloudNode, self).setUp()
self.app.client_manager.baremetal = mock.Mock()
self.app.client_manager.workflow_engine = mock.Mock()
self.app.client_manager.tripleoclient = FakeClientWrapper()

View File

@ -297,3 +297,54 @@ class TestIntrospectNode(fakes.TestOvercloudNode):
self.assertRaises(test_utils.ParserException, self.assertRaises(test_utils.ParserException,
self.check_parser, self.check_parser,
self.cmd, argslist, verifylist) self.cmd, argslist, verifylist)
def _check_introspect_all_manageable(self, parsed_args, provide=False):
self.websocket.wait_for_messages.return_value = iter([{
"status": "SUCCESS",
"message": "Success",
"introspected_nodes": {},
"execution_id": "IDID"
}] * 2)
self.cmd.take_action(parsed_args)
call_list = [mock.call(
'tripleo.baremetal.v1.introspect_manageable_nodes',
workflow_input={'run_validations': False, 'concurrency': 20}
)]
if provide:
call_list.append(mock.call(
'tripleo.baremetal.v1.provide_manageable_nodes',
workflow_input={}
))
self.workflow.executions.create.assert_has_calls(call_list)
self.assertEqual(self.workflow.executions.create.call_count,
2 if provide else 1)
def _check_introspect_nodes(self, parsed_args, nodes, provide=False):
self.websocket.wait_for_messages.return_value = [{
"status": "SUCCESS",
"message": "Success",
"execution_id": "IDID",
}]
self.cmd.take_action(parsed_args)
call_list = [mock.call(
'tripleo.baremetal.v1.introspect', workflow_input={
'node_uuids': nodes,
'run_validations': False,
'concurrency': 20}
)]
if provide:
call_list.append(mock.call(
'tripleo.baremetal.v1.provide', workflow_input={
'node_uuids': nodes}
))
self.workflow.executions.create.assert_has_calls(call_list)
self.assertEqual(self.workflow.executions.create.call_count,
2 if provide else 1)

View File

@ -14,13 +14,12 @@
import mock import mock
from osc_lib.tests import utils
from tripleoclient import exceptions from tripleoclient import exceptions
from tripleoclient.tests import fakes
from tripleoclient.workflows import baremetal from tripleoclient.workflows import baremetal
class TestBaremetalWorkflows(utils.TestCommand): class TestBaremetalWorkflows(fakes.FakePlaybookExecution):
def setUp(self): def setUp(self):
super(TestBaremetalWorkflows, self).setUp() super(TestBaremetalWorkflows, self).setUp()
@ -49,44 +48,11 @@ class TestBaremetalWorkflows(utils.TestCommand):
}]) }])
def test_register_or_update_success(self): def test_register_or_update_success(self):
self.websocket.wait_for_messages.return_value = self.message_success
self.assertEqual(baremetal.register_or_update( self.assertEqual(baremetal.register_or_update(
self.app.client_manager, self.app.client_manager,
nodes_json=[], nodes_json=[],
kernel_name="kernel", instance_boot_option='local'
ramdisk_name="ramdisk" ), [mock.ANY])
), [])
self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal.v1.register_or_update',
workflow_input={
'kernel_name': 'kernel',
'nodes_json': [],
'ramdisk_name': 'ramdisk'
})
def test_register_or_update_error(self):
self.websocket.wait_for_messages.return_value = self.message_failed
self.assertRaises(
exceptions.RegisterOrUpdateError,
baremetal.register_or_update,
self.app.client_manager,
nodes_json=[],
kernel_name="kernel",
ramdisk_name="ramdisk"
)
self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal.v1.register_or_update',
workflow_input={
'kernel_name': 'kernel',
'nodes_json': [],
'ramdisk_name': 'ramdisk'
})
def test_provide_success(self): def test_provide_success(self):

View File

@ -13,10 +13,8 @@
# under the License. # under the License.
# #
import argparse
import collections import collections
import logging import logging
import os
import sys import sys
from cliff.formatters import table from cliff.formatters import table
@ -286,148 +284,6 @@ class CleanNode(command.Command):
baremetal.provide_manageable_nodes(self.app.client_manager) baremetal.provide_manageable_nodes(self.app.client_manager)
class IntrospectNode(command.Command):
"""Introspect specified nodes or all nodes in 'manageable' state."""
log = logging.getLogger(__name__ + ".IntrospectNode")
def get_parser(self, prog_name):
parser = super(IntrospectNode, 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 '
'introspected'))
group.add_argument("--all-manageable",
action='store_true',
help=_("Introspect all nodes currently in "
"'manageable' state"))
parser.add_argument('--provide',
action='store_true',
help=_('Provide (make available) the nodes once '
'introspected'))
parser.add_argument('--run-validations', action='store_true',
default=False,
help=_('Run the pre-deployment validations. These '
'external validations are from the TripleO '
'Validations project.'))
parser.add_argument('--concurrency', type=int,
default=20,
help=_('Maximum number of nodes to introspect at '
'once.'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
nodes = parsed_args.node_uuids
if nodes:
baremetal.introspect(self.app.client_manager,
node_uuids=nodes,
run_validations=parsed_args.run_validations,
concurrency=parsed_args.concurrency
)
else:
baremetal.introspect_manageable_nodes(
self.app.client_manager,
run_validations=parsed_args.run_validations,
concurrency=parsed_args.concurrency
)
if parsed_args.provide:
if nodes:
baremetal.provide(self.app.client_manager,
node_uuids=nodes,
)
else:
baremetal.provide_manageable_nodes(self.app.client_manager)
class ImportNode(command.Command):
"""Import baremetal nodes from a JSON, YAML or CSV file.
The node status will be set to 'manageable' by default.
"""
log = logging.getLogger(__name__ + ".ImportNode")
def get_parser(self, prog_name):
parser = super(ImportNode, self).get_parser(prog_name)
parser.add_argument('--introspect',
action='store_true',
help=_('Introspect the imported nodes'))
parser.add_argument('--run-validations', action='store_true',
default=False,
help=_('Run the pre-deployment validations. These '
'external validations are from the TripleO '
'Validations project.'))
parser.add_argument('--validate-only', action='store_true',
default=False,
help=_('Validate the env_file and then exit '
'without actually importing the nodes.'))
parser.add_argument('--provide',
action='store_true',
help=_('Provide (make available) the nodes'))
parser.add_argument('--no-deploy-image', action='store_true',
help=_('Skip setting the deploy kernel and '
'ramdisk.'))
parser.add_argument('--instance-boot-option',
choices=['local', 'netboot'], default=None,
help=_('Whether to set instances for booting from '
'local hard drive (local) or network '
'(netboot).'))
parser.add_argument("--http-boot",
default=os.environ.get(
'HTTP_BOOT',
constants.IRONIC_HTTP_BOOT_BIND_MOUNT),
help=_("Root directory for the ironic-python-agent"
" image"))
parser.add_argument('--concurrency', type=int,
default=20,
help=_('Maximum number of nodes to introspect at '
'once.'))
parser.add_argument('env_file', type=argparse.FileType('r'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
nodes_config = oooutils.parse_env_file(parsed_args.env_file)
parsed_args.env_file.close()
if parsed_args.validate_only:
return baremetal.validate_nodes(self.app.client_manager,
nodes_json=nodes_config)
# Look for *specific* deploy images and update the node data if
# one is found.
if not parsed_args.no_deploy_image:
oooutils.update_nodes_deploy_data(nodes_config,
http_boot=parsed_args.http_boot)
nodes = baremetal.register_or_update(
self.app.client_manager,
nodes_json=nodes_config,
instance_boot_option=parsed_args.instance_boot_option
)
nodes_uuids = [node['uuid'] for node in nodes]
if parsed_args.introspect:
baremetal.introspect(self.app.client_manager,
node_uuids=nodes_uuids,
run_validations=parsed_args.run_validations,
concurrency=parsed_args.concurrency
)
if parsed_args.provide:
baremetal.provide(self.app.client_manager,
node_uuids=nodes_uuids,
)
class ConfigureNode(command.Command): class ConfigureNode(command.Command):
"""Configure Node boot options.""" """Configure Node boot options."""
@ -578,7 +434,7 @@ class DiscoverNode(command.Command):
**kwargs **kwargs
) )
nodes_uuids = [node['uuid'] for node in nodes] nodes_uuids = [node.uuid for node in nodes]
if parsed_args.introspect: if parsed_args.introspect:
baremetal.introspect(self.app.client_manager, baremetal.introspect(self.app.client_manager,

View File

@ -102,7 +102,7 @@ class ImportNode(command.Command):
instance_boot_option=parsed_args.instance_boot_option instance_boot_option=parsed_args.instance_boot_option
) )
nodes_uuids = [node['uuid'] for node in nodes] nodes_uuids = [node.uuid for node in nodes]
if parsed_args.introspect: if parsed_args.introspect:
extra_vars = { extra_vars = {

View File

@ -44,34 +44,53 @@ def validate_nodes(clients, nodes_json):
raise exceptions.RegisterOrUpdateError(validated_nodes) raise exceptions.RegisterOrUpdateError(validated_nodes)
def register_or_update(clients, **workflow_input): def register_or_update(clients, nodes_json, kernel_name=None,
ramdisk_name=None, instance_boot_option=None):
"""Node Registration or Update """Node Registration or Update
Run the tripleo.baremetal.v1.register_or_update Mistral workflow. :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
""" """
workflow_client = clients.workflow_engine context = clients.tripleoclient.create_mistral_context()
tripleoclients = clients.tripleoclient nodes = baremetal.RegisterOrUpdateNodes(
nodes_json=nodes_json,
with tripleoclients.messaging_websocket() as ws: ramdisk_name=ramdisk_name,
execution = base.start_workflow( kernel_name=kernel_name,
workflow_client, instance_boot_option=instance_boot_option
'tripleo.baremetal.v1.register_or_update',
workflow_input=workflow_input
) )
for payload in base.wait_for_messages(workflow_client, ws, execution): registered_nodes = nodes.run(context=context)
if 'message' in payload: if not isinstance(registered_nodes, list):
print(payload['message']) raise exceptions.RegisterOrUpdateError(registered_nodes)
if payload['status'] == 'SUCCESS':
registered_nodes = payload['registered_nodes']
for nd in registered_nodes:
print('Successfully registered node UUID %s' % nd['uuid'])
return registered_nodes
else: else:
raise exceptions.RegisterOrUpdateError( for node in registered_nodes:
'Exception registering nodes: {}'.format(payload['message'])) 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): def _format_errors(payload):
@ -363,18 +382,13 @@ def discover_and_enroll(clients, ip_addresses, credentials, kernel_name,
) )
print('Successfully probed node IP {}'.format(node['ip'])) print('Successfully probed node IP {}'.format(node['ip']))
register_or_update = baremetal.RegisterOrUpdateNodes( return register_or_update(
clients=clients,
nodes_json=probed_nodes, nodes_json=probed_nodes,
instance_boot_option=instance_boot_option, instance_boot_option=instance_boot_option,
kernel_name=kernel_name, kernel_name=kernel_name,
ramdisk_name=ramdisk_name ramdisk_name=ramdisk_name
) )
registered_nodes = list()
for node in register_or_update.run(context=context):
print('Successfully registered node UUID {}'.format(node['uuid']))
registered_nodes.append(node)
else:
return registered_nodes
def clean_nodes(clients, **workflow_input): def clean_nodes(clients, **workflow_input):

View File

@ -59,8 +59,7 @@ def deploy(log, clients, **workflow_input):
message = payload.get('message') message = payload.get('message')
if message and status == "RUNNING": if message and status == "RUNNING":
print(message) print(message)
elif payload['status'] != "SUCCESS":
if payload['status'] != "SUCCESS":
log.info(pprint.pformat(payload)) log.info(pprint.pformat(payload))
print(payload['message']) print(payload['message'])
raise ValueError("Unexpected status %s for %s" raise ValueError("Unexpected status %s for %s"