1459 lines
52 KiB
Python
1459 lines
52 KiB
Python
# Copyright 2015 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
|
|
import copy
|
|
import json
|
|
import os
|
|
import tempfile
|
|
|
|
import fixtures
|
|
import mock
|
|
from oslo_utils import units
|
|
import yaml
|
|
|
|
from tripleoclient import exceptions
|
|
from tripleoclient.tests.v1.baremetal import fakes
|
|
from tripleoclient.v1 import baremetal
|
|
|
|
|
|
class TestValidateInstackEnv(fakes.TestBaremetal):
|
|
|
|
def setUp(self):
|
|
super(TestValidateInstackEnv, self).setUp()
|
|
|
|
self.instack_json = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
|
|
|
# Get the command object to test
|
|
self.cmd = baremetal.ValidateInstackEnv(self.app, None)
|
|
|
|
def mock_instackenv_json(self, instackenv_data):
|
|
json.dump(instackenv_data, self.instack_json)
|
|
self.instack_json.close()
|
|
|
|
def tearDown(self):
|
|
super(TestValidateInstackEnv, self).tearDown()
|
|
os.unlink(self.instack_json.name)
|
|
|
|
def test_success(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "SOME SSH KEY",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(0, self.cmd.error_count)
|
|
|
|
def test_empty_password(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
def test_no_password(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
def test_empty_user(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "SOME SSH KEY",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
def test_no_user(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "SOME SSH KEY",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
def test_empty_mac(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "SOME SSH KEY",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
def test_no_mac(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "SOME SSH KEY",
|
|
"pm_type": "pxe_ssh",
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
def test_duplicated_mac(self):
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "KEY1",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:58"
|
|
],
|
|
}, {
|
|
"arch": "x86_64",
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.2",
|
|
"pm_password": "KEY2",
|
|
"pm_type": "pxe_ssh",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:58"
|
|
]
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
@mock.patch('tripleoclient.utils.run_shell')
|
|
def test_ipmitool_success(self, mock_run_shell):
|
|
mock_run_shell.return_value = 0
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "KEY1",
|
|
"pm_type": "pxe_ipmitool",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(0, self.cmd.error_count)
|
|
|
|
@mock.patch('tripleoclient.utils.run_shell')
|
|
def test_ipmitool_failure(self, mock_run_shell):
|
|
mock_run_shell.return_value = 1
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "KEY1",
|
|
"pm_type": "pxe_ipmitool",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
@mock.patch('tripleoclient.utils.run_shell')
|
|
def test_duplicated_baremetal_ip(self, mock_run_shell):
|
|
mock_run_shell.return_value = 0
|
|
self.mock_instackenv_json({
|
|
"nodes": [{
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "KEY1",
|
|
"pm_type": "pxe_ipmitool",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:59"
|
|
],
|
|
}, {
|
|
"arch": "x86_64",
|
|
"pm_user": "stack",
|
|
"pm_addr": "192.168.122.1",
|
|
"pm_password": "KEY2",
|
|
"pm_type": "pxe_ipmitool",
|
|
"mac": [
|
|
"00:0b:d0:69:7e:58"
|
|
]
|
|
}]
|
|
})
|
|
|
|
arglist = ['-f', self.instack_json.name]
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(1, self.cmd.error_count)
|
|
|
|
|
|
class TestImportBaremetal(fakes.TestBaremetal):
|
|
|
|
def setUp(self):
|
|
super(TestImportBaremetal, self).setUp()
|
|
|
|
# Get the command object to test
|
|
self.cmd = baremetal.ImportBaremetal(self.app, None)
|
|
|
|
self.csv_file = tempfile.NamedTemporaryFile(
|
|
mode='w', delete=False, suffix='.csv')
|
|
self.json_file = tempfile.NamedTemporaryFile(
|
|
mode='w', delete=False, suffix='.json')
|
|
self.instack_json = tempfile.NamedTemporaryFile(
|
|
mode='w', delete=False, suffix='.json')
|
|
self.yaml_file = tempfile.NamedTemporaryFile(
|
|
mode='w', delete=False, suffix='.yaml')
|
|
self.instack_yaml = tempfile.NamedTemporaryFile(
|
|
mode='w', delete=False, suffix='.yaml')
|
|
self.unsupported_txt = tempfile.NamedTemporaryFile(
|
|
mode='w', delete=False, suffix='.txt')
|
|
|
|
self.csv_file.write("""\
|
|
pxe_ssh,192.168.122.1,stack,"KEY1",00:0b:d0:69:7e:59
|
|
pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
|
|
|
|
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"
|
|
]
|
|
}]
|
|
|
|
json.dump(self.nodes_list, self.json_file)
|
|
json.dump({"nodes": self.nodes_list}, self.instack_json)
|
|
self.yaml_file.write(yaml.safe_dump(self.nodes_list, indent=2))
|
|
self.instack_yaml.write(
|
|
yaml.safe_dump({"nodes": self.nodes_list}, indent=2))
|
|
|
|
self.csv_file.close()
|
|
self.json_file.close()
|
|
self.instack_json.close()
|
|
self.yaml_file.close()
|
|
self.instack_yaml.close()
|
|
self.baremetal = self.app.client_manager.baremetal
|
|
self.baremetal.http_client.os_ironic_api_version = '1.11'
|
|
self.baremetal.node = fakes.FakeBaremetalNodeClient(
|
|
states={"ABCDEFGH": "enroll", "IJKLMNOP": "enroll"},
|
|
transitions={
|
|
("ABCDEFGH", "manage"): "manageable",
|
|
("IJKLMNOP", "manage"): "manageable",
|
|
("ABCDEFGH", "provide"): "available",
|
|
("IJKLMNOP", "provide"): "available",
|
|
|
|
}
|
|
)
|
|
self.mock_websocket_success = [{
|
|
"status": "SUCCESS",
|
|
"registered_nodes": [{
|
|
"uuid": "MOCK_NODE_UUID"
|
|
}],
|
|
}, {
|
|
"status": "SUCCESS"
|
|
}]
|
|
|
|
self.workflow = self.app.client_manager.workflow_engine
|
|
tripleoclient = self.app.client_manager.tripleoclient
|
|
websocket = tripleoclient.messaging_websocket()
|
|
websocket.wait_for_message.side_effect = self.mock_websocket_success
|
|
self.websocket = websocket
|
|
|
|
uuid4_patcher = mock.patch('uuid.uuid4', return_value="UUID4")
|
|
self.mock_uuid4 = uuid4_patcher.start()
|
|
self.addCleanup(self.mock_uuid4.stop)
|
|
|
|
def tearDown(self):
|
|
|
|
super(TestImportBaremetal, self).tearDown()
|
|
os.unlink(self.csv_file.name)
|
|
os.unlink(self.json_file.name)
|
|
os.unlink(self.instack_json.name)
|
|
os.unlink(self.yaml_file.name)
|
|
os.unlink(self.instack_yaml.name)
|
|
|
|
def _check_workflow_call(self, local=True, provide=True,
|
|
kernel_name='bm-deploy-kernel',
|
|
ramdisk_name='bm-deploy-ramdisk'):
|
|
nodes_list = copy.deepcopy(self.nodes_list)
|
|
for node in nodes_list:
|
|
if local:
|
|
node['capabilities'] = 'boot_option:local'
|
|
else:
|
|
node['capabilities'] = 'boot_option:netboot'
|
|
|
|
call_list = [mock.call(
|
|
'tripleo.baremetal.v1.register_or_update', workflow_input={
|
|
'kernel_name': kernel_name,
|
|
'nodes_json': nodes_list,
|
|
'queue_name': 'UUID4',
|
|
'ramdisk_name': ramdisk_name}
|
|
)]
|
|
|
|
if provide:
|
|
call_list.append(mock.call(
|
|
'tripleo.baremetal.v1.provide', workflow_input={
|
|
'node_uuids': ['MOCK_NODE_UUID', ],
|
|
'queue_name': 'UUID4'
|
|
}
|
|
))
|
|
|
|
self.workflow.executions.create.assert_has_calls(call_list)
|
|
|
|
self.assertEqual(self.workflow.executions.create.call_count,
|
|
2 if provide else 1)
|
|
|
|
def test_json_import(self):
|
|
|
|
arglist = [self.json_file.name, '--json', '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', True),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call()
|
|
|
|
def test_json_import_initial_state_enroll(self):
|
|
|
|
arglist = [
|
|
self.json_file.name,
|
|
'--json',
|
|
'-s', 'http://localhost',
|
|
'--initial-state', 'enroll'
|
|
]
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', True),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
self._check_workflow_call(provide=False)
|
|
self.assertEqual([], self.baremetal.node.updates)
|
|
|
|
def test_available_does_not_require_api_1_11(self):
|
|
arglist = [self.json_file.name, '--json', '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', True),
|
|
]
|
|
self.baremetal.http_client.os_ironic_api_version = '1.6'
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
self._check_workflow_call()
|
|
|
|
def test_enroll_requires_api_1_11(self):
|
|
arglist = [
|
|
self.json_file.name,
|
|
'--json',
|
|
'-s', 'http://localhost',
|
|
'--initial-state', 'enroll'
|
|
]
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', True),
|
|
]
|
|
self.baremetal.http_client.os_ironic_api_version = '1.6'
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
self.assertRaisesRegexp(exceptions.InvalidConfiguration,
|
|
'OS_BAREMETAL_API_VERSION',
|
|
self.cmd.take_action, parsed_args)
|
|
self.workflow.executions.create.assert_not_called()
|
|
|
|
def test_json_import_detect_suffix(self):
|
|
|
|
arglist = [self.json_file.name, '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', False),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call()
|
|
|
|
def test_instack_json_import(self):
|
|
|
|
arglist = [self.instack_json.name, '--json', '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', True),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call()
|
|
|
|
def test_csv_import(self):
|
|
|
|
arglist = [self.csv_file.name, '--csv', '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', True),
|
|
('json', False),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call()
|
|
|
|
def test_csv_import_detect_suffix(self):
|
|
|
|
arglist = [self.csv_file.name, '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', False),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call()
|
|
|
|
def test_yaml_import(self):
|
|
|
|
arglist = [self.yaml_file.name, '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', False),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call()
|
|
|
|
def test_invalid_import_filetype(self):
|
|
|
|
arglist = [self.unsupported_txt.name, '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', False),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.assertRaisesRegexp(exceptions.InvalidConfiguration,
|
|
'Invalid file extension',
|
|
self.cmd.take_action, parsed_args)
|
|
|
|
def test_instack_yaml_import(self):
|
|
|
|
arglist = [self.instack_yaml.name, '-s', 'http://localhost']
|
|
|
|
verifylist = [
|
|
('csv', False),
|
|
('json', False),
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call()
|
|
|
|
def test_netboot(self):
|
|
|
|
arglist = [self.json_file.name, '-s', 'http://localhost',
|
|
'--instance-boot-option', 'netboot']
|
|
|
|
verifylist = [
|
|
('instance_boot_option', 'netboot')
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call(local=False)
|
|
|
|
def test_custom_image(self):
|
|
|
|
arglist = [self.json_file.name, '-s', 'http://localhost',
|
|
'--deploy-kernel', 'k', '--deploy-ramdisk', 'r']
|
|
|
|
verifylist = [
|
|
('deploy_kernel', 'k'),
|
|
('deploy_ramdisk', 'r')
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call(kernel_name='k', ramdisk_name='r')
|
|
|
|
def test_no_image(self):
|
|
|
|
arglist = [self.json_file.name, '-s', 'http://localhost',
|
|
'--no-deploy-image']
|
|
|
|
verifylist = [
|
|
('no_deploy_image', True)
|
|
]
|
|
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self._check_workflow_call(kernel_name=None, ramdisk_name=None)
|
|
|
|
|
|
class TestStartBaremetalIntrospectionBulk(fakes.TestBaremetal):
|
|
|
|
def setUp(self):
|
|
super(TestStartBaremetalIntrospectionBulk, self).setUp()
|
|
|
|
# Get the command object to test
|
|
self.cmd = baremetal.StartBaremetalIntrospectionBulk(self.app, None)
|
|
|
|
def test_introspect_bulk_one(self):
|
|
client = self.app.client_manager.baremetal
|
|
client.node = fakes.FakeBaremetalNodeClient(
|
|
states={"ABCDEFGH": "available"},
|
|
transitions={
|
|
("ABCDEFGH", "manage"): "manageable",
|
|
("ABCDEFGH", "provide"): "available",
|
|
}
|
|
)
|
|
inspector_client = self.app.client_manager.baremetal_introspection
|
|
inspector_client.states['ABCDEFGH'] = {'finished': True, 'error': None}
|
|
|
|
parsed_args = self.check_parser(self.cmd, [], [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(client.node.updates, [
|
|
('ABCDEFGH', 'manage'),
|
|
('ABCDEFGH', 'provide')
|
|
])
|
|
self.assertEqual(['ABCDEFGH'], inspector_client.on_introspection)
|
|
|
|
def test_introspect_bulk_failed(self):
|
|
client = self.app.client_manager.baremetal
|
|
client.node = fakes.FakeBaremetalNodeClient(
|
|
states={"ABCDEFGH": "available", "IJKLMNOP": "available"},
|
|
transitions={
|
|
("ABCDEFGH", "manage"): "manageable",
|
|
("IJKLMNOP", "manage"): "manageable",
|
|
("ABCDEFGH", "provide"): "available",
|
|
}
|
|
)
|
|
inspector_client = self.app.client_manager.baremetal_introspection
|
|
inspector_client.states['ABCDEFGH'] = {'finished': True,
|
|
'error': None}
|
|
inspector_client.states['IJKLMNOP'] = {'finished': True,
|
|
'error': 'fake error'}
|
|
|
|
parsed_args = self.check_parser(self.cmd, [], [])
|
|
self.assertRaisesRegexp(exceptions.IntrospectionError,
|
|
'IJKLMNOP: fake error',
|
|
self.cmd.take_action, parsed_args)
|
|
|
|
self.assertEqual({'ABCDEFGH': 'available', 'IJKLMNOP': 'manageable'},
|
|
client.node.states)
|
|
self.assertEqual(['ABCDEFGH', 'IJKLMNOP'],
|
|
inspector_client.on_introspection)
|
|
|
|
def test_introspect_bulk(self):
|
|
client = self.app.client_manager.baremetal
|
|
client.node = fakes.FakeBaremetalNodeClient(
|
|
states={
|
|
"ABC": "available",
|
|
"DEF": "enroll",
|
|
"GHI": "manageable",
|
|
"JKL": "clean_wait"
|
|
},
|
|
transitions={
|
|
("ABC", "manage"): "manageable",
|
|
("DEF", "manage"): "manageable",
|
|
("ABC", "provide"): "available",
|
|
("DEF", "provide"): "available",
|
|
("GHI", "provide"): "available"
|
|
}
|
|
)
|
|
inspector_client = self.app.client_manager.baremetal_introspection
|
|
for uuid in ('ABC', 'DEF', 'GHI'):
|
|
inspector_client.states[uuid] = {'finished': True, 'error': None}
|
|
|
|
parsed_args = self.check_parser(self.cmd, [], [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
# The nodes that are available are set to "manageable" state.
|
|
# Then all manageable nodes are set to "available".
|
|
self.assertEqual(client.node.updates, [
|
|
('ABC', 'manage'),
|
|
('DEF', 'manage'),
|
|
('ABC', 'provide'),
|
|
('DEF', 'provide'),
|
|
('GHI', 'provide')
|
|
])
|
|
|
|
# Nodes which start in "enroll", "available" or "manageable" states are
|
|
# introspected:
|
|
self.assertEqual(['ABC', 'DEF', 'GHI'],
|
|
sorted(inspector_client.on_introspection))
|
|
|
|
def test_introspect_bulk_timeout(self):
|
|
client = self.app.client_manager.baremetal
|
|
client.node = fakes.FakeBaremetalNodeClient(
|
|
states={
|
|
"ABC": "available",
|
|
"DEF": "enroll",
|
|
},
|
|
transitions={
|
|
("ABC", "manage"): "available", # transition times out
|
|
("DEF", "manage"): "manageable",
|
|
("DEF", "provide"): "available"
|
|
}
|
|
)
|
|
inspector_client = self.app.client_manager.baremetal_introspection
|
|
inspector_client.states['ABC'] = {'finished': False, 'error': None}
|
|
inspector_client.states['DEF'] = {'finished': True, 'error': None}
|
|
log_fixture = self.useFixture(fixtures.FakeLogger())
|
|
|
|
parsed_args = self.check_parser(self.cmd, [], [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertIn("FAIL: Timeout waiting for Node ABC", log_fixture.output)
|
|
# Nodes that were successfully introspected are made available
|
|
self.assertEqual(
|
|
[("ABC", "manage"), ("DEF", "manage"), ("DEF", "provide")],
|
|
client.node.updates)
|
|
|
|
def test_introspect_bulk_transition_fails(self):
|
|
client = self.app.client_manager.baremetal
|
|
client.node = fakes.FakeBaremetalNodeClient(
|
|
states={
|
|
"ABC": "available",
|
|
"DEF": "enroll",
|
|
},
|
|
transitions={
|
|
("ABC", "manage"): "manageable",
|
|
("DEF", "manage"): "enroll", # state transition fails
|
|
("ABC", "provide"): "available"
|
|
},
|
|
transition_errors={
|
|
("DEF", "manage"): "power credential verification failed"
|
|
}
|
|
)
|
|
inspector_client = self.app.client_manager.baremetal_introspection
|
|
for uuid in ('ABC', 'DEF'):
|
|
inspector_client.states[uuid] = {'finished': True, 'error': None}
|
|
log_fixture = self.useFixture(fixtures.FakeLogger())
|
|
|
|
parsed_args = self.check_parser(self.cmd, [], [])
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
self.assertIn("FAIL: State transition failed for Node DEF",
|
|
log_fixture.output)
|
|
# Nodes that were successfully introspected are made available
|
|
self.assertEqual(
|
|
[("ABC", "manage"), ("DEF", "manage"), ("ABC", "provide")],
|
|
client.node.updates)
|
|
|
|
|
|
class TestStatusBaremetalIntrospectionBulk(fakes.TestBaremetal):
|
|
|
|
def setUp(self):
|
|
super(TestStatusBaremetalIntrospectionBulk, self).setUp()
|
|
|
|
# Get the command object to test
|
|
self.cmd = baremetal.StatusBaremetalIntrospectionBulk(self.app, None)
|
|
|
|
def test_status_bulk_one(self):
|
|
client = self.app.client_manager.baremetal
|
|
client.node.list.return_value = [
|
|
mock.Mock(uuid="ABCDEFGH")
|
|
]
|
|
inspector_client = self.app.client_manager.baremetal_introspection
|
|
inspector_client.states['ABCDEFGH'] = {'finished': False,
|
|
'error': None}
|
|
|
|
parsed_args = self.check_parser(self.cmd, [], [])
|
|
result = self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(result, (
|
|
('Node UUID', 'Finished', 'Error'),
|
|
[('ABCDEFGH', False, None)]))
|
|
|
|
def test_status_bulk(self):
|
|
client = self.app.client_manager.baremetal
|
|
client.node.list.return_value = [
|
|
mock.Mock(uuid="ABCDEFGH"),
|
|
mock.Mock(uuid="IJKLMNOP"),
|
|
mock.Mock(uuid="QRSTUVWX"),
|
|
]
|
|
inspector_client = self.app.client_manager.baremetal_introspection
|
|
for node in client.node.list.return_value:
|
|
inspector_client.states[node.uuid] = {'finished': False,
|
|
'error': None}
|
|
|
|
parsed_args = self.check_parser(self.cmd, [], [])
|
|
result = self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual(result, (
|
|
('Node UUID', 'Finished', 'Error'),
|
|
[
|
|
('ABCDEFGH', False, None),
|
|
('IJKLMNOP', False, None),
|
|
('QRSTUVWX', False, None)
|
|
]
|
|
))
|
|
|
|
|
|
class TestConfigureReadyState(fakes.TestBaremetal):
|
|
|
|
def setUp(self):
|
|
super(TestConfigureReadyState, self).setUp()
|
|
self.cmd = baremetal.ConfigureReadyState(self.app, None)
|
|
self.node = mock.Mock(uuid='foo')
|
|
self.ready_state_data = """{
|
|
"compute" :{
|
|
"bios_settings": {"ProcVirtualization": "Enabled"}
|
|
},
|
|
"storage" :{
|
|
"bios_settings": {"ProcVirtualization": "Disabled"}
|
|
}
|
|
}
|
|
"""
|
|
self.ready_state_config = {
|
|
"compute": {
|
|
"bios_settings": {"ProcVirtualization": "Enabled"}
|
|
},
|
|
"storage": {
|
|
"bios_settings": {"ProcVirtualization": "Disabled"},
|
|
}
|
|
}
|
|
|
|
@mock.patch('tripleoclient.utils.node_get_capabilities')
|
|
@mock.patch('tripleoclient.v1.baremetal.ConfigureReadyState.'
|
|
'_apply_changes')
|
|
@mock.patch('tripleoclient.v1.baremetal.ConfigureReadyState.'
|
|
'_configure_bios')
|
|
@mock.patch('tripleoclient.v1.baremetal.ConfigureReadyState.'
|
|
'_change_power_state')
|
|
def test_configure_ready_state(
|
|
self, mock_change_power_state, mock_configure_bios,
|
|
mock_apply_changes, mock_node_get_capabilities):
|
|
|
|
nodes = [mock.Mock(uuid='foo', driver='drac'),
|
|
mock.Mock(uuid='bar', driver='ilo'),
|
|
mock.Mock(uuid='baz', driver='drac')]
|
|
drac_nodes = [node for node in nodes if 'drac' in node.driver]
|
|
drac_nodes_with_profiles = [(drac_nodes[0], 'compute'),
|
|
(drac_nodes[1], 'storage')]
|
|
|
|
bm_client = self.app.client_manager.baremetal
|
|
bm_client.node.list.return_value = nodes
|
|
|
|
mock_node_get_capabilities.side_effect = [
|
|
{'profile': 'compute'}, {'profile': 'storage'}]
|
|
mock_configure_bios.return_value = set([nodes[0]])
|
|
|
|
arglist = ['ready-state.json']
|
|
verifylist = [('file', 'ready-state.json')]
|
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
|
|
with mock.patch('six.moves.builtins.open',
|
|
mock.mock_open(read_data=self.ready_state_data)):
|
|
self.cmd.take_action(parsed_args)
|
|
|
|
mock_node_get_capabilities.assert_has_calls(
|
|
[mock.call(nodes[0]), mock.call(nodes[2])])
|
|
mock_configure_bios.assert_called_once_with(drac_nodes_with_profiles)
|
|
mock_apply_changes.assert_has_calls([
|
|
# configure BIOS
|
|
mock.call(set([nodes[0]]))])
|
|
mock_change_power_state.assert_called_once_with(drac_nodes, 'off')
|
|
|
|
@mock.patch.object(baremetal.ConfigureReadyState, 'sleep_time',
|
|
new_callable=mock.PropertyMock,
|
|
return_value=0)
|
|
def test__configure_bios(self, mock_sleep_time):
|
|
nodes = [(self.node, 'compute')]
|
|
bm_client = self.app.client_manager.baremetal
|
|
self.cmd.bm_client = bm_client
|
|
self.cmd.ready_state_config = self.ready_state_config
|
|
|
|
self.cmd._configure_bios(nodes)
|
|
|
|
bm_client.node.vendor_passthru.assert_has_calls([
|
|
mock.call('foo', 'set_bios_config',
|
|
args={'ProcVirtualization': 'Enabled'},
|
|
http_method='POST'),
|
|
mock.call('foo', 'commit_bios_config', http_method='POST')])
|
|
|
|
@mock.patch.object(baremetal.ConfigureReadyState, 'sleep_time',
|
|
new_callable=mock.PropertyMock,
|
|
return_value=0)
|
|
def test__wait_for_drac_config_jobs(self, mock_sleep_time):
|
|
nodes = [self.node]
|
|
bm_client = self.app.client_manager.baremetal
|
|
bm_client.node.vendor_passthru.side_effect = [
|
|
mock.Mock(unfinished_jobs={'percent_complete': '34',
|
|
'id': 'JID_343938731947',
|
|
'name': 'ConfigBIOS:BIOS.Setup.1-1'}),
|
|
mock.Mock(unfinished_jobs={}),
|
|
]
|
|
self.cmd.bm_client = bm_client
|
|
|
|
self.cmd._wait_for_drac_config_jobs(nodes)
|
|
|
|
self.assertEqual(2, bm_client.node.vendor_passthru.call_count)
|
|
bm_client.node.vendor_passthru.assert_has_calls(
|
|
[mock.call('foo', 'list_unfinished_jobs', http_method='GET')]
|
|
)
|
|
|
|
@mock.patch.object(baremetal.ConfigureReadyState, 'sleep_time',
|
|
new_callable=mock.PropertyMock,
|
|
return_value=0)
|
|
def test__wait_for_drac_config_jobs_times_out(self, mock_sleep_time):
|
|
nodes = [self.node]
|
|
bm_client = self.app.client_manager.baremetal
|
|
bm_client.node.vendor_passthru.return_value = mock.Mock(
|
|
unfinished_jobs={'percent_complete': '34',
|
|
'id': 'JID_343938731947',
|
|
'name': 'ConfigBIOS:BIOS.Setup.1-1'})
|
|
self.cmd.bm_client = bm_client
|
|
|
|
self.assertRaises(exceptions.Timeout,
|
|
self.cmd._wait_for_drac_config_jobs,
|
|
nodes)
|
|
|
|
def test__change_power_state(self):
|
|
nodes = [self.node]
|
|
bm_client = self.app.client_manager.baremetal
|
|
self.cmd.bm_client = bm_client
|
|
|
|
self.cmd._change_power_state(nodes, 'reboot')
|
|
|
|
bm_client.node.set_power_state.assert_called_once_with('foo', 'reboot')
|
|
|
|
@mock.patch('tripleoclient.v1.baremetal.ConfigureReadyState.'
|
|
'_change_power_state')
|
|
@mock.patch('tripleoclient.v1.baremetal.ConfigureReadyState.'
|
|
'_wait_for_drac_config_jobs')
|
|
def test__apply_changes(self, mock_wait_for_drac_config_jobs,
|
|
mock_change_power_state):
|
|
nodes = [self.node]
|
|
bm_client = self.app.client_manager.baremetal
|
|
self.cmd.bm_client = bm_client
|
|
|
|
self.cmd._apply_changes(nodes)
|
|
|
|
mock_change_power_state.assert_called_once_with(nodes, 'reboot')
|
|
mock_wait_for_drac_config_jobs.assert_called_once_with(nodes)
|
|
|
|
|
|
class TestConfigureBaremetalBoot(fakes.TestBaremetal):
|
|
|
|
def setUp(self):
|
|
super(TestConfigureBaremetalBoot, self).setUp()
|
|
|
|
# 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):
|
|
|
|
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"),
|
|
]
|
|
|
|
bm_client.node.get.side_effect = [
|
|
mock.Mock(uuid="ABCDEFGH", properties={}),
|
|
mock.Mock(uuid="IJKLMNOP", properties={}),
|
|
]
|
|
|
|
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')
|
|
])
|
|
|
|
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'
|
|
}])
|
|
])
|
|
|
|
@mock.patch('openstackclient.common.utils.find_resource', autospec=True)
|
|
def test_configure_boot_with_suffix(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"),
|
|
]
|
|
|
|
bm_client.node.get.side_effect = [
|
|
mock.Mock(uuid="ABCDEFGH", properties={}),
|
|
mock.Mock(uuid="IJKLMNOP", properties={}),
|
|
]
|
|
|
|
arglist = ['--deploy-kernel', 'bm-deploy-kernel_20150101T100620',
|
|
'--deploy-ramdisk', 'bm-deploy-ramdisk_20150101T100620']
|
|
verifylist = [('deploy_kernel', 'bm-deploy-kernel_20150101T100620'),
|
|
('deploy_ramdisk', 'bm-deploy-ramdisk_20150101T100620')]
|
|
|
|
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.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'
|
|
}])
|
|
])
|
|
|
|
@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")
|
|
|
|
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)
|
|
|
|
@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)
|
|
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 = [
|
|
mock.Mock(uuid="ABCDEFGH", maintenance=False),
|
|
]
|
|
|
|
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(
|
|
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']
|
|
verifylist = [('root_device', 'smallest'),
|
|
('overwrite_root_device_hints', True)]
|
|
|
|
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_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)
|
|
|
|
|
|
class TestShowNodeCapabilities(fakes.TestBaremetal):
|
|
|
|
def setUp(self):
|
|
super(TestShowNodeCapabilities, self).setUp()
|
|
|
|
# Get the command object to test
|
|
self.cmd = baremetal.ShowNodeCapabilities(self.app, None)
|
|
|
|
def test_success(self):
|
|
|
|
bm_client = self.app.client_manager.baremetal
|
|
|
|
bm_client.node.list.return_value = [
|
|
mock.Mock(uuid='UUID1'),
|
|
mock.Mock(uuid='UUID2'),
|
|
]
|
|
|
|
bm_client.node.get.return_value = mock.Mock(
|
|
properties={'capabilities': 'boot_option:local'})
|
|
|
|
arglist = []
|
|
parsed_args = self.check_parser(self.cmd, arglist, [])
|
|
result = self.cmd.take_action(parsed_args)
|
|
|
|
self.assertEqual((
|
|
('Node UUID', 'Node Capabilities'),
|
|
[('UUID1', 'boot_option:local'), ('UUID2', 'boot_option:local')]
|
|
), result)
|