Use Mistral for baremetal boot configuration
This updates the 'baremetal configure boot' command to work with the new Mistral workflow. The Root Device tests have migrated to tripleo-common where the main code now lives as a Mistral action (see dependent patch). Change-Id: I3acbfd7900177fc2159448b839b71599e8e51d5f Depends-On: I5ba0a3710012c44822dd3b8e69662bbef04d3787 Related-Bug: #1595205
This commit is contained in:
parent
d0b6de2b4a
commit
6900d694e3
@ -12,13 +12,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
from oslo_utils import units
|
||||
import yaml
|
||||
|
||||
from tripleoclient import exceptions
|
||||
@ -892,17 +891,42 @@ class TestConfigureBaremetalBoot(fakes.TestBaremetal):
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal.ConfigureBaremetalBoot(self.app, None)
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
||||
def test_configure_boot(self, find_resource_mock):
|
||||
# Mistral-related mocks
|
||||
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": ""
|
||||
}
|
||||
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
bm_client.node.list.return_value = [
|
||||
uuid4_patcher = mock.patch('uuid.uuid4', return_value="UUID4")
|
||||
self.mock_uuid4 = uuid4_patcher.start()
|
||||
self.addCleanup(self.mock_uuid4.stop)
|
||||
|
||||
self.workflow_input = {'queue_name': 'UUID4',
|
||||
'node_uuids': ['ABCDEFGH'],
|
||||
'kernel_name': 'bm-deploy-kernel',
|
||||
'ramdisk_name': 'bm-deploy-ramdisk',
|
||||
'root_device': None,
|
||||
'root_device_minimum_size': 4,
|
||||
'overwrite_root_device_hints': False}
|
||||
# Ironic mocks
|
||||
self.bm_client = self.app.client_manager.baremetal
|
||||
self.bm_client.node.list.return_value = [
|
||||
mock.Mock(uuid="ABCDEFGH"),
|
||||
]
|
||||
|
||||
self.node = mock.Mock(uuid="ABCDEFGH", properties={})
|
||||
self.bm_client.node.get.return_value = self.node
|
||||
|
||||
def test_configure_boot(self):
|
||||
self.bm_client.node.list.return_value = [
|
||||
mock.Mock(uuid="ABCDEFGH"),
|
||||
mock.Mock(uuid="IJKLMNOP"),
|
||||
]
|
||||
|
||||
bm_client.node.get.side_effect = [
|
||||
self.bm_client.node.get.side_effect = [
|
||||
mock.Mock(uuid="ABCDEFGH", properties={}),
|
||||
mock.Mock(uuid="IJKLMNOP", properties={}),
|
||||
]
|
||||
@ -910,47 +934,23 @@ class TestConfigureBaremetalBoot(fakes.TestBaremetal):
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(find_resource_mock.call_count, 2)
|
||||
self.assertEqual(find_resource_mock.mock_calls, [
|
||||
mock.call(mock.ANY, 'bm-deploy-kernel'),
|
||||
mock.call(mock.ANY, 'bm-deploy-ramdisk')
|
||||
])
|
||||
call_list = [mock.call('tripleo.baremetal.v1.configure',
|
||||
workflow_input=self.workflow_input)]
|
||||
|
||||
self.assertEqual(bm_client.node.update.call_count, 2)
|
||||
self.assertEqual(bm_client.node.update.mock_calls, [
|
||||
mock.call('ABCDEFGH', [{
|
||||
'op': 'add', 'value': 'boot_option:local',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}]),
|
||||
mock.call('IJKLMNOP', [{
|
||||
'op': 'add', 'value': 'boot_option:local',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}])
|
||||
])
|
||||
workflow_input = copy.copy(self.workflow_input)
|
||||
workflow_input['node_uuids'] = ["IJKLMNOP"]
|
||||
call_list.append(mock.call('tripleo.baremetal.v1.configure',
|
||||
workflow_input=workflow_input))
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
||||
def test_configure_boot_with_suffix(self, find_resource_mock):
|
||||
self.workflow.executions.create.assert_has_calls(call_list)
|
||||
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
bm_client.node.list.return_value = [
|
||||
def test_configure_boot_with_suffix(self):
|
||||
self.bm_client.node.list.return_value = [
|
||||
mock.Mock(uuid="ABCDEFGH"),
|
||||
mock.Mock(uuid="IJKLMNOP"),
|
||||
]
|
||||
|
||||
bm_client.node.get.side_effect = [
|
||||
self.bm_client.node.get.side_effect = [
|
||||
mock.Mock(uuid="ABCDEFGH", properties={}),
|
||||
mock.Mock(uuid="IJKLMNOP", properties={}),
|
||||
]
|
||||
@ -963,390 +963,86 @@ class TestConfigureBaremetalBoot(fakes.TestBaremetal):
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(find_resource_mock.call_count, 2)
|
||||
self.assertEqual(find_resource_mock.mock_calls, [
|
||||
mock.call(mock.ANY, 'bm-deploy-kernel_20150101T100620'),
|
||||
mock.call(mock.ANY, 'bm-deploy-ramdisk_20150101T100620')
|
||||
])
|
||||
self.workflow_input['kernel_name'] = 'bm-deploy-kernel_20150101T100620'
|
||||
self.workflow_input['ramdisk_name'] = (
|
||||
'bm-deploy-ramdisk_20150101T100620')
|
||||
|
||||
self.assertEqual(bm_client.node.update.call_count, 2)
|
||||
self.assertEqual(bm_client.node.update.mock_calls, [
|
||||
mock.call('ABCDEFGH', [{
|
||||
'op': 'add', 'value': 'boot_option:local',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}]),
|
||||
mock.call('IJKLMNOP', [{
|
||||
'op': 'add', 'value': 'boot_option:local',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}])
|
||||
])
|
||||
call_list = [mock.call('tripleo.baremetal.v1.configure',
|
||||
workflow_input=self.workflow_input)]
|
||||
|
||||
workflow_input = copy.copy(self.workflow_input)
|
||||
workflow_input['node_uuids'] = ["IJKLMNOP"]
|
||||
call_list.append(mock.call('tripleo.baremetal.v1.configure',
|
||||
workflow_input=workflow_input))
|
||||
|
||||
self.workflow.executions.create.assert_has_calls(call_list)
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
||||
@mock.patch.object(baremetal.ConfigureBaremetalBoot, 'sleep_time',
|
||||
new_callable=mock.PropertyMock,
|
||||
return_value=0)
|
||||
def test_configure_boot_in_transition(self, _, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
def test_configure_boot_in_transition(self, _):
|
||||
self.bm_client.node.list.return_value = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None)]
|
||||
|
||||
self.bm_client.node.get.side_effect = [
|
||||
mock.Mock(uuid="ABCDEFGH", power_state=None, properties={}),
|
||||
mock.Mock(uuid="ABCDEFGH", power_state=None, properties={}),
|
||||
mock.Mock(uuid="ABCDEFGH", power_state='available', properties={}),
|
||||
mock.Mock(uuid="ABCDEFGH", power_state='available', properties={}),
|
||||
]
|
||||
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
bm_client.node.list.return_value = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None),
|
||||
]
|
||||
bm_client.node.get.side_effect = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None,
|
||||
properties={}),
|
||||
mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None,
|
||||
properties={}),
|
||||
mock.Mock(uuid="ABCDEFGH",
|
||||
power_state='available',
|
||||
properties={}),
|
||||
mock.Mock(uuid="ABCDEFGH",
|
||||
power_state='available',
|
||||
properties={}),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(1, bm_client.node.list.call_count)
|
||||
self.assertEqual(3, bm_client.node.get.call_count)
|
||||
self.assertEqual(1, bm_client.node.update.call_count)
|
||||
self.assertEqual(1, self.bm_client.node.list.call_count)
|
||||
self.assertEqual(3, self.bm_client.node.get.call_count)
|
||||
self.assertEqual(1, self.workflow.executions.create.call_count)
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
||||
@mock.patch.object(baremetal.ConfigureBaremetalBoot, 'sleep_time',
|
||||
new_callable=mock.PropertyMock,
|
||||
return_value=0)
|
||||
def test_configure_boot_timeout(self, _, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
bm_client.node.list.return_value = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None),
|
||||
]
|
||||
bm_client.node.get.return_value = mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None)
|
||||
def test_configure_boot_timeout(self, _):
|
||||
self.bm_client.node.list.return_value = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None)]
|
||||
self.bm_client.node.get.return_value = mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None)
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.assertRaises(exceptions.Timeout,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
||||
def test_configure_boot_skip_maintenance(self, find_resource_mock):
|
||||
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
bm_client.node.list.return_value = [
|
||||
def test_configure_boot_skip_maintenance(self):
|
||||
self.bm_client.node.list.return_value = [
|
||||
mock.Mock(uuid="ABCDEFGH", maintenance=False),
|
||||
]
|
||||
|
||||
bm_client.node.get.return_value = mock.Mock(uuid="ABCDEFGH",
|
||||
maintenance=False,
|
||||
properties={})
|
||||
self.bm_client.node.get.return_value = mock.Mock(uuid="ABCDEFGH",
|
||||
maintenance=False,
|
||||
properties={})
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(bm_client.node.list.mock_calls, [mock.call(
|
||||
self.assertEqual(self.bm_client.node.list.mock_calls, [mock.call(
|
||||
maintenance=False)])
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
||||
def test_configure_boot_existing_properties(self, find_resource_mock):
|
||||
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
bm_client.node.list.return_value = [
|
||||
mock.Mock(uuid="ABCDEFGH"),
|
||||
mock.Mock(uuid="IJKLMNOP"),
|
||||
mock.Mock(uuid="QRSTUVWX"),
|
||||
mock.Mock(uuid="YZABCDEF"),
|
||||
]
|
||||
|
||||
bm_client.node.get.side_effect = [
|
||||
mock.Mock(uuid="ABCDEFGH", properties={
|
||||
'capabilities': 'existing:cap'
|
||||
}),
|
||||
mock.Mock(uuid="IJKLMNOP", properties={
|
||||
'capabilities': 'boot_option:local'
|
||||
}),
|
||||
mock.Mock(uuid="QRSTUVWX", properties={
|
||||
'capabilities': 'boot_option:remote'
|
||||
}),
|
||||
mock.Mock(uuid="YZABCDEF", properties={}),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(find_resource_mock.call_count, 2)
|
||||
|
||||
self.assertEqual(bm_client.node.update.call_count, 4)
|
||||
self.assertEqual(bm_client.node.update.mock_calls, [
|
||||
mock.call('ABCDEFGH', [{
|
||||
'op': 'add', 'value': 'boot_option:local,existing:cap',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}]),
|
||||
mock.call('IJKLMNOP', [{
|
||||
'op': 'add', 'value': 'boot_option:local',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}]),
|
||||
mock.call('QRSTUVWX', [{
|
||||
'op': 'add', 'value': 'boot_option:remote',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}]),
|
||||
mock.call('YZABCDEF', [{
|
||||
'op': 'add', 'value': 'boot_option:local',
|
||||
'path': '/properties/capabilities'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_ramdisk'
|
||||
}, {
|
||||
'op': 'add', 'value': 'IDIDID',
|
||||
'path': '/driver_info/deploy_kernel'
|
||||
}]),
|
||||
])
|
||||
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
||||
class TestConfigureBaremetalBootRootDeviceDetection(fakes.TestBaremetal):
|
||||
|
||||
def setUp(self):
|
||||
super(TestConfigureBaremetalBootRootDeviceDetection, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal.ConfigureBaremetalBoot(self.app, None)
|
||||
|
||||
self.disks = [
|
||||
{'name': '/dev/sda', 'size': 11 * units.Gi},
|
||||
{'name': '/dev/sdb', 'size': 2 * units.Gi},
|
||||
{'name': '/dev/sdc', 'size': 5 * units.Gi},
|
||||
{'name': '/dev/sdd', 'size': 21 * units.Gi},
|
||||
{'name': '/dev/sde', 'size': 13 * units.Gi},
|
||||
]
|
||||
for i, disk in enumerate(self.disks):
|
||||
disk['wwn'] = 'wwn%d' % i
|
||||
disk['serial'] = 'serial%d' % i
|
||||
|
||||
self.inspector_client = self.app.client_manager.baremetal_introspection
|
||||
self.inspector_client.data['ABCDEFGH'] = {
|
||||
'inventory': {'disks': self.disks}
|
||||
}
|
||||
|
||||
self.bm_client = self.app.client_manager.baremetal
|
||||
self.bm_client.node.list.return_value = [
|
||||
mock.Mock(uuid="ABCDEFGH"),
|
||||
]
|
||||
|
||||
self.node = mock.Mock(uuid="ABCDEFGH", properties={})
|
||||
self.bm_client.node.get.return_value = self.node
|
||||
|
||||
def test_smallest(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
arglist = ['--root-device', 'smallest']
|
||||
verifylist = [('root_device', 'smallest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 2)
|
||||
root_device_args = self.bm_client.node.update.call_args_list[1]
|
||||
expected_patch = [{'op': 'add', 'path': '/properties/root_device',
|
||||
'value': {'wwn': 'wwn2'}},
|
||||
{'op': 'add', 'path': '/properties/local_gb',
|
||||
'value': 4}]
|
||||
self.assertEqual(mock.call('ABCDEFGH', expected_patch),
|
||||
root_device_args)
|
||||
|
||||
def test_largest(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
arglist = ['--root-device', 'largest']
|
||||
verifylist = [('root_device', 'largest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 2)
|
||||
root_device_args = self.bm_client.node.update.call_args_list[1]
|
||||
expected_patch = [{'op': 'add', 'path': '/properties/root_device',
|
||||
'value': {'wwn': 'wwn3'}},
|
||||
{'op': 'add', 'path': '/properties/local_gb',
|
||||
'value': 20}]
|
||||
self.assertEqual(mock.call('ABCDEFGH', expected_patch),
|
||||
root_device_args)
|
||||
|
||||
def test_no_overwrite(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
self.node.properties['root_device'] = {'foo': 'bar'}
|
||||
|
||||
arglist = ['--root-device', 'smallest']
|
||||
verifylist = [('root_device', 'smallest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 1)
|
||||
|
||||
def test_with_overwrite(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
self.node.properties['root_device'] = {'foo': 'bar'}
|
||||
|
||||
arglist = ['--root-device', 'smallest',
|
||||
'--overwrite-root-device-hints']
|
||||
def test_root_device_options(self):
|
||||
argslist = ['--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, arglist, verifylist)
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 2)
|
||||
root_device_args = self.bm_client.node.update.call_args_list[1]
|
||||
expected_patch = [{'op': 'add', 'path': '/properties/root_device',
|
||||
'value': {'wwn': 'wwn2'}},
|
||||
{'op': 'add', 'path': '/properties/local_gb',
|
||||
'value': 4}]
|
||||
self.assertEqual(mock.call('ABCDEFGH', expected_patch),
|
||||
root_device_args)
|
||||
|
||||
def test_minimum_size(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
arglist = ['--root-device', 'smallest',
|
||||
'--root-device-minimum-size', '10']
|
||||
verifylist = [('root_device', 'smallest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 2)
|
||||
root_device_args = self.bm_client.node.update.call_args_list[1]
|
||||
expected_patch = [{'op': 'add', 'path': '/properties/root_device',
|
||||
'value': {'wwn': 'wwn0'}},
|
||||
{'op': 'add', 'path': '/properties/local_gb',
|
||||
'value': 10}]
|
||||
self.assertEqual(mock.call('ABCDEFGH', expected_patch),
|
||||
root_device_args)
|
||||
|
||||
def test_bad_inventory(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
del self.inspector_client.data['ABCDEFGH']['inventory']
|
||||
|
||||
arglist = ['--root-device', 'smallest']
|
||||
verifylist = [('root_device', 'smallest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaisesRegexp(exceptions.RootDeviceDetectionError,
|
||||
"Malformed introspection data",
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 1)
|
||||
|
||||
def test_no_disks(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
self.inspector_client.data['ABCDEFGH']['inventory']['disks'] = [
|
||||
{'name': '/dev/sda', 'size': 1 * units.Gi}
|
||||
]
|
||||
|
||||
arglist = ['--root-device', 'smallest']
|
||||
verifylist = [('root_device', 'smallest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaisesRegexp(exceptions.RootDeviceDetectionError,
|
||||
"No suitable disks",
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 1)
|
||||
|
||||
def test_no_data(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
del self.inspector_client.data['ABCDEFGH']
|
||||
|
||||
arglist = ['--root-device', 'smallest']
|
||||
verifylist = [('root_device', 'smallest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaisesRegexp(exceptions.RootDeviceDetectionError,
|
||||
"No introspection data",
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 1)
|
||||
|
||||
def test_no_wwn_and_serial(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
self.inspector_client.data['ABCDEFGH']['inventory']['disks'] = [
|
||||
{'name': '/dev/sda', 'size': 10 * units.Gi}
|
||||
]
|
||||
|
||||
arglist = ['--root-device', 'smallest']
|
||||
verifylist = [('root_device', 'smallest')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaisesRegexp(exceptions.RootDeviceDetectionError,
|
||||
"Neither WWN nor serial number are known",
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 1)
|
||||
|
||||
def test_device_list(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
arglist = ['--root-device', 'hda,sda,sdb,sdc']
|
||||
verifylist = [('root_device', 'hda,sda,sdb,sdc')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 2)
|
||||
root_device_args = self.bm_client.node.update.call_args_list[1]
|
||||
expected_patch = [{'op': 'add', 'path': '/properties/root_device',
|
||||
'value': {'wwn': 'wwn0'}},
|
||||
{'op': 'add', 'path': '/properties/local_gb',
|
||||
'value': 10}]
|
||||
self.assertEqual(mock.call('ABCDEFGH', expected_patch),
|
||||
root_device_args)
|
||||
|
||||
def test_device_list_not_found(self, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
arglist = ['--root-device', 'hda']
|
||||
verifylist = [('root_device', 'hda')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaisesRegexp(exceptions.RootDeviceDetectionError,
|
||||
"Cannot find a disk",
|
||||
self.cmd.take_action, parsed_args)
|
||||
|
||||
self.assertEqual(self.bm_client.node.update.call_count, 1)
|
||||
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',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
|
||||
class TestShowNodeCapabilities(fakes.TestBaremetal):
|
||||
|
@ -23,10 +23,7 @@ import uuid
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
import ironic_inspector_client
|
||||
from openstackclient.common import utils as osc_utils
|
||||
from openstackclient.i18n import _
|
||||
from oslo_utils import units
|
||||
|
||||
from tripleoclient import exceptions
|
||||
from tripleoclient import utils
|
||||
@ -391,31 +388,10 @@ class ConfigureBaremetalBoot(command.Command):
|
||||
def take_action(self, parsed_args):
|
||||
|
||||
self.log.debug("take_action(%s)" % parsed_args)
|
||||
|
||||
queue_name = str(uuid.uuid4())
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
|
||||
image_client = self.app.client_manager.image
|
||||
|
||||
try:
|
||||
kernel_id = osc_utils.find_resource(
|
||||
image_client.images, parsed_args.deploy_kernel).id
|
||||
except AttributeError:
|
||||
self.log.error("Please make sure that an image named \"%s\" exists"
|
||||
" in Glance and is the only image with this name."
|
||||
% parsed_args.deploy_ramdisk)
|
||||
return
|
||||
|
||||
try:
|
||||
ramdisk_id = osc_utils.find_resource(
|
||||
image_client.images, parsed_args.deploy_ramdisk).id
|
||||
except AttributeError:
|
||||
self.log.error("Please make sure that an image named \"%s\" exists"
|
||||
" in Glance and is the only image with this name."
|
||||
% parsed_args.deploy_ramdisk)
|
||||
return
|
||||
|
||||
self.log.debug("Using kernel ID: {0} and ramdisk ID: {1}".format(
|
||||
kernel_id, ramdisk_id))
|
||||
|
||||
for node in bm_client.node.list(maintenance=False):
|
||||
# NOTE(bnemec): Ironic won't let us update the node while the
|
||||
# power_state is transitioning.
|
||||
@ -438,136 +414,17 @@ class ConfigureBaremetalBoot(command.Command):
|
||||
node.uuid)
|
||||
raise exceptions.Timeout(msg)
|
||||
|
||||
# Get the full node info
|
||||
capabilities = node_detail.properties.get('capabilities', None)
|
||||
|
||||
# Only update capabilities to add boot_option if it doesn't exist.
|
||||
if capabilities:
|
||||
if 'boot_option' not in capabilities:
|
||||
capabilities = "boot_option:local,%s" % capabilities
|
||||
else:
|
||||
capabilities = "boot_option:local"
|
||||
|
||||
self.log.debug("Configuring boot for Node {0}".format(
|
||||
node.uuid))
|
||||
|
||||
bm_client.node.update(node.uuid, [
|
||||
{
|
||||
'op': 'add',
|
||||
'path': '/properties/capabilities',
|
||||
'value': capabilities,
|
||||
},
|
||||
{
|
||||
'op': 'add',
|
||||
'path': '/driver_info/deploy_ramdisk',
|
||||
'value': ramdisk_id,
|
||||
},
|
||||
{
|
||||
'op': 'add',
|
||||
'path': '/driver_info/deploy_kernel',
|
||||
'value': kernel_id,
|
||||
},
|
||||
])
|
||||
|
||||
self._apply_root_device_strategy(
|
||||
node_detail, parsed_args.root_device,
|
||||
parsed_args.root_device_minimum_size,
|
||||
parsed_args.overwrite_root_device_hints)
|
||||
|
||||
def _apply_root_device_strategy(self, node, strategy, minimum_size,
|
||||
overwrite=False):
|
||||
if not strategy:
|
||||
return
|
||||
|
||||
if node.properties.get('root_device') and not overwrite:
|
||||
# This is a correct situation, we still want to allow people to
|
||||
# fine-tune the root device setting for a subset of nodes.
|
||||
# However, issue a warning, so that they know which nodes were not
|
||||
# updated during this run.
|
||||
self.log.warning('Root device hints are already set for node %s '
|
||||
'and overwriting is not requested, skipping',
|
||||
node.uuid)
|
||||
self.log.warning('You may unset them by running $ ironic '
|
||||
'node-update %s remove properties/root_device',
|
||||
node.uuid)
|
||||
return
|
||||
|
||||
inspector_client = self.app.client_manager.baremetal_introspection
|
||||
try:
|
||||
data = inspector_client.get_data(node.uuid)
|
||||
except ironic_inspector_client.ClientError:
|
||||
raise exceptions.RootDeviceDetectionError(
|
||||
'No introspection data found for node %s, '
|
||||
'root device cannot be detected' % node.uuid)
|
||||
except AttributeError:
|
||||
raise RuntimeError('Ironic inspector client version 1.2.0 or '
|
||||
'newer is required for detecting root device')
|
||||
|
||||
try:
|
||||
disks = data['inventory']['disks']
|
||||
except KeyError:
|
||||
raise exceptions.RootDeviceDetectionError(
|
||||
'Malformed introspection data for node %s: '
|
||||
'disks list is missing' % node.uuid)
|
||||
|
||||
minimum_size *= units.Gi
|
||||
disks = [d for d in disks if d.get('size', 0) >= minimum_size]
|
||||
|
||||
if not disks:
|
||||
raise exceptions.RootDeviceDetectionError(
|
||||
'No suitable disks found for node %s' % node.uuid)
|
||||
|
||||
if strategy == 'smallest':
|
||||
disks.sort(key=lambda d: d['size'])
|
||||
root_device = disks[0]
|
||||
elif strategy == 'largest':
|
||||
disks.sort(key=lambda d: d['size'], reverse=True)
|
||||
root_device = disks[0]
|
||||
else:
|
||||
disk_names = [x.strip() for x in strategy.split(',')]
|
||||
disks = {d['name']: d for d in disks}
|
||||
for candidate in disk_names:
|
||||
try:
|
||||
root_device = disks['/dev/%s' % candidate]
|
||||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise exceptions.RootDeviceDetectionError(
|
||||
'Cannot find a disk with any of names %(strategy)s '
|
||||
'for node %(node)s' %
|
||||
{'strategy': strategy, 'node': node.uuid})
|
||||
|
||||
hint = None
|
||||
for hint_name in ('wwn', 'serial'):
|
||||
if root_device.get(hint_name):
|
||||
hint = {hint_name: root_device[hint_name]}
|
||||
break
|
||||
|
||||
if hint is None:
|
||||
# I don't think it might actually happen, but just in case
|
||||
raise exceptions.RootDeviceDetectionError(
|
||||
'Neither WWN nor serial number are known for device %(dev)s '
|
||||
'on node %(node)s; root device hints cannot be used' %
|
||||
{'dev': root_device['name'], 'node': node.uuid})
|
||||
|
||||
# During the introspection process we got local_gb assigned according
|
||||
# to the default strategy. Now we need to update it.
|
||||
new_size = root_device['size'] / units.Gi
|
||||
# This -1 is what we always do to account for partitioning
|
||||
new_size -= 1
|
||||
|
||||
bm_client = self.app.client_manager.baremetal
|
||||
bm_client.node.update(
|
||||
node.uuid,
|
||||
[{'op': 'add', 'path': '/properties/root_device', 'value': hint},
|
||||
{'op': 'add', 'path': '/properties/local_gb', 'value': new_size}])
|
||||
|
||||
self.log.info('Updated root device for node %(node)s, new device '
|
||||
'is %(dev)s, new local_gb is %(local_gb)d',
|
||||
{'node': node.uuid, 'dev': root_device,
|
||||
'local_gb': new_size})
|
||||
baremetal.configure(
|
||||
self.app.client_manager,
|
||||
node_uuids=[node.uuid],
|
||||
queue_name=queue_name,
|
||||
kernel_name=parsed_args.deploy_kernel,
|
||||
ramdisk_name=parsed_args.deploy_ramdisk,
|
||||
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)
|
||||
)
|
||||
|
||||
|
||||
class ShowNodeCapabilities(lister.Lister):
|
||||
|
Loading…
Reference in New Issue
Block a user