Ilo drivers sets capabilities:boot_mode in node

Ilo drivers sets capabilities:boot_mode in node properties
if there is none.
For a node capable of booting in both bios and uefi, setting
of this property by driver prevents it from getting selected for
future deploys requests for other boot mode.
It would be selected only for deploy requests where flavor
extra_specs specifies capabilities:boot_mode with a value
matching with one set by driver in the node properties.

Closes-Bug: 1418327

Change-Id: I90571487840c6a74699a8fbfbac8035c3efb5672
This commit is contained in:
Shivanand Tendulker 2015-02-11 19:38:02 -08:00
parent 7571df1366
commit d5bf6233f9
11 changed files with 158 additions and 71 deletions

View File

@ -191,7 +191,7 @@ def create_pxe_config(task, pxe_options, template=None):
pxe_config = _build_pxe_config(pxe_options, template)
utils.write_to_file(pxe_config_file_path, pxe_config)
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
_link_ip_address_pxe_configs(task)
else:
_link_mac_pxe_configs(task)
@ -205,7 +205,7 @@ def clean_up_pxe_config(task):
"""
LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
api = dhcp_factory.DHCPFactory().provider
ip_addresses = api.get_ip_addresses(task)
if not ip_addresses:
@ -252,7 +252,7 @@ def dhcp_options_for_instance(task):
dhcp_opts.append({'opt_name': 'bootfile-name',
'opt_value': ipxe_script_url})
else:
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
boot_file = CONF.pxe.uefi_pxe_bootfile_name
else:
boot_file = CONF.pxe.pxe_bootfile_name

View File

@ -968,8 +968,7 @@ def try_set_boot_device(task, device, persistent=True):
manager_utils.node_set_boot_device(task, device,
persistent=persistent)
except exception.IPMIFailure:
if driver_utils.get_node_capability(task.node,
'boot_mode') == 'uefi':
if driver_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
LOG.warning(_LW("ipmitool is unable to set boot device while "
"the node %s is in UEFI boot mode. Please set "
"the boot device manually.") % task.node.uuid)

View File

@ -346,42 +346,58 @@ def set_boot_mode(node, boot_mode):
def update_boot_mode(task):
"""Update 'boot_mode' capability value of node's 'capabilities' property.
"""Update instance_info with boot mode to be used for deploy.
This method updates the 'boot_mode' capability in node's 'capabilities'
property if not set.
It also sets the boot mode to be used in the next boot.
This method updates instance_info with boot mode to be used for
deploy if node properties['capabilities'] do not have boot_mode.
It sets the boot mode on the node.
:param task: Task object.
:raises: IloOperationError if setting boot mode failed.
"""
node = task.node
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
node = task.node
boot_mode = driver_utils.get_boot_mode_for_deploy(node)
if boot_mode is not None:
LOG.debug("Node %(uuid)s boot mode is being set to %(boot_mode)s",
{'uuid': node.uuid, 'boot_mode': boot_mode})
set_boot_mode(node, boot_mode)
set_boot_mode(node, boot_mode.lower())
return
ilo_object = get_ilo_object(task.node)
LOG.debug("Check pending boot mode for node %s.", node.uuid)
ilo_object = get_ilo_object(node)
try:
p_boot_mode = ilo_object.get_pending_boot_mode()
if p_boot_mode == 'UNKNOWN':
# NOTE(faizan) ILO will return this in remote cases and mostly on
# the nodes which supports UEFI. Such nodes mostly comes with UEFI
# as default boot mode. So we will try setting bootmode to UEFI
# and if it fails then we fall back to BIOS boot mode.
ilo_object.set_pending_boot_mode('UEFI')
p_boot_mode = 'UEFI'
boot_mode = ilo_object.get_pending_boot_mode()
except ilo_error.IloCommandNotSupportedError:
p_boot_mode = DEFAULT_BOOT_MODE
boot_mode = 'legacy'
driver_utils.rm_node_capability(task, 'boot_mode')
if boot_mode != 'UNKNOWN':
boot_mode = BOOT_MODE_ILO_TO_GENERIC[boot_mode.lower()]
driver_utils.add_node_capability(task, 'boot_mode',
BOOT_MODE_ILO_TO_GENERIC[p_boot_mode.lower()])
if boot_mode == 'UNKNOWN':
# NOTE(faizan) ILO will return this in remote cases and mostly on
# the nodes which supports UEFI. Such nodes mostly comes with UEFI
# as default boot mode. So we will try setting bootmode to UEFI
# and if it fails then we fall back to BIOS boot mode.
try:
boot_mode = 'uefi'
ilo_object.set_pending_boot_mode(
BOOT_MODE_GENERIC_TO_ILO[boot_mode].upper())
except ilo_error.IloError as ilo_exception:
operation = _("Setting %s as boot mode") % boot_mode
raise exception.IloOperationError(operation=operation,
error=ilo_exception)
LOG.debug("Node %(uuid)s boot mode is being set to %(boot_mode)s "
"as pending boot mode is unknown.",
{'uuid': node.uuid, 'boot_mode': boot_mode})
instance_info = node.instance_info
instance_info['deploy_boot_mode'] = boot_mode
node.instance_info = instance_info
node.save()
def setup_vmedia_for_boot(task, boot_iso, parameters=None):

View File

@ -160,7 +160,7 @@ def _get_boot_iso(task, root_uuid):
# Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift
# and provide its name.
deploy_iso_uuid = deploy_info['ilo_deploy_iso']
boot_mode = driver_utils.get_node_capability(task.node, 'boot_mode')
boot_mode = driver_utils.get_boot_mode_for_deploy(task.node)
boot_iso_object_name = _get_boot_iso_object_name(task.node)
kernel_params = CONF.pxe.pxe_append_params
container = CONF.ilo.swift_ilo_container

View File

@ -416,7 +416,7 @@ def _get_boot_mode(node):
:param node: A single Node.
:returns: A string representing the boot mode type. Defaults to 'bios'.
"""
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
boot_mode = driver_utils.get_boot_mode_for_deploy(node)
if boot_mode:
return boot_mode.lower()
return "bios"

View File

@ -327,7 +327,7 @@ class PXEDeploy(base.DeployInterface):
driver_utils.validate_boot_mode_capability(node)
driver_utils.validate_boot_option_capability(node)
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
boot_mode = driver_utils.get_boot_mode_for_deploy(task.node)
if CONF.pxe.ipxe_enabled:
if not CONF.pxe.http_url or not CONF.pxe.http_root:
@ -417,7 +417,7 @@ class PXEDeploy(base.DeployInterface):
pxe_options = _build_pxe_config_options(task.node, pxe_info,
task.context)
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
pxe_config_template = CONF.pxe.uefi_pxe_config_template
else:
pxe_config_template = CONF.pxe.pxe_config_template
@ -455,7 +455,7 @@ class PXEDeploy(base.DeployInterface):
task.node.uuid)
deploy_utils.switch_pxe_config(
pxe_config_path, root_uuid_or_disk_id,
driver_utils.get_node_capability(task.node, 'boot_mode'),
driver_utils.get_boot_mode_for_deploy(task.node),
iwdi)
def clean_up(self, task):
@ -561,10 +561,10 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
pxe_utils.clean_up_pxe_config(task)
else:
pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
node_cap = driver_utils.get_node_capability(node, 'boot_mode')
boot_mode = driver_utils.get_boot_mode_for_deploy(node)
deploy_utils.switch_pxe_config(pxe_config_path,
root_uuid_or_disk_id,
node_cap, is_whole_disk_image)
boot_mode, is_whole_disk_image)
deploy_utils.notify_deploy_complete(kwargs['address'])
LOG.info(_LI('Deployment to node %s done'), node.uuid)
@ -617,7 +617,7 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
root_uuid_or_disk_id = uuid_dict.get(
'root uuid', uuid_dict.get('disk identifier'))
pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
boot_mode = driver_utils.get_boot_mode_for_deploy(node)
deploy_utils.switch_pxe_config(pxe_config_path,
root_uuid_or_disk_id,
boot_mode, is_whole_disk_image)

View File

@ -250,3 +250,28 @@ def validate_secure_boot_capability(node):
"""
validate_capability(node, 'secure_boot', ('true', 'false'))
def get_boot_mode_for_deploy(node):
"""Returns the boot mode that would be used for deploy.
This method returns deploy_boot_mode available in node field
boot_mode from 'capabilities' of node 'properties'.
Otherwise returns boot mode specified in node's 'instance_info'.
:param node: an ironic node object.
:returns: Value of boot mode that would be used for deploy.
Possible values are 'bios', 'uefi'.
It would return None if boot mode is present neither
in 'capabilities' of node 'properties' nor in node's
'instance_info'.
"""
boot_mode = get_node_capability(node, 'boot_mode')
if boot_mode is None:
instance_info = node.instance_info
boot_mode = instance_info.get('deploy_boot_mode', None)
LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.',
{'boot_mode': boot_mode, 'node': node.uuid})
return boot_mode

View File

@ -27,7 +27,6 @@ from ironic.common import swift
from ironic.common import utils
from ironic.conductor import task_manager
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers import utils as driver_utils
from ironic.tests.conductor import utils as mgr_utils
from ironic.tests.db import base as db_base
from ironic.tests.db import utils as db_utils
@ -300,42 +299,39 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
get_pending_boot_mode_mock.assert_called_once_with()
@mock.patch.object(ilo_common, 'set_boot_mode')
@mock.patch.object(driver_utils, 'get_node_capability')
def test_update_boot_mode_avbl(self,
node_capability_mock,
set_boot_mode_mock):
node_capability_mock.return_value = 'uefi'
def test_update_boot_mode_instance_info_exists(self,
set_boot_mode_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.instance_info['deploy_boot_mode'] = 'bios'
ilo_common.update_boot_mode(task)
node_capability_mock.assert_called_once_with(task.node,
'boot_mode')
set_boot_mode_mock.assert_called_once_with(task.node, 'uefi')
set_boot_mode_mock.assert_called_once_with(task.node, 'bios')
@mock.patch.object(ilo_common, 'set_boot_mode')
def test_update_boot_mode_capabilities_exist(self,
set_boot_mode_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'boot_mode:bios'
ilo_common.update_boot_mode(task)
set_boot_mode_mock.assert_called_once_with(task.node, 'bios')
@mock.patch.object(driver_utils, 'rm_node_capability')
@mock.patch.object(driver_utils, 'add_node_capability')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_update_boot_mode(self, get_ilo_object_mock,
add_node_capability_mock,
rm_node_capability_mock):
def test_update_boot_mode(self, get_ilo_object_mock):
ilo_mock_obj = get_ilo_object_mock.return_value
ilo_mock_obj.get_pending_boot_mode.return_value = 'legacy'
ilo_mock_obj.get_pending_boot_mode.return_value = 'LEGACY'
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
ilo_common.update_boot_mode(task)
get_ilo_object_mock.assert_called_once_with(task.node)
ilo_mock_obj.get_pending_boot_mode.assert_called_once_with()
rm_node_capability_mock.assert_called_once_with(task, 'boot_mode')
add_node_capability_mock.assert_called_once_with(task,
'boot_mode',
'bios')
self.assertEqual('bios',
task.node.instance_info['deploy_boot_mode'])
@mock.patch.object(driver_utils, 'add_node_capability')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_update_boot_mode_unknown(self,
get_ilo_object_mock,
add_node_capability_mock):
get_ilo_object_mock):
ilo_mock_obj = get_ilo_object_mock.return_value
ilo_mock_obj.get_pending_boot_mode.return_value = 'UNKNOWN'
set_pending_boot_mode_mock = ilo_mock_obj.set_pending_boot_mode
@ -346,15 +342,28 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
get_ilo_object_mock.assert_called_once_with(task.node)
ilo_mock_obj.get_pending_boot_mode.assert_called_once_with()
set_pending_boot_mode_mock.assert_called_once_with('UEFI')
add_node_capability_mock.assert_called_once_with(task,
'boot_mode',
'uefi')
self.assertEqual('uefi',
task.node.instance_info['deploy_boot_mode'])
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_update_boot_mode_unknown_except(self,
get_ilo_object_mock):
ilo_mock_obj = get_ilo_object_mock.return_value
ilo_mock_obj.get_pending_boot_mode.return_value = 'UNKNOWN'
set_pending_boot_mode_mock = ilo_mock_obj.set_pending_boot_mode
exc = ilo_error.IloError('error')
set_pending_boot_mode_mock.side_effect = exc
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaises(exception.IloOperationError,
ilo_common.update_boot_mode, task)
get_ilo_object_mock.assert_called_once_with(task.node)
ilo_mock_obj.get_pending_boot_mode.assert_called_once_with()
@mock.patch.object(driver_utils, 'add_node_capability')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_update_boot_mode_legacy(self,
get_ilo_object_mock,
add_node_capability_mock):
get_ilo_object_mock):
ilo_mock_obj = get_ilo_object_mock.return_value
exc = ilo_error.IloCommandNotSupportedError('error')
ilo_mock_obj.get_pending_boot_mode.side_effect = exc
@ -364,9 +373,8 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
ilo_common.update_boot_mode(task)
get_ilo_object_mock.assert_called_once_with(task.node)
ilo_mock_obj.get_pending_boot_mode.assert_called_once_with()
add_node_capability_mock.assert_called_once_with(task,
'boot_mode',
'bios')
self.assertEqual('bios',
task.node.instance_info['deploy_boot_mode'])
@mock.patch.object(ilo_common, 'set_boot_mode')
def test_update_boot_mode_prop_boot_mode_exist(self,

View File

@ -124,26 +124,29 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
boot_iso_expected = 'boot-iso-uuid'
self.assertEqual(boot_iso_expected, boot_iso_actual)
@mock.patch.object(driver_utils, 'get_node_capability')
@mock.patch.object(driver_utils, 'get_boot_mode_for_deploy')
@mock.patch.object(images, 'get_image_properties')
@mock.patch.object(ilo_deploy, '_parse_deploy_info')
def test__get_boot_iso_uefi_no_glance_image(self, deploy_info_mock,
image_props_mock, get_node_cap_mock):
def test__get_boot_iso_uefi_no_glance_image(self,
deploy_info_mock,
image_props_mock,
boot_mode_mock):
deploy_info_mock.return_value = {'image_source': 'image-uuid',
'ilo_deploy_iso': 'deploy_iso_uuid'}
image_props_mock.return_value = {'boot_iso': None,
'kernel_id': None,
'ramdisk_id': None}
get_node_cap_mock.return_value = 'uefi'
properties = {'capabilities': 'boot_mode:uefi'}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties = properties
boot_iso_result = ilo_deploy._get_boot_iso(task, 'root-uuid')
deploy_info_mock.assert_called_once_with(task.node)
image_props_mock.assert_called_once_with(
task.context, 'image-uuid',
['boot_iso', 'kernel_id', 'ramdisk_id'])
self.assertFalse(get_node_cap_mock.called)
self.assertFalse(boot_mode_mock.called)
self.assertIsNone(boot_iso_result)
@mock.patch.object(tempfile, 'NamedTemporaryFile')
@ -1006,6 +1009,7 @@ class IloPXEDeployTestCase(db_base.DbTestCase):
pxe_prepare_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
task.driver.deploy.prepare(task)
update_boot_mode_mock.assert_called_once_with(task)
pxe_prepare_mock.assert_called_once_with(task)

View File

@ -191,3 +191,17 @@ class UtilsTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
driver_utils.validate_secure_boot_capability,
self.node)
def test_get_boot_mode_for_deploy_using_capabilities(self):
properties = {'capabilities': 'boot_mode:uefi,cap2:value2'}
self.node.properties = properties
result = driver_utils.get_boot_mode_for_deploy(self.node)
self.assertEqual('uefi', result)
def test_get_boot_mode_for_deploy_using_instance_info(self):
instance_info = {'deploy_boot_mode': 'uefi'}
self.node.instance_info = instance_info
result = driver_utils.get_boot_mode_for_deploy(self.node)
self.assertEqual('uefi', result)

View File

@ -308,6 +308,27 @@ class TestPXEUtils(db_base.DbTestCase):
task.node.properties = properties
pxe_utils.clean_up_pxe_config(task)
unlink_mock.assert_called_once_with('/tftpboot/0A0A0001.conf')
rmtree_mock.assert_called_once_with(
unlink_mock.assert_called_once_with('/tftpboot/0A0A0001.conf')
rmtree_mock.assert_called_once_with(
os.path.join(CONF.pxe.tftp_root, self.node.uuid))
@mock.patch('ironic.common.utils.rmtree_without_raise')
@mock.patch('ironic.common.utils.unlink_without_raise')
@mock.patch('ironic.common.dhcp_factory.DHCPFactory.provider')
def test_clean_up_pxe_config_uefi_instance_info(self,
provider_mock, unlink_mock,
rmtree_mock):
ip_address = '10.10.0.1'
address = "aa:aa:aa:aa:aa:aa"
object_utils.create_test_port(self.context, node_id=self.node.id,
address=address)
provider_mock.get_ip_addresses.return_value = [ip_address]
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.instance_info['deploy_boot_mode'] = 'uefi'
pxe_utils.clean_up_pxe_config(task)
unlink_mock.assert_called_once_with('/tftpboot/0A0A0001.conf')
rmtree_mock.assert_called_once_with(
os.path.join(CONF.pxe.tftp_root, self.node.uuid))