python utility to manage a tripleo based cloud
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1437 lines
53 KiB

  1. # Copyright 2015 Red Hat, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. #
  15. import collections
  16. import copy
  17. import json
  18. import mock
  19. import os
  20. import tempfile
  21. import fixtures
  22. from osc_lib import exceptions as oscexc
  23. from osc_lib.tests import utils as test_utils
  24. import yaml
  25. from tripleoclient import exceptions
  26. from tripleoclient import plugin
  27. from tripleoclient.tests import fakes as ooofakes
  28. from tripleoclient.tests.v1.overcloud_node import fakes
  29. from tripleoclient.v1 import overcloud_node
  30. class TestDeleteNode(fakes.TestDeleteNode):
  31. def setUp(self):
  32. super(TestDeleteNode, self).setUp()
  33. # Get the command object to test
  34. self.cmd = overcloud_node.DeleteNode(self.app, None)
  35. self.app.client_manager.workflow_engine = mock.Mock()
  36. self.tripleoclient = mock.Mock()
  37. self.websocket = mock.Mock()
  38. self.websocket.__enter__ = lambda s: self.websocket
  39. self.websocket.__exit__ = lambda s, *exc: None
  40. self.tripleoclient = mock.Mock()
  41. self.tripleoclient.messaging_websocket.return_value = self.websocket
  42. tc = self.app.client_manager.tripleoclient = self.tripleoclient
  43. tc.create_mistral_context = plugin.ClientWrapper(
  44. instance=ooofakes.FakeInstanceData
  45. ).create_mistral_context
  46. self.workflow = self.app.client_manager.workflow_engine
  47. self.stack_name = self.app.client_manager.orchestration.stacks.get
  48. self.stack_name.return_value = mock.Mock(stack_name="overcloud")
  49. execution = mock.Mock()
  50. execution.id = "IDID"
  51. self.workflow.executions.create.return_value = execution
  52. delete_node = mock.patch(
  53. 'tripleo_common.actions.scale.ScaleDownAction.run',
  54. autospec=True
  55. )
  56. delete_node.start()
  57. delete_node.return_value = None
  58. self.addCleanup(delete_node.stop)
  59. # TODO(someone): This test does not pass with autospec=True, it should
  60. # probably be fixed so that it can pass with that.
  61. @mock.patch("heatclient.common.event_utils.poll_for_events")
  62. def test_node_delete(self, mock_poll):
  63. mock_poll.return_value = ("CREATE_IN_PROGRESS", "MESSAGE")
  64. argslist = ['instance1', 'instance2', '--templates',
  65. '--stack', 'overcast', '--timeout', '90', '--yes']
  66. verifylist = [
  67. ('stack', 'overcast'),
  68. ('nodes', ['instance1', 'instance2'])
  69. ]
  70. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  71. self.websocket.wait_for_messages.return_value = iter([{
  72. "execution_id": "IDID",
  73. "status": "SUCCESS",
  74. "message": "Success.",
  75. }])
  76. self.stack_name.return_value = mock.Mock(stack_name="overcast")
  77. self.cmd.take_action(parsed_args)
  78. @mock.patch('tripleoclient.utils.prompt_user_for_confirmation',
  79. return_value=False)
  80. def test_node_delete_no_confirm(self, confirm_mock):
  81. argslist = ['instance1', 'instance2', '--templates',
  82. '--stack', 'overcast', '--timeout', '90']
  83. verifylist = [
  84. ('stack', 'overcast'),
  85. ('nodes', ['instance1', 'instance2'])
  86. ]
  87. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  88. self.assertRaises(oscexc.CommandError,
  89. self.cmd.take_action,
  90. parsed_args)
  91. def test_node_wrong_stack(self):
  92. argslist = ['instance1', '--templates',
  93. '--stack', 'overcast', '--yes']
  94. verifylist = [
  95. ('stack', 'overcast'),
  96. ('nodes', ['instance1', ])
  97. ]
  98. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  99. self.stack_name.return_value = None
  100. self.assertRaises(exceptions.InvalidConfiguration,
  101. self.cmd.take_action,
  102. parsed_args)
  103. # Verify
  104. self.workflow.executions.create.assert_not_called()
  105. @mock.patch("heatclient.common.event_utils.poll_for_events")
  106. def test_node_delete_without_stack(self, mock_poll):
  107. mock_poll.return_value = ("CREATE_IN_PROGRESS", "MESSAGE")
  108. arglist = ['instance1', '--yes']
  109. verifylist = [
  110. ('stack', 'overcloud'),
  111. ('nodes', ['instance1']),
  112. ]
  113. parsed_args = self.check_parser(self.cmd, arglist, verifylist)
  114. self.websocket.wait_for_messages.return_value = iter([{
  115. "execution_id": "IDID",
  116. "status": "SUCCESS",
  117. "message": "Success.",
  118. }])
  119. self.cmd.take_action(parsed_args)
  120. def test_node_delete_wrong_instance(self):
  121. argslist = ['wrong_instance', '--templates',
  122. '--stack', 'overcloud', '--yes']
  123. verifylist = [
  124. ('stack', 'overcloud'),
  125. ('nodes', ['wrong_instance']),
  126. ]
  127. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  128. self.websocket.wait_for_messages.return_value = iter([{
  129. "status": "FAILED",
  130. "execution_id": "IDID",
  131. "message": """Failed to run action ERROR: Couldn't find \
  132. following instances in stack overcloud: wrong_instance"""
  133. }])
  134. # Verify
  135. self.assertRaises(exceptions.DeploymentError,
  136. self.cmd.take_action, parsed_args)
  137. @mock.patch("heatclient.common.event_utils.poll_for_events")
  138. @mock.patch('tripleoclient.workflows.baremetal.expand_roles',
  139. autospec=True)
  140. @mock.patch('tripleoclient.workflows.baremetal.undeploy_roles',
  141. autospec=True)
  142. def test_node_delete_baremetal_deployment(self, mock_undeploy_roles,
  143. mock_expand_roles, mock_poll):
  144. mock_poll.return_value = ("CREATE_IN_PROGRESS", "MESSAGE")
  145. self.websocket.wait_for_messages.return_value = iter([{
  146. "execution_id": "IDID",
  147. "status": "SUCCESS",
  148. "message": "Success.",
  149. }])
  150. bm_yaml = [{
  151. 'name': 'Compute',
  152. 'count': 5,
  153. 'instances': [{
  154. 'name': 'baremetal-2',
  155. 'hostname': 'overcast-compute-0',
  156. 'provisioned': False
  157. }],
  158. }, {
  159. 'name': 'Controller',
  160. 'count': 2,
  161. 'instances': [{
  162. 'name': 'baremetal-1',
  163. 'hostname': 'overcast-controller-1',
  164. 'provisioned': False
  165. }]
  166. }]
  167. expand_to_delete = {
  168. 'instances': [{
  169. 'name': 'baremetal-1',
  170. 'hostname': 'overcast-controller-1'
  171. }, {
  172. 'name': 'baremetal-2',
  173. 'hostname': 'overcast-compute-0'
  174. }]
  175. }
  176. expand_to_translate = {
  177. 'environment': {
  178. 'parameter_defaults': {
  179. 'ComputeRemovalPolicies': [{
  180. 'resource_list': [0]
  181. }],
  182. 'ControllerRemovalPolicies': [{
  183. 'resource_list': [1]
  184. }]
  185. }
  186. }
  187. }
  188. mock_expand_roles.side_effect = [
  189. expand_to_delete,
  190. expand_to_translate
  191. ]
  192. self.stack_name.return_value = mock.Mock(stack_name="overcast")
  193. res_list = self.app.client_manager.orchestration.resources.list
  194. res_list.return_value = [
  195. mock.Mock(
  196. resource_type='OS::TripleO::ComputeServer',
  197. parent_resource='0',
  198. physical_resource_id='aaaa'
  199. ),
  200. mock.Mock(
  201. resource_type='OS::TripleO::ComputeServer',
  202. parent_resource='1',
  203. physical_resource_id='bbbb'
  204. ),
  205. mock.Mock(
  206. resource_type='OS::TripleO::ControllerServer',
  207. parent_resource='0',
  208. physical_resource_id='cccc'
  209. ),
  210. mock.Mock(
  211. resource_type='OS::TripleO::ControllerServer',
  212. parent_resource='1',
  213. physical_resource_id='dddd'
  214. ),
  215. mock.Mock(
  216. resource_type='OS::TripleO::ControllerServer',
  217. parent_resource='2',
  218. physical_resource_id='eeee'
  219. )
  220. ]
  221. with tempfile.NamedTemporaryFile(mode='w') as inp:
  222. yaml.dump(bm_yaml, inp, encoding='utf-8')
  223. inp.flush()
  224. argslist = ['--baremetal-deployment', inp.name, '--templates',
  225. '--stack', 'overcast', '--timeout', '90', '--yes']
  226. verifylist = [
  227. ('stack', 'overcast'),
  228. ('baremetal_deployment', inp.name)
  229. ]
  230. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  231. self.cmd.take_action(parsed_args)
  232. # Verify
  233. res_list.assert_called_once_with('overcast', nested_depth=5)
  234. mock_expand_roles.assert_has_calls([
  235. mock.call(
  236. self.app.client_manager,
  237. provisioned=False,
  238. roles=bm_yaml,
  239. stackname='overcast'
  240. ),
  241. mock.call(
  242. self.app.client_manager,
  243. provisioned=True,
  244. roles=bm_yaml,
  245. stackname='overcast'
  246. )
  247. ])
  248. @mock.patch('tripleoclient.workflows.baremetal.expand_roles',
  249. autospec=True)
  250. def test_nodes_to_delete(self, mock_expand_roles):
  251. bm_yaml = [{
  252. 'name': 'Compute',
  253. 'count': 5,
  254. 'instances': [{
  255. 'name': 'baremetal-2',
  256. 'hostname': 'overcast-compute-0',
  257. 'provisioned': False
  258. }],
  259. }, {
  260. 'name': 'Controller',
  261. 'count': 2,
  262. 'instances': [{
  263. 'name': 'baremetal-1',
  264. 'hostname': 'overcast-controller-1',
  265. 'provisioned': False
  266. }]
  267. }]
  268. mock_expand_roles.return_value = {
  269. 'instances': [{
  270. 'name': 'baremetal-1',
  271. 'hostname': 'overcast-controller-1'
  272. }, {
  273. 'name': 'baremetal-2',
  274. 'hostname': 'overcast-compute-0'
  275. }]
  276. }
  277. argslist = ['--baremetal-deployment', '/foo/bm_deploy.yaml']
  278. verifylist = [
  279. ('baremetal_deployment', '/foo/bm_deploy.yaml')
  280. ]
  281. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  282. result = self.cmd._nodes_to_delete(parsed_args, bm_yaml)
  283. expected = '''+-----------------------+-------------+
  284. | hostname | name |
  285. +-----------------------+-------------+
  286. | overcast-controller-1 | baremetal-1 |
  287. | overcast-compute-0 | baremetal-2 |
  288. +-----------------------+-------------+
  289. '''
  290. self.assertEqual(expected, result)
  291. @mock.patch('tripleoclient.workflows.baremetal.expand_roles',
  292. autospec=True)
  293. def test_translate_nodes_to_resources(self, mock_expand_roles):
  294. bm_yaml = [{
  295. 'name': 'Compute',
  296. 'count': 5,
  297. 'instances': [{
  298. 'name': 'baremetal-2',
  299. 'hostname': 'overcast-compute-0',
  300. 'provisioned': False
  301. }],
  302. }, {
  303. 'name': 'Controller',
  304. 'count': 2,
  305. 'instances': [{
  306. 'name': 'baremetal-1',
  307. 'hostname': 'overcast-controller-1',
  308. 'provisioned': False
  309. }]
  310. }]
  311. res_list = self.app.client_manager.orchestration.resources.list
  312. res_list.return_value = [
  313. mock.Mock(
  314. resource_type='OS::TripleO::ComputeServer',
  315. parent_resource='0',
  316. physical_resource_id='aaaa'
  317. ),
  318. mock.Mock(
  319. resource_type='OS::TripleO::ComputeServer',
  320. parent_resource='1',
  321. physical_resource_id='bbbb'
  322. ),
  323. mock.Mock(
  324. resource_type='OS::TripleO::ControllerServer',
  325. parent_resource='0',
  326. physical_resource_id='cccc'
  327. ),
  328. mock.Mock(
  329. resource_type='OS::TripleO::ControllerServer',
  330. parent_resource='1',
  331. physical_resource_id='dddd'
  332. ),
  333. mock.Mock(
  334. resource_type='OS::TripleO::ControllerServer',
  335. parent_resource='2',
  336. physical_resource_id='eeee'
  337. )
  338. ]
  339. mock_expand_roles.return_value = {
  340. 'environment': {
  341. 'parameter_defaults': {
  342. 'ComputeRemovalPolicies': [{
  343. 'resource_list': [0]
  344. }],
  345. 'ControllerRemovalPolicies': [{
  346. 'resource_list': [1]
  347. }]
  348. }
  349. }
  350. }
  351. argslist = ['--baremetal-deployment', '/foo/bm_deploy.yaml']
  352. verifylist = [
  353. ('baremetal_deployment', '/foo/bm_deploy.yaml')
  354. ]
  355. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  356. result = self.cmd._translate_nodes_to_resources(
  357. parsed_args, bm_yaml)
  358. self.assertEqual(['aaaa', 'dddd'], result)
  359. class TestProvideNode(fakes.TestOvercloudNode):
  360. def setUp(self):
  361. super(TestProvideNode, self).setUp()
  362. self.workflow = self.app.client_manager.workflow_engine
  363. execution = mock.Mock()
  364. execution.id = "IDID"
  365. self.workflow.executions.create.return_value = execution
  366. client = self.app.client_manager.tripleoclient
  367. self.websocket = client.messaging_websocket()
  368. # Get the command object to test
  369. self.cmd = overcloud_node.ProvideNode(self.app, None)
  370. self.websocket.wait_for_messages.return_value = iter([{
  371. "status": "SUCCESS",
  372. "message": "Success",
  373. "execution_id": "IDID"
  374. }])
  375. def test_provide_all_manageable_nodes(self):
  376. parsed_args = self.check_parser(self.cmd,
  377. ['--all-manageable'],
  378. [('all_manageable', True)])
  379. self.cmd.take_action(parsed_args)
  380. self.workflow.executions.create.assert_called_once_with(
  381. 'tripleo.baremetal.v1.provide_manageable_nodes',
  382. workflow_input={}
  383. )
  384. def test_provide_one_node(self):
  385. node_id = 'node_uuid1'
  386. parsed_args = self.check_parser(self.cmd,
  387. [node_id],
  388. [('node_uuids', [node_id])])
  389. self.cmd.take_action(parsed_args)
  390. self.workflow.executions.create.assert_called_once_with(
  391. 'tripleo.baremetal.v1.provide',
  392. workflow_input={'node_uuids': [node_id]}
  393. )
  394. def test_provide_multiple_nodes(self):
  395. node_id1 = 'node_uuid1'
  396. node_id2 = 'node_uuid2'
  397. argslist = [node_id1, node_id2]
  398. verifylist = [('node_uuids', [node_id1, node_id2])]
  399. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  400. self.cmd.take_action(parsed_args)
  401. self.workflow.executions.create.assert_called_once_with(
  402. 'tripleo.baremetal.v1.provide', workflow_input={
  403. 'node_uuids': [node_id1, node_id2]
  404. }
  405. )
  406. def test_provide_no_node_or_flag_specified(self):
  407. self.assertRaises(test_utils.ParserException,
  408. self.check_parser,
  409. self.cmd, [], [])
  410. def test_provide_uuids_and_all_both_specified(self):
  411. argslist = ['node_id1', 'node_id2', '--all-manageable']
  412. verifylist = [('node_uuids', ['node_id1', 'node_id2']),
  413. ('all_manageable', True)]
  414. self.assertRaises(test_utils.ParserException,
  415. self.check_parser,
  416. self.cmd, argslist, verifylist)
  417. class TestIntrospectNode(fakes.TestOvercloudNode):
  418. def setUp(self):
  419. super(TestIntrospectNode, self).setUp()
  420. self.workflow = self.app.client_manager.workflow_engine
  421. execution = mock.Mock()
  422. execution.id = "IDID"
  423. self.workflow.executions.create.return_value = execution
  424. client = self.app.client_manager.tripleoclient
  425. self.websocket = client.messaging_websocket()
  426. # Get the command object to test
  427. self.cmd = overcloud_node.IntrospectNode(self.app, None)
  428. def _check_introspect_all_manageable(self, parsed_args, provide=False):
  429. self.websocket.wait_for_messages.return_value = iter([{
  430. "status": "SUCCESS",
  431. "message": "Success",
  432. "introspected_nodes": {},
  433. "execution_id": "IDID"
  434. }] * 2)
  435. self.cmd.take_action(parsed_args)
  436. call_list = [mock.call(
  437. 'tripleo.baremetal.v1.introspect_manageable_nodes',
  438. workflow_input={'run_validations': False, 'concurrency': 20}
  439. )]
  440. if provide:
  441. call_list.append(mock.call(
  442. 'tripleo.baremetal.v1.provide_manageable_nodes',
  443. workflow_input={}
  444. ))
  445. self.workflow.executions.create.assert_has_calls(call_list)
  446. self.assertEqual(self.workflow.executions.create.call_count,
  447. 2 if provide else 1)
  448. def _check_introspect_nodes(self, parsed_args, nodes, provide=False):
  449. self.websocket.wait_for_messages.return_value = [{
  450. "status": "SUCCESS",
  451. "message": "Success",
  452. "execution_id": "IDID",
  453. }]
  454. self.cmd.take_action(parsed_args)
  455. call_list = [mock.call(
  456. 'tripleo.baremetal.v1.introspect', workflow_input={
  457. 'node_uuids': nodes,
  458. 'run_validations': False,
  459. 'concurrency': 20}
  460. )]
  461. if provide:
  462. call_list.append(mock.call(
  463. 'tripleo.baremetal.v1.provide', workflow_input={
  464. 'node_uuids': nodes}
  465. ))
  466. self.workflow.executions.create.assert_has_calls(call_list)
  467. self.assertEqual(self.workflow.executions.create.call_count,
  468. 2 if provide else 1)
  469. def test_introspect_all_manageable_nodes_without_provide(self):
  470. parsed_args = self.check_parser(self.cmd,
  471. ['--all-manageable'],
  472. [('all_manageable', True)])
  473. self._check_introspect_all_manageable(parsed_args, provide=False)
  474. def test_introspect_all_manageable_nodes_with_provide(self):
  475. parsed_args = self.check_parser(self.cmd,
  476. ['--all-manageable', '--provide'],
  477. [('all_manageable', True),
  478. ('provide', True)])
  479. self._check_introspect_all_manageable(parsed_args, provide=True)
  480. def test_introspect_nodes_without_provide(self):
  481. nodes = ['node_uuid1', 'node_uuid2']
  482. parsed_args = self.check_parser(self.cmd,
  483. nodes,
  484. [('node_uuids', nodes)])
  485. self._check_introspect_nodes(parsed_args, nodes, provide=False)
  486. def test_introspect_nodes_with_provide(self):
  487. nodes = ['node_uuid1', 'node_uuid2']
  488. argslist = nodes + ['--provide']
  489. parsed_args = self.check_parser(self.cmd,
  490. argslist,
  491. [('node_uuids', nodes),
  492. ('provide', True)])
  493. self._check_introspect_nodes(parsed_args, nodes, provide=True)
  494. def test_introspect_no_node_or_flag_specified(self):
  495. self.assertRaises(test_utils.ParserException,
  496. self.check_parser,
  497. self.cmd, [], [])
  498. def test_introspect_uuids_and_all_both_specified(self):
  499. argslist = ['node_id1', 'node_id2', '--all-manageable']
  500. verifylist = [('node_uuids', ['node_id1', 'node_id2']),
  501. ('all_manageable', True)]
  502. self.assertRaises(test_utils.ParserException,
  503. self.check_parser,
  504. self.cmd, argslist, verifylist)
  505. class TestCleanNode(fakes.TestOvercloudNode):
  506. def setUp(self):
  507. super(TestCleanNode, self).setUp()
  508. self.workflow = self.app.client_manager.workflow_engine
  509. execution = mock.Mock()
  510. execution.id = "IDID"
  511. self.workflow.executions.create.return_value = execution
  512. client = self.app.client_manager.tripleoclient
  513. self.websocket = client.messaging_websocket()
  514. # Get the command object to test
  515. self.cmd = overcloud_node.CleanNode(self.app, None)
  516. def _check_clean_all_manageable(self, parsed_args, provide=False):
  517. self.websocket.wait_for_messages.return_value = iter([{
  518. "status": "SUCCESS",
  519. "message": "Success",
  520. "cleaned_nodes": {},
  521. "execution_id": "IDID"
  522. }] * 2)
  523. self.cmd.take_action(parsed_args)
  524. call_list = [mock.call(
  525. 'tripleo.baremetal.v1.clean_manageable_nodes',
  526. workflow_input={}
  527. )]
  528. if provide:
  529. call_list.append(mock.call(
  530. 'tripleo.baremetal.v1.provide_manageable_nodes',
  531. workflow_input={}
  532. ))
  533. self.workflow.executions.create.assert_has_calls(call_list)
  534. self.assertEqual(self.workflow.executions.create.call_count,
  535. 2 if provide else 1)
  536. def _check_clean_nodes(self, parsed_args, nodes, provide=False):
  537. self.websocket.wait_for_messages.return_value = [{
  538. "status": "SUCCESS",
  539. "message": "Success",
  540. "execution_id": "IDID"
  541. }]
  542. self.cmd.take_action(parsed_args)
  543. call_list = [mock.call(
  544. 'tripleo.baremetal.v1.clean_nodes', workflow_input={
  545. 'node_uuids': nodes}
  546. )]
  547. if provide:
  548. call_list.append(mock.call(
  549. 'tripleo.baremetal.v1.provide', workflow_input={
  550. 'node_uuids': nodes}
  551. ))
  552. self.workflow.executions.create.assert_has_calls(call_list)
  553. self.assertEqual(self.workflow.executions.create.call_count,
  554. 2 if provide else 1)
  555. def test_clean_all_manageable_nodes_without_provide(self):
  556. parsed_args = self.check_parser(self.cmd,
  557. ['--all-manageable'],
  558. [('all_manageable', True)])
  559. self._check_clean_all_manageable(parsed_args, provide=False)
  560. def test_clean_all_manageable_nodes_with_provide(self):
  561. parsed_args = self.check_parser(self.cmd,
  562. ['--all-manageable', '--provide'],
  563. [('all_manageable', True),
  564. ('provide', True)])
  565. self._check_clean_all_manageable(parsed_args, provide=True)
  566. def test_clean_nodes_without_provide(self):
  567. nodes = ['node_uuid1', 'node_uuid2']
  568. parsed_args = self.check_parser(self.cmd,
  569. nodes,
  570. [('node_uuids', nodes)])
  571. self._check_clean_nodes(parsed_args, nodes, provide=False)
  572. def test_clean_nodes_with_provide(self):
  573. nodes = ['node_uuid1', 'node_uuid2']
  574. argslist = nodes + ['--provide']
  575. parsed_args = self.check_parser(self.cmd,
  576. argslist,
  577. [('node_uuids', nodes),
  578. ('provide', True)])
  579. self._check_clean_nodes(parsed_args, nodes, provide=True)
  580. class TestImportNode(fakes.TestOvercloudNode):
  581. def setUp(self):
  582. super(TestImportNode, self).setUp()
  583. self.nodes_list = [{
  584. "pm_user": "stack",
  585. "pm_addr": "192.168.122.1",
  586. "pm_password": "KEY1",
  587. "pm_type": "pxe_ssh",
  588. "mac": [
  589. "00:0b:d0:69:7e:59"
  590. ],
  591. }, {
  592. "pm_user": "stack",
  593. "pm_addr": "192.168.122.2",
  594. "pm_password": "KEY2",
  595. "pm_type": "pxe_ssh",
  596. "mac": [
  597. "00:0b:d0:69:7e:58"
  598. ]
  599. }]
  600. self.json_file = tempfile.NamedTemporaryFile(
  601. mode='w', delete=False, suffix='.json')
  602. json.dump(self.nodes_list, self.json_file)
  603. self.json_file.close()
  604. self.addCleanup(os.unlink, self.json_file.name)
  605. self.workflow = self.app.client_manager.workflow_engine
  606. execution = mock.Mock()
  607. execution.id = "IDID"
  608. self.workflow.executions.create.return_value = execution
  609. client = self.app.client_manager.tripleoclient
  610. self.websocket = client.messaging_websocket()
  611. # Get the command object to test
  612. self.cmd = overcloud_node.ImportNode(self.app, None)
  613. image = collections.namedtuple('image', ['id', 'name'])
  614. self.app.client_manager.image = mock.Mock()
  615. self.app.client_manager.image.images.list.return_value = [
  616. image(id=3, name='overcloud-full'),
  617. ]
  618. self.http_boot = '/var/lib/ironic/httpboot'
  619. self.useFixture(fixtures.MockPatch(
  620. 'os.path.exists', autospec=True,
  621. side_effect=lambda path: path in [os.path.join(self.http_boot, i)
  622. for i in ('agent.kernel',
  623. 'agent.ramdisk')]))
  624. def _check_workflow_call(self, parsed_args, introspect=False,
  625. provide=False, local=None, no_deploy_image=False):
  626. self.websocket.wait_for_messages.return_value = [{
  627. "status": "SUCCESS",
  628. "message": "Success",
  629. "registered_nodes": [{
  630. "uuid": "MOCK_NODE_UUID"
  631. }],
  632. "execution_id": "IDID"
  633. }]
  634. self.cmd.take_action(parsed_args)
  635. nodes_list = copy.deepcopy(self.nodes_list)
  636. if not no_deploy_image:
  637. for node in nodes_list:
  638. node.update({
  639. 'kernel_id': 'file://%s/agent.kernel' % self.http_boot,
  640. 'ramdisk_id': 'file://%s/agent.ramdisk' % self.http_boot,
  641. })
  642. call_count = 1
  643. call_list = [mock.call(
  644. 'tripleo.baremetal.v1.register_or_update', workflow_input={
  645. 'nodes_json': nodes_list,
  646. 'instance_boot_option': ('local' if local is True else
  647. 'netboot' if local is False else None)
  648. }
  649. )]
  650. if introspect:
  651. call_count += 1
  652. call_list.append(mock.call(
  653. 'tripleo.baremetal.v1.introspect', workflow_input={
  654. 'node_uuids': ['MOCK_NODE_UUID'],
  655. 'run_validations': False,
  656. 'concurrency': 20}
  657. ))
  658. if provide:
  659. call_count += 1
  660. call_list.append(mock.call(
  661. 'tripleo.baremetal.v1.provide', workflow_input={
  662. 'node_uuids': ['MOCK_NODE_UUID']
  663. }
  664. ))
  665. self.workflow.executions.create.assert_has_calls(call_list)
  666. self.assertEqual(self.workflow.executions.create.call_count,
  667. call_count)
  668. def test_import_only(self):
  669. argslist = [self.json_file.name]
  670. verifylist = [('introspect', False),
  671. ('provide', False)]
  672. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  673. self._check_workflow_call(parsed_args)
  674. def test_import_and_introspect(self):
  675. argslist = [self.json_file.name, '--introspect']
  676. verifylist = [('introspect', True),
  677. ('provide', False)]
  678. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  679. self._check_workflow_call(parsed_args, introspect=True)
  680. def test_import_and_provide(self):
  681. argslist = [self.json_file.name, '--provide']
  682. verifylist = [('introspect', False),
  683. ('provide', True)]
  684. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  685. self._check_workflow_call(parsed_args, provide=True)
  686. def test_import_and_introspect_and_provide(self):
  687. argslist = [self.json_file.name, '--introspect', '--provide']
  688. verifylist = [('introspect', True),
  689. ('provide', True)]
  690. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  691. self._check_workflow_call(parsed_args, introspect=True, provide=True)
  692. def test_import_with_netboot(self):
  693. arglist = [self.json_file.name, '--instance-boot-option', 'netboot']
  694. verifylist = [('instance_boot_option', 'netboot')]
  695. parsed_args = self.check_parser(self.cmd, arglist, verifylist)
  696. self._check_workflow_call(parsed_args, local=False)
  697. def test_import_with_no_deployed_image(self):
  698. arglist = [self.json_file.name, '--no-deploy-image']
  699. verifylist = [('no_deploy_image', True)]
  700. parsed_args = self.check_parser(self.cmd, arglist, verifylist)
  701. self._check_workflow_call(parsed_args, no_deploy_image=True)
  702. class TestImportNodeMultiArch(fakes.TestOvercloudNode):
  703. def setUp(self):
  704. super(TestImportNodeMultiArch, self).setUp()
  705. self.nodes_list = [{
  706. "pm_user": "stack",
  707. "pm_addr": "192.168.122.1",
  708. "pm_password": "KEY1",
  709. "pm_type": "pxe_ssh",
  710. "mac": [
  711. "00:0b:d0:69:7e:59"
  712. ],
  713. }, {
  714. "pm_user": "stack",
  715. "pm_addr": "192.168.122.2",
  716. "pm_password": "KEY2",
  717. "pm_type": "pxe_ssh",
  718. "arch": "x86_64",
  719. "mac": [
  720. "00:0b:d0:69:7e:58"
  721. ]
  722. }, {
  723. "pm_user": "stack",
  724. "pm_addr": "192.168.122.3",
  725. "pm_password": "KEY3",
  726. "pm_type": "pxe_ssh",
  727. "arch": "x86_64",
  728. "platform": "SNB",
  729. "mac": [
  730. "00:0b:d0:69:7e:58"
  731. ]
  732. }]
  733. self.json_file = tempfile.NamedTemporaryFile(
  734. mode='w', delete=False, suffix='.json')
  735. json.dump(self.nodes_list, self.json_file)
  736. self.json_file.close()
  737. self.addCleanup(os.unlink, self.json_file.name)
  738. self.workflow = self.app.client_manager.workflow_engine
  739. execution = mock.Mock()
  740. execution.id = "IDID"
  741. self.workflow.executions.create.return_value = execution
  742. client = self.app.client_manager.tripleoclient
  743. self.websocket = client.messaging_websocket()
  744. # Get the command object to test
  745. self.cmd = overcloud_node.ImportNode(self.app, None)
  746. image = collections.namedtuple('image', ['id', 'name'])
  747. self.app.client_manager.image = mock.Mock()
  748. self.app.client_manager.image.images.list.return_value = [
  749. image(id=3, name='overcloud-full'),
  750. image(id=6, name='x86_64-overcloud-full'),
  751. image(id=9, name='SNB-x86_64-overcloud-full'),
  752. ]
  753. self.http_boot = '/var/lib/ironic/httpboot'
  754. existing = ['agent', 'x86_64/agent', 'SNB-x86_64/agent']
  755. existing = {os.path.join(self.http_boot, name + ext)
  756. for name in existing for ext in ('.kernel', '.ramdisk')}
  757. self.useFixture(fixtures.MockPatch(
  758. 'os.path.exists', autospec=True,
  759. side_effect=lambda path: path in existing))
  760. def _check_workflow_call(self, parsed_args, introspect=False,
  761. provide=False, local=None, no_deploy_image=False):
  762. self.websocket.wait_for_messages.return_value = [{
  763. "status": "SUCCESS",
  764. "message": "Success",
  765. "registered_nodes": [{
  766. "uuid": "MOCK_NODE_UUID"
  767. }],
  768. "execution_id": "IDID"
  769. }]
  770. self.cmd.take_action(parsed_args)
  771. nodes_list = copy.deepcopy(self.nodes_list)
  772. if not no_deploy_image:
  773. nodes_list[0]['kernel_id'] = (
  774. 'file://%s/agent.kernel' % self.http_boot)
  775. nodes_list[0]['ramdisk_id'] = (
  776. 'file://%s/agent.ramdisk' % self.http_boot)
  777. nodes_list[1]['kernel_id'] = (
  778. 'file://%s/x86_64/agent.kernel' % self.http_boot)
  779. nodes_list[1]['ramdisk_id'] = (
  780. 'file://%s/x86_64/agent.ramdisk' % self.http_boot)
  781. nodes_list[2]['kernel_id'] = (
  782. 'file://%s/SNB-x86_64/agent.kernel' % self.http_boot)
  783. nodes_list[2]['ramdisk_id'] = (
  784. 'file://%s/SNB-x86_64/agent.ramdisk' % self.http_boot)
  785. call_count = 1
  786. call_list = [mock.call(
  787. 'tripleo.baremetal.v1.register_or_update', workflow_input={
  788. 'nodes_json': nodes_list,
  789. 'instance_boot_option': ('local' if local is True else
  790. 'netboot' if local is False else None)
  791. }
  792. )]
  793. if introspect:
  794. call_count += 1
  795. call_list.append(mock.call(
  796. 'tripleo.baremetal.v1.introspect', workflow_input={
  797. 'node_uuids': ['MOCK_NODE_UUID'],
  798. 'run_validations': False,
  799. 'concurrency': 20}
  800. ))
  801. if provide:
  802. call_count += 1
  803. call_list.append(mock.call(
  804. 'tripleo.baremetal.v1.provide', workflow_input={
  805. 'node_uuids': ['MOCK_NODE_UUID']
  806. }
  807. ))
  808. self.workflow.executions.create.assert_has_calls(call_list)
  809. self.assertEqual(self.workflow.executions.create.call_count,
  810. call_count)
  811. def test_import_only(self):
  812. argslist = [self.json_file.name]
  813. verifylist = [('introspect', False),
  814. ('provide', False)]
  815. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  816. self._check_workflow_call(parsed_args)
  817. def test_import_and_introspect(self):
  818. argslist = [self.json_file.name, '--introspect']
  819. verifylist = [('introspect', True),
  820. ('provide', False)]
  821. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  822. self._check_workflow_call(parsed_args, introspect=True)
  823. def test_import_and_provide(self):
  824. argslist = [self.json_file.name, '--provide']
  825. verifylist = [('introspect', False),
  826. ('provide', True)]
  827. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  828. self._check_workflow_call(parsed_args, provide=True)
  829. def test_import_and_introspect_and_provide(self):
  830. argslist = [self.json_file.name, '--introspect', '--provide']
  831. verifylist = [('introspect', True),
  832. ('provide', True)]
  833. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  834. self._check_workflow_call(parsed_args, introspect=True, provide=True)
  835. def test_import_with_netboot(self):
  836. arglist = [self.json_file.name, '--instance-boot-option', 'netboot']
  837. verifylist = [('instance_boot_option', 'netboot')]
  838. parsed_args = self.check_parser(self.cmd, arglist, verifylist)
  839. self._check_workflow_call(parsed_args, local=False)
  840. def test_import_with_no_deployed_image(self):
  841. arglist = [self.json_file.name, '--no-deploy-image']
  842. verifylist = [('no_deploy_image', True)]
  843. parsed_args = self.check_parser(self.cmd, arglist, verifylist)
  844. self._check_workflow_call(parsed_args, no_deploy_image=True)
  845. class TestConfigureNode(fakes.TestOvercloudNode):
  846. def setUp(self):
  847. super(TestConfigureNode, self).setUp()
  848. self.workflow = self.app.client_manager.workflow_engine
  849. execution = mock.Mock()
  850. execution.id = "IDID"
  851. self.workflow.executions.create.return_value = execution
  852. client = self.app.client_manager.tripleoclient
  853. self.websocket = client.messaging_websocket()
  854. self.websocket.wait_for_messages.return_value = iter([{
  855. "status": "SUCCESS",
  856. "message": "",
  857. "execution_id": "IDID"
  858. }])
  859. # Get the command object to test
  860. self.cmd = overcloud_node.ConfigureNode(self.app, None)
  861. self.http_boot = '/var/lib/ironic/httpboot'
  862. self.workflow_input = {
  863. 'kernel_name': 'file://%s/agent.kernel' % self.http_boot,
  864. 'ramdisk_name': 'file://%s/agent.ramdisk' % self.http_boot,
  865. 'instance_boot_option': None,
  866. 'root_device': None,
  867. 'root_device_minimum_size': 4,
  868. 'overwrite_root_device_hints': False
  869. }
  870. def test_configure_all_manageable_nodes(self):
  871. parsed_args = self.check_parser(self.cmd,
  872. ['--all-manageable'],
  873. [('all_manageable', True)])
  874. self.cmd.take_action(parsed_args)
  875. self.workflow.executions.create.assert_called_once_with(
  876. 'tripleo.baremetal.v1.configure_manageable_nodes',
  877. workflow_input=self.workflow_input
  878. )
  879. def test_failed_to_configure_all_manageable_nodes(self):
  880. self.websocket.wait_for_messages.return_value = iter([{
  881. "status": "FAILED",
  882. "message": "Test failure.",
  883. "execution_id": "IDID"
  884. }])
  885. parsed_args = self.check_parser(self.cmd, ['--all-manageable'], [])
  886. self.assertRaises(exceptions.NodeConfigurationError,
  887. self.cmd.take_action, parsed_args)
  888. self.workflow.executions.create.assert_called_once_with(
  889. 'tripleo.baremetal.v1.configure_manageable_nodes',
  890. workflow_input=self.workflow_input
  891. )
  892. def test_configure_specified_nodes(self):
  893. argslist = ['node_uuid1', 'node_uuid2']
  894. verifylist = [('node_uuids', ['node_uuid1', 'node_uuid2'])]
  895. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  896. self.cmd.take_action(parsed_args)
  897. self.workflow_input['node_uuids'] = ['node_uuid1', 'node_uuid2']
  898. self.workflow.executions.create.assert_called_once_with(
  899. 'tripleo.baremetal.v1.configure',
  900. workflow_input=self.workflow_input
  901. )
  902. def test_failed_to_configure_specified_nodes(self):
  903. self.websocket.wait_for_messages.return_value = iter([{
  904. "status": "FAILED",
  905. "message": "Test failure.",
  906. "execution_id": "IDID"
  907. }])
  908. parsed_args = self.check_parser(self.cmd, ['node_uuid1'], [])
  909. self.assertRaises(exceptions.NodeConfigurationError,
  910. self.cmd.take_action, parsed_args)
  911. self.workflow_input['node_uuids'] = ['node_uuid1']
  912. self.workflow.executions.create.assert_called_once_with(
  913. 'tripleo.baremetal.v1.configure',
  914. workflow_input=self.workflow_input
  915. )
  916. def test_configure_no_node_or_flag_specified(self):
  917. self.assertRaises(test_utils.ParserException,
  918. self.check_parser,
  919. self.cmd, [], [])
  920. def test_configure_uuids_and_all_both_specified(self):
  921. argslist = ['node_id1', 'node_id2', '--all-manageable']
  922. verifylist = [('node_uuids', ['node_id1', 'node_id2']),
  923. ('all_manageable', True)]
  924. self.assertRaises(test_utils.ParserException,
  925. self.check_parser,
  926. self.cmd, argslist, verifylist)
  927. def test_configure_kernel_and_ram(self):
  928. argslist = ['--all-manageable', '--deploy-ramdisk', 'test_ramdisk',
  929. '--deploy-kernel', 'test_kernel']
  930. verifylist = [('deploy_kernel', 'test_kernel'),
  931. ('deploy_ramdisk', 'test_ramdisk')]
  932. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  933. self.cmd.take_action(parsed_args)
  934. self.workflow_input['kernel_name'] = 'test_kernel'
  935. self.workflow_input['ramdisk_name'] = 'test_ramdisk'
  936. self.workflow.executions.create.assert_called_once_with(
  937. 'tripleo.baremetal.v1.configure_manageable_nodes',
  938. workflow_input=self.workflow_input
  939. )
  940. def test_configure_instance_boot_option(self):
  941. argslist = ['--all-manageable', '--instance-boot-option', 'netboot']
  942. verifylist = [('instance_boot_option', 'netboot')]
  943. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  944. self.cmd.take_action(parsed_args)
  945. self.workflow_input['instance_boot_option'] = 'netboot'
  946. self.workflow.executions.create.assert_called_once_with(
  947. 'tripleo.baremetal.v1.configure_manageable_nodes',
  948. workflow_input=self.workflow_input
  949. )
  950. def test_configure_root_device(self):
  951. argslist = ['--all-manageable',
  952. '--root-device', 'smallest',
  953. '--root-device-minimum-size', '2',
  954. '--overwrite-root-device-hints']
  955. verifylist = [('root_device', 'smallest'),
  956. ('root_device_minimum_size', 2),
  957. ('overwrite_root_device_hints', True)]
  958. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  959. self.cmd.take_action(parsed_args)
  960. self.workflow_input['root_device'] = 'smallest'
  961. self.workflow_input['root_device_minimum_size'] = 2
  962. self.workflow_input['overwrite_root_device_hints'] = True
  963. self.workflow.executions.create.assert_called_once_with(
  964. 'tripleo.baremetal.v1.configure_manageable_nodes',
  965. workflow_input=self.workflow_input
  966. )
  967. def test_configure_specified_node_with_all_arguments(self):
  968. argslist = ['node_id',
  969. '--deploy-kernel', 'test_kernel',
  970. '--deploy-ramdisk', 'test_ramdisk',
  971. '--instance-boot-option', 'netboot',
  972. '--root-device', 'smallest',
  973. '--root-device-minimum-size', '2',
  974. '--overwrite-root-device-hints']
  975. verifylist = [('node_uuids', ['node_id']),
  976. ('deploy_kernel', 'test_kernel'),
  977. ('deploy_ramdisk', 'test_ramdisk'),
  978. ('instance_boot_option', 'netboot'),
  979. ('root_device', 'smallest'),
  980. ('root_device_minimum_size', 2),
  981. ('overwrite_root_device_hints', True)]
  982. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  983. self.cmd.take_action(parsed_args)
  984. self.workflow_input.update({'node_uuids': ['node_id'],
  985. 'kernel_name': 'test_kernel',
  986. 'ramdisk_name': 'test_ramdisk',
  987. 'instance_boot_option': 'netboot',
  988. 'root_device': 'smallest',
  989. 'root_device_minimum_size': 2,
  990. 'overwrite_root_device_hints': True})
  991. self.workflow.executions.create.assert_called_once_with(
  992. 'tripleo.baremetal.v1.configure',
  993. workflow_input=self.workflow_input
  994. )
  995. class TestDiscoverNode(fakes.TestOvercloudNode):
  996. def setUp(self):
  997. super(TestDiscoverNode, self).setUp()
  998. self.workflow = self.app.client_manager.workflow_engine
  999. execution = mock.Mock()
  1000. execution.id = "IDID"
  1001. self.workflow.executions.create.return_value = execution
  1002. client = self.app.client_manager.tripleoclient
  1003. client.create_mistral_context = plugin.ClientWrapper(
  1004. instance=ooofakes.FakeInstanceData
  1005. ).create_mistral_context
  1006. self.websocket = client.messaging_websocket()
  1007. self.cmd = overcloud_node.DiscoverNode(self.app, None)
  1008. self.gcn = mock.patch(
  1009. 'tripleo_common.actions.baremetal.GetCandidateNodes',
  1010. autospec=True
  1011. )
  1012. self.gcn.start()
  1013. self.addCleanup(self.gcn.stop)
  1014. self.roun = mock.patch(
  1015. 'tripleo_common.actions.baremetal.RegisterOrUpdateNodes',
  1016. autospec=True
  1017. )
  1018. self.roun.start()
  1019. self.addCleanup(self.roun.stop)
  1020. self.websocket.wait_for_messages.return_value = [{
  1021. "status": "SUCCESS",
  1022. "message": "Success",
  1023. "registered_nodes": [{
  1024. "uuid": "MOCK_NODE_UUID"
  1025. }],
  1026. "execution_id": "IDID"
  1027. }]
  1028. self.http_boot = '/var/lib/ironic/httpboot'
  1029. def test_with_ip_range(self):
  1030. argslist = ['--range', '10.0.0.0/24',
  1031. '--credentials', 'admin:password']
  1032. verifylist = [('ip_addresses', '10.0.0.0/24'),
  1033. ('credentials', ['admin:password'])]
  1034. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  1035. self.cmd.take_action(parsed_args)
  1036. def test_with_address_list(self):
  1037. argslist = ['--ip', '10.0.0.1', '--ip', '10.0.0.2',
  1038. '--credentials', 'admin:password']
  1039. verifylist = [('ip_addresses', ['10.0.0.1', '10.0.0.2']),
  1040. ('credentials', ['admin:password'])]
  1041. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  1042. self.cmd.take_action(parsed_args)
  1043. def test_with_all_options(self):
  1044. argslist = ['--range', '10.0.0.0/24',
  1045. '--credentials', 'admin:password',
  1046. '--credentials', 'admin2:password2',
  1047. '--port', '623', '--port', '6230',
  1048. '--introspect', '--provide', '--run-validations',
  1049. '--no-deploy-image', '--instance-boot-option', 'netboot',
  1050. '--concurrency', '10']
  1051. verifylist = [('ip_addresses', '10.0.0.0/24'),
  1052. ('credentials', ['admin:password', 'admin2:password2']),
  1053. ('port', [623, 6230]),
  1054. ('introspect', True),
  1055. ('run_validations', True),
  1056. ('concurrency', 10),
  1057. ('provide', True),
  1058. ('no_deploy_image', True),
  1059. ('instance_boot_option', 'netboot')]
  1060. parsed_args = self.check_parser(self.cmd, argslist, verifylist)
  1061. self.cmd.take_action(parsed_args)
  1062. workflows_calls = [
  1063. mock.call('tripleo.baremetal.v1.introspect',
  1064. workflow_input={'node_uuids': [],
  1065. 'run_validations': True,
  1066. 'concurrency': 10}),
  1067. mock.call('tripleo.baremetal.v1.provide',
  1068. workflow_input={'node_uuids': []}
  1069. )
  1070. ]
  1071. self.workflow.executions.create.assert_has_calls(workflows_calls)
  1072. class TestProvisionNode(fakes.TestOvercloudNode):
  1073. def setUp(self):
  1074. super(TestProvisionNode, self).setUp()
  1075. self.workflow = self.app.client_manager.workflow_engine
  1076. execution = mock.Mock()
  1077. execution.id = "IDID"
  1078. self.workflow.executions.create.return_value = execution
  1079. client = self.app.client_manager.tripleoclient
  1080. self.websocket = client.messaging_websocket()
  1081. self.websocket.wait_for_messages.return_value = [{
  1082. "status": "SUCCESS",
  1083. "message": "Success",
  1084. "environment": {"cat": "meow"},
  1085. "execution": {"id": "IDID"}
  1086. }]
  1087. self.cmd = overcloud_node.ProvisionNode(self.app, None)
  1088. def test_ok(self):
  1089. with tempfile.NamedTemporaryFile() as inp:
  1090. with tempfile.NamedTemporaryFile() as outp:
  1091. with tempfile.NamedTemporaryFile() as keyf:
  1092. inp.write(b'- name: Compute\n- name: Controller\n')
  1093. inp.flush()
  1094. keyf.write(b'I am a key')
  1095. keyf.flush()
  1096. argslist = ['--output', outp.name,
  1097. '--overcloud-ssh-key', keyf.name,
  1098. inp.name]
  1099. verifylist = [('input', inp.name),
  1100. ('output', outp.name),
  1101. ('overcloud_ssh_key', keyf.name)]
  1102. parsed_args = self.check_parser(self.cmd,
  1103. argslist, verifylist)
  1104. self.cmd.take_action(parsed_args)
  1105. data = yaml.safe_load(outp)
  1106. self.assertEqual({"cat": "meow"}, data)
  1107. self.workflow.executions.create.assert_called_once_with(
  1108. 'tripleo.baremetal_deploy.v1.deploy_roles',
  1109. workflow_input={'roles': [{'name': 'Compute'},
  1110. {'name': 'Controller'}],
  1111. 'plan': 'overcloud',
  1112. 'ssh_keys': ['I am a key'],
  1113. 'ssh_user_name': 'heat-admin'}
  1114. )
  1115. class TestUnprovisionNode(fakes.TestOvercloudNode):
  1116. def setUp(self):
  1117. super(TestUnprovisionNode, self).setUp()
  1118. self.workflow = self.app.client_manager.workflow_engine
  1119. execution = mock.Mock()
  1120. execution.id = "IDID"
  1121. self.workflow.executions.create.return_value = execution
  1122. client = self.app.client_manager.tripleoclient
  1123. self.websocket = client.messaging_websocket()
  1124. self.websocket.wait_for_messages.return_value = [{
  1125. "status": "SUCCESS",
  1126. "message": "Success",
  1127. "environment": {"cat": "meow"},
  1128. "execution": {"id": "IDID"}
  1129. }]
  1130. self.cmd = overcloud_node.UnprovisionNode(self.app, None)
  1131. def test_ok(self):
  1132. rv = mock.Mock()
  1133. rv.output = json.dumps({
  1134. 'result': {
  1135. 'instances': [
  1136. {'hostname': 'compute-0', 'name': 'baremetal-1'},
  1137. {'hostname': 'controller-0', 'name': 'baremetal-2'}
  1138. ]
  1139. }
  1140. })
  1141. self.workflow.action_executions.create.return_value = rv
  1142. with tempfile.NamedTemporaryFile() as inp:
  1143. inp.write(b'- name: Compute\n- name: Controller\n')
  1144. inp.flush()
  1145. argslist = ['--yes', inp.name]
  1146. verifylist = [('input', inp.name), ('yes', True)]
  1147. parsed_args = self.check_parser(self.cmd,
  1148. argslist, verifylist)
  1149. self.cmd.take_action(parsed_args)
  1150. self.workflow.executions.create.assert_called_once_with(
  1151. 'tripleo.baremetal_deploy.v1.undeploy_roles',
  1152. workflow_input={
  1153. 'plan': 'overcloud',
  1154. 'roles': [{
  1155. 'name': 'Unprovisioned',
  1156. 'count': 0,
  1157. 'instances': [
  1158. {'hostname': u'compute-0', 'provisioned': False},
  1159. {'hostname': u'controller-0', 'provisioned': False}
  1160. ]
  1161. }]
  1162. }
  1163. )
  1164. def test_ok_all(self):
  1165. rv = mock.Mock()
  1166. rv.output = json.dumps({
  1167. 'result': {
  1168. 'instances': [
  1169. {'hostname': 'compute-0', 'name': 'baremetal-1'},
  1170. {'hostname': 'controller-0', 'name': 'baremetal-2'}
  1171. ]
  1172. }
  1173. })
  1174. rv_provisioned = mock.Mock()
  1175. rv_provisioned.output = json.dumps({
  1176. 'result': {
  1177. 'instances': [
  1178. {'hostname': 'compute-1', 'name': 'baremetal-3'},
  1179. {'hostname': 'controller-1', 'name': 'baremetal-4'}
  1180. ]
  1181. }
  1182. })
  1183. self.workflow.action_executions.create.side_effect = [
  1184. rv, rv_provisioned
  1185. ]
  1186. with tempfile.NamedTemporaryFile() as inp:
  1187. inp.write(b'- name: Compute\n- name: Controller\n')
  1188. inp.flush()
  1189. argslist = ['--all', '--yes', inp.name]
  1190. verifylist = [('input', inp.name), ('yes', True), ('all', True)]
  1191. parsed_args = self.check_parser(self.cmd,
  1192. argslist, verifylist)
  1193. self.cmd.take_action(parsed_args)
  1194. self.workflow.executions.create.assert_called_once_with(
  1195. 'tripleo.baremetal_deploy.v1.undeploy_roles',
  1196. workflow_input={
  1197. 'plan': 'overcloud',
  1198. 'roles': [{
  1199. 'name': 'Unprovisioned',
  1200. 'count': 0,
  1201. 'instances': [
  1202. {'hostname': u'compute-0', 'provisioned': False},
  1203. {'hostname': u'controller-0', 'provisioned': False},
  1204. {'hostname': u'compute-1', 'provisioned': False},
  1205. {'hostname': u'controller-1', 'provisioned': False}
  1206. ]
  1207. }]
  1208. }
  1209. )