Add uefi boot mode support in IloVirtualMediaIscsiDeploy

The following functionality need to be added in iLO driver:
  - Add set_boot_mode function to iLO driver to switch between
    bios and uefi boot modes.
  - Validate boot_mode for IloVirtualMediaIscsiDeploy driver.

Change-Id: Ic8496a2d97aab6634b573c85d379b446c49e04b9
Implements: blueprint uefi-boot-for-ironic
This commit is contained in:
Faizan Barmawer 2014-08-25 17:52:36 +05:30
parent 20ef93cfd0
commit d9cff4a2d8
9 changed files with 383 additions and 82 deletions

View File

@ -22,7 +22,6 @@ from oslo.config import cfg
from ironic.common import dhcp_factory
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common.i18n import _LW
from ironic.common import utils
from ironic.drivers import utils as driver_utils
from ironic.openstack.common import fileutils
@ -191,7 +190,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 get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
_link_ip_address_pxe_configs(task)
else:
_link_mac_pxe_configs(task)
@ -205,7 +204,7 @@ def clean_up_pxe_config(task):
"""
LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)
if get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
api = dhcp_factory.DHCPFactory().provider
ip_addresses = api.get_ip_addresses(task)
if not ip_addresses:
@ -244,7 +243,7 @@ def dhcp_options_for_instance(task):
dhcp_opts.append({'opt_name': 'bootfile-name',
'opt_value': ipxe_script_url})
else:
if get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
boot_file = CONF.pxe.uefi_pxe_bootfile_name
else:
boot_file = CONF.pxe.pxe_bootfile_name
@ -257,42 +256,3 @@ def dhcp_options_for_instance(task):
dhcp_opts.append({'opt_name': 'tftp-server',
'opt_value': CONF.pxe.tftp_server})
return dhcp_opts
def get_node_capability(node, capability):
"""Returns 'capability' value from node's 'capabilities' property.
:param node: Node object.
:param capability: Capability key.
:return: Capability value.
If capability is not present, then return "None"
"""
capabilities = node.properties.get('capabilities')
if not capabilities:
return
for node_capability in str(capabilities).split(','):
parts = node_capability.split(':')
if len(parts) == 2 and parts[0] and parts[1]:
if parts[0] == capability:
return parts[1]
else:
LOG.warn(_LW("Ignoring malformed capability '%s'. "
"Format should be 'key:val'."), node_capability)
def validate_boot_mode_capability(node):
"""Validate the boot_mode capability set in node property.
:param node: an ironic node object.
:raises: InvalidParameterValue, if 'boot_mode' capability is set
other than 'bios' or 'uefi' or None.
"""
boot_mode = get_node_capability(node, 'boot_mode')
if boot_mode and boot_mode not in ['bios', 'uefi']:
raise exception.InvalidParameterValue(_("Invalid boot_mode "
"parameter '%s'.") % boot_mode)

View File

@ -28,6 +28,7 @@ from ironic.common.i18n import _LI
from ironic.common import images
from ironic.common import swift
from ironic.common import utils
from ironic.drivers import utils as driver_utils
from ironic.openstack.common import log as logging
ilo_client = importutils.try_import('proliantutils.ilo.ribcl')
@ -69,6 +70,11 @@ OPTIONAL_PROPERTIES = {
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
DEFAULT_BOOT_MODE = 'LEGACY'
BOOT_MODE_GENERIC_TO_ILO = {'bios': 'legacy', 'uefi': 'uefi'}
BOOT_MODE_ILO_TO_GENERIC = dict((v, k)
for (k, v) in BOOT_MODE_GENERIC_TO_ILO.items())
def parse_driver_info(node):
@ -274,6 +280,63 @@ def set_boot_device(node, device):
{'uuid': node.uuid, 'device': device})
def set_boot_mode(node, boot_mode):
"""Sets the node to boot using boot_mode for the next boot.
:param node: an ironic node object.
:param boot_mode: Next boot mode.
:raises: IloOperationError if setting boot mode failed.
"""
ilo_object = get_ilo_object(node)
try:
p_boot_mode = ilo_object.get_pending_boot_mode()
except ilo_client.IloCommandNotSupportedError:
p_boot_mode = DEFAULT_BOOT_MODE
if BOOT_MODE_ILO_TO_GENERIC[p_boot_mode.lower()] == boot_mode:
LOG.info(_LI("Node %(uuid)s pending boot mode is %(boot_mode)s."),
{'uuid': node.uuid, 'boot_mode': boot_mode})
return
try:
ilo_object.set_pending_boot_mode(
BOOT_MODE_GENERIC_TO_ILO[boot_mode].upper())
except ilo_client.IloError as ilo_exception:
operation = _("Setting %s as boot mode") % boot_mode
raise exception.IloOperationError(operation=operation,
error=ilo_exception)
LOG.info(_LI("Node %(uuid)s boot mode is set to %(boot_mode)s."),
{'uuid': node.uuid, 'boot_mode': boot_mode})
def update_boot_mode_capability(task):
"""Update 'boot_mode' capability value of node's 'capabilities' property.
:param task: Task object.
"""
ilo_object = get_ilo_object(task.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'
except ilo_client.IloCommandNotSupportedError:
p_boot_mode = DEFAULT_BOOT_MODE
driver_utils.rm_node_capability(task, 'boot_mode')
driver_utils.add_node_capability(task, 'boot_mode',
BOOT_MODE_ILO_TO_GENERIC[p_boot_mode.lower()])
def setup_vmedia_for_boot(task, boot_iso, parameters=None):
"""Sets up the node to boot from the given ISO image.

View File

@ -33,6 +33,7 @@ from ironic.drivers.modules import agent
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers import utils as driver_utils
from ironic.openstack.common import log as logging
LOG = logging.getLogger(__name__)
@ -92,6 +93,14 @@ def _get_boot_iso(task, root_uuid):
LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid)
return 'glance:%s' % boot_iso_uuid
# NOTE(faizan) For uefi boot_mode, operator should provide efi capable
# boot-iso in glance
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
LOG.error(_LE("Unable to find boot_iso in Glance, required to deploy "
"node %(node)s in UEFI boot mode."),
{'node': task.node.uuid})
return
kernel_uuid = images.get_glance_image_property(task.context,
image_uuid, 'kernel_id')
ramdisk_uuid = images.get_glance_image_property(task.context,
@ -238,6 +247,7 @@ class IloVirtualMediaIscsiDeploy(base.DeployInterface):
d_info = _parse_deploy_info(task.node)
iscsi_deploy.validate_glance_image_properties(task.context, d_info,
props)
driver_utils.validate_boot_mode_capability(task.node)
@task_manager.require_exclusive_lock
def deploy(self, task):
@ -288,8 +298,13 @@ class IloVirtualMediaIscsiDeploy(base.DeployInterface):
"""Prepare the deployment environment for this task's node.
:param task: a TaskManager instance containing the node to act on.
:raises: IloOperationError, if some operation on iLO failed.
"""
pass
boot_mode = driver_utils.get_node_capability(task.node, 'boot_mode')
if boot_mode is not None:
ilo_common.set_boot_mode(task.node, boot_mode)
else:
ilo_common.update_boot_mode_capability(task)
def clean_up(self, task):
"""Clean up the deployment environment for the task's node.

View File

@ -39,6 +39,7 @@ from ironic.drivers import base
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules import image_cache
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers import utils as driver_utils
from ironic.openstack.common import fileutils
from ironic.openstack.common import log as logging
@ -273,8 +274,9 @@ class PXEDeploy(base.DeployInterface):
:raises: InvalidParameterValue.
:raises: MissingParameterValue
"""
# Check the boot_mode capability parameter value.
pxe_utils.validate_boot_mode_capability(task.node)
driver_utils.validate_boot_mode_capability(task.node)
if CONF.pxe.ipxe_enabled:
if not CONF.pxe.http_url or not CONF.pxe.http_root:
@ -282,7 +284,8 @@ class PXEDeploy(base.DeployInterface):
"iPXE boot is enabled but no HTTP URL or HTTP "
"root was specified."))
# iPXE and UEFI should not be configured together.
if pxe_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_node_capability(task.node,
'boot_mode') == 'uefi':
LOG.error(_LE("UEFI boot mode is not supported with "
"iPXE boot enabled."))
raise exception.InvalidParameterValue(_(
@ -330,7 +333,8 @@ class PXEDeploy(base.DeployInterface):
try:
manager_utils.node_set_boot_device(task, 'pxe', persistent=True)
except exception.IPMIFailure:
if pxe_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_node_capability(task.node,
'boot_mode') == 'uefi':
LOG.warning(_LW("ipmitool is unable to set boot device while "
"the node is in UEFI boot mode."
"Please set the boot device manually."))
@ -373,7 +377,7 @@ class PXEDeploy(base.DeployInterface):
pxe_options = _build_pxe_config_options(task.node, pxe_info,
task.context)
if pxe_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
pxe_config_template = CONF.pxe.uefi_pxe_config_template
else:
pxe_config_template = CONF.pxe.pxe_config_template
@ -461,7 +465,7 @@ class VendorPassthru(base.VendorInterface):
try:
pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
deploy_utils.switch_pxe_config(pxe_config_path, root_uuid,
pxe_utils.get_node_capability(node, 'boot_mode'))
driver_utils.get_node_capability(node, 'boot_mode'))
deploy_utils.notify_deploy_complete(kwargs['address'])

View File

@ -14,7 +14,12 @@
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common.i18n import _LW
from ironic.drivers import base
from ironic.openstack.common import log as logging
LOG = logging.getLogger(__name__)
def _raise_unsupported_error(method=None):
@ -107,3 +112,92 @@ def get_node_mac_addresses(task):
:returns: A list of MAC addresses in the format xx:xx:xx:xx:xx:xx.
"""
return [p.address for p in task.ports]
def get_node_capability(node, capability):
"""Returns 'capability' value from node's 'capabilities' property.
:param node: Node object.
:param capability: Capability key.
:return: Capability value.
If capability is not present, then return "None"
"""
capabilities = node.properties.get('capabilities')
if not capabilities:
return
for node_capability in capabilities.split(','):
parts = node_capability.split(':')
if len(parts) == 2 and parts[0] and parts[1]:
if parts[0] == capability:
return parts[1]
else:
LOG.warn(_LW("Ignoring malformed capability '%s'. "
"Format should be 'key:val'."), node_capability)
def rm_node_capability(task, capability):
"""Remove 'capability' from node's 'capabilities' property.
:param task: Task object.
:param capability: Capability key.
"""
node = task.node
capabilities = node.properties.get('capabilities')
if not capabilities:
return
caps = []
for cap in capabilities.split(','):
parts = cap.split(':')
if len(parts) == 2 and parts[0] and parts[1]:
if parts[0] == capability:
continue
caps.append(cap)
new_cap_str = ",".join(caps)
node.properties['capabilities'] = new_cap_str if new_cap_str else None
node.save(task.context)
def add_node_capability(task, capability, value):
"""Add 'capability' to node's 'capabilities' property.
If 'capability' is already present, then a duplicate entry
will be added.
:param task: Task object.
:param capability: Capability key.
:param value: Capability value.
"""
node = task.node
capabilities = node.properties.get('capabilities')
new_cap = ':'.join([capability, value])
if capabilities:
capabilities = ','.join([capabilities, new_cap])
else:
capabilities = new_cap
node.properties['capabilities'] = capabilities
node.save(task.context)
def validate_boot_mode_capability(node):
"""Validate the boot_mode capability set in node property.
:param node: an ironic node object.
:raises: InvalidParameterValue, if 'boot_mode' capability is set
other than 'bios' or 'uefi' or None.
"""
boot_mode = get_node_capability(node, 'boot_mode')
if boot_mode and boot_mode not in ['bios', 'uefi']:
raise exception.InvalidParameterValue(_("Invalid boot_mode "
"parameter '%s'.") % boot_mode)

View File

@ -28,6 +28,7 @@ from ironic.common import utils
from ironic.conductor import task_manager
from ironic.db import api as dbapi
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers import utils as driver_utils
from ironic.openstack.common import context
from ironic.tests import base
from ironic.tests.conductor import utils as mgr_utils
@ -258,12 +259,111 @@ class IloCommonMethodsTestCase(base.TestCase):
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_set_boot_device(self, get_ilo_object_mock):
ilo_object_mock = mock.MagicMock()
get_ilo_object_mock.return_value = ilo_object_mock
ilo_object_mock = get_ilo_object_mock.return_value
ilo_common.set_boot_device(self.node, 'CDROM')
get_ilo_object_mock.assert_called_once_with(self.node)
ilo_object_mock.set_one_time_boot.assert_called_once_with('CDROM')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_set_boot_mode(self, get_ilo_object_mock):
ilo_object_mock = get_ilo_object_mock.return_value
get_pending_boot_mode_mock = ilo_object_mock.get_pending_boot_mode
set_pending_boot_mode_mock = ilo_object_mock.set_pending_boot_mode
get_pending_boot_mode_mock.return_value = 'LEGACY'
ilo_common.set_boot_mode(self.node, 'uefi')
get_ilo_object_mock.assert_called_once_with(self.node)
get_pending_boot_mode_mock.assert_called_once_with()
set_pending_boot_mode_mock.assert_called_once_with('UEFI')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_set_boot_mode_without_set_pending_boot_mode(self,
get_ilo_object_mock):
ilo_object_mock = get_ilo_object_mock.return_value
get_pending_boot_mode_mock = ilo_object_mock.get_pending_boot_mode
get_pending_boot_mode_mock.return_value = 'LEGACY'
ilo_common.set_boot_mode(self.node, 'bios')
get_ilo_object_mock.assert_called_once_with(self.node)
get_pending_boot_mode_mock.assert_called_once_with()
self.assertFalse(ilo_object_mock.set_pending_boot_mode.called)
@mock.patch.object(ilo_common, 'ilo_client')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_set_boot_mode_with_IloOperationError(self,
get_ilo_object_mock,
ilo_client_mock):
ilo_object_mock = get_ilo_object_mock.return_value
get_pending_boot_mode_mock = ilo_object_mock.get_pending_boot_mode
get_pending_boot_mode_mock.return_value = 'UEFI'
set_pending_boot_mode_mock = ilo_object_mock.set_pending_boot_mode
ilo_client_mock.IloError = Exception
set_pending_boot_mode_mock.side_effect = Exception
self.assertRaises(exception.IloOperationError,
ilo_common.set_boot_mode, self.node, 'bios')
get_ilo_object_mock.assert_called_once_with(self.node)
get_pending_boot_mode_mock.assert_called_once_with()
@mock.patch.object(driver_utils, 'rm_node_capability')
@mock.patch.object(driver_utils, 'add_node_capability')
@mock.patch.object(ilo_common, 'get_ilo_object')
@mock.patch.object(ilo_common, 'ilo_client')
def test_update_boot_mode_capability(self, ilo_client_mock,
get_ilo_object_mock,
add_node_capability_mock,
rm_node_capability_mock):
ilo_client_mock.IloCommandNotSupportedError = Exception
ilo_mock_obj = get_ilo_object_mock.return_value
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_capability(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')
@mock.patch.object(driver_utils, 'add_node_capability')
@mock.patch.object(ilo_common, 'get_ilo_object')
@mock.patch.object(ilo_common, 'ilo_client')
def test_update_boot_mode_capability_unknown(self, ilo_client_mock,
get_ilo_object_mock,
add_node_capability_mock):
ilo_client_mock.IloCommandNotSupportedError = Exception
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
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
ilo_common.update_boot_mode_capability(task)
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')
@mock.patch.object(driver_utils, 'add_node_capability')
@mock.patch.object(ilo_common, 'get_ilo_object')
@mock.patch.object(ilo_common, 'ilo_client')
def test_update_boot_mode_capability_legacy(self, ilo_client_mock,
get_ilo_object_mock,
add_node_capability_mock):
ilo_client_mock.IloCommandNotSupportedError = Exception
ilo_mock_obj = get_ilo_object_mock.return_value
ilo_mock_obj.get_pending_boot_mode.side_effect = Exception
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
ilo_common.update_boot_mode_capability(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')
@mock.patch.object(images, 'get_temp_url_for_glance_image')
@mock.patch.object(ilo_common, 'attach_vmedia')
@mock.patch.object(ilo_common, '_prepare_floppy_image')

View File

@ -32,6 +32,7 @@ from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules.ilo import deploy as ilo_deploy
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers import utils as driver_utils
from ironic.openstack.common import context
from ironic.openstack.common import importutils
from ironic.tests import base
@ -77,6 +78,24 @@ class IloDeployPrivateMethodsTestCase(base.TestCase):
boot_iso_expected = 'glance:boot-iso-uuid'
self.assertEqual(boot_iso_expected, boot_iso_actual)
@mock.patch.object(driver_utils, 'get_node_capability')
@mock.patch.object(images, 'get_glance_image_property')
@mock.patch.object(ilo_deploy, '_parse_deploy_info')
def test__get_boot_iso_uefi_no_glance_image(self, deploy_info_mock,
image_prop_mock, get_node_cap_mock):
deploy_info_mock.return_value = {'image_source': 'image-uuid'}
image_prop_mock.return_value = None
get_node_cap_mock.return_value = 'uefi'
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
boot_iso_result = ilo_deploy._get_boot_iso(task, 'root-uuid')
deploy_info_mock.assert_called_once_with(task.node)
image_prop_mock.assert_called_once_with(task.context, 'image-uuid',
'boot_iso')
get_node_cap_mock.assert_called_once_with(task.node, 'boot_mode')
self.assertIsNone(boot_iso_result)
@mock.patch.object(tempfile, 'NamedTemporaryFile')
@mock.patch.object(images, 'create_boot_iso')
@mock.patch.object(swift, 'SwiftAPI')
@ -185,11 +204,12 @@ class IloVirtualMediaIscsiDeployTestCase(base.TestCase):
self.node = obj_utils.create_test_node(self.context,
driver='iscsi_ilo', driver_info=INFO_DICT)
@mock.patch.object(driver_utils, 'validate_boot_mode_capability')
@mock.patch.object(iscsi_deploy, 'validate_glance_image_properties')
@mock.patch.object(ilo_deploy, '_parse_deploy_info')
@mock.patch.object(iscsi_deploy, 'validate')
def test_validate(self, validate_mock, deploy_info_mock,
validate_prop_mock):
validate_prop_mock, validate_boot_mode_mock):
d_info = {'a': 'b'}
deploy_info_mock.return_value = d_info
with task_manager.acquire(self.context, self.node.uuid,
@ -199,6 +219,7 @@ class IloVirtualMediaIscsiDeployTestCase(base.TestCase):
deploy_info_mock.assert_called_once_with(task.node)
validate_prop_mock.assert_called_once_with(task.context,
d_info, ['kernel_id', 'ramdisk_id'])
validate_boot_mode_mock.assert_called_once_with(task.node)
@mock.patch.object(ilo_deploy, '_reboot_into')
@mock.patch.object(ilo_deploy, '_get_single_nic_with_vif_port_id')

View File

@ -129,3 +129,77 @@ class UtilsTestCase(base.TestCase):
with task_manager.acquire(self.context, self.node.uuid) as task:
node_macs = driver_utils.get_node_mac_addresses(task)
self.assertEqual(sorted([p.address for p in ports]), sorted(node_macs))
def test_get_node_capability(self):
properties = {'capabilities': 'cap1:value1,cap2:value2'}
self.node.properties = properties
expected = 'value1'
result = driver_utils.get_node_capability(self.node, 'cap1')
self.assertEqual(expected, result)
def test_get_node_capability_returns_none(self):
properties = {'capabilities': 'cap1:value1,cap2:value2'}
self.node.properties = properties
result = driver_utils.get_node_capability(self.node, 'capX')
self.assertIsNone(result)
def test_add_node_capability(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = ''
driver_utils.add_node_capability(task, 'boot_mode', 'bios')
self.assertEqual('boot_mode:bios',
task.node.properties['capabilities'])
def test_add_node_capability_append(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'a:b,c:d'
driver_utils.add_node_capability(task, 'boot_mode', 'bios')
self.assertEqual('a:b,c:d,boot_mode:bios',
task.node.properties['capabilities'])
def test_add_node_capability_append_duplicate(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'a:b,c:d'
driver_utils.add_node_capability(task, 'a', 'b')
self.assertEqual('a:b,c:d,a:b',
task.node.properties['capabilities'])
def test_rm_node_capability(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'a:b'
driver_utils.rm_node_capability(task, 'a')
self.assertIsNone(task.node.properties['capabilities'])
def test_rm_node_capability_exists(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'a:b,c:d,x:y'
self.assertIsNone(driver_utils.rm_node_capability(task, 'c'))
self.assertEqual('a:b,x:y', task.node.properties['capabilities'])
def test_rm_node_capability_non_existent(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'a:b'
self.assertIsNone(driver_utils.rm_node_capability(task, 'x'))
self.assertEqual('a:b', task.node.properties['capabilities'])
def test_validate_boot_mode_capability(self):
properties = {'capabilities': 'boot_mode:uefi,cap2:value2'}
self.node.properties = properties
result = driver_utils.validate_boot_mode_capability(self.node)
self.assertIsNone(result)
def test_validate_boot_mode_capability_with_exception(self):
properties = {'capabilities': 'boot_mode:foo,cap2:value2'}
self.node.properties = properties
self.assertRaises(exception.InvalidParameterValue,
driver_utils.validate_boot_mode_capability, self.node)

View File

@ -19,7 +19,6 @@ import os
import mock
from oslo.config import cfg
from ironic.common import exception
from ironic.common import pxe_utils
from ironic.conductor import task_manager
from ironic.db import api as dbapi
@ -275,35 +274,6 @@ class TestPXEUtils(db_base.DbTestCase):
self.assertEqual(sorted(expected_info),
sorted(pxe_utils.dhcp_options_for_instance(task)))
def test_get_node_capability(self):
properties = {'capabilities': 'cap1:value1,cap2:value2'}
self.node.properties = properties
expected = 'value1'
result = pxe_utils.get_node_capability(self.node, 'cap1')
self.assertEqual(expected, result)
def test_get_node_capability_returns_none(self):
properties = {'capabilities': 'cap1:value1,cap2:value2'}
self.node.properties = properties
result = pxe_utils.get_node_capability(self.node, 'capX')
self.assertIsNone(result)
def test_validate_boot_mode_capability(self):
properties = {'capabilities': 'boot_mode:uefi,cap2:value2'}
self.node.properties = properties
result = pxe_utils.validate_boot_mode_capability(self.node)
self.assertIsNone(result)
def test_validate_boot_mode_capability_with_exception(self):
properties = {'capabilities': 'boot_mode:foo,cap2:value2'}
self.node.properties = properties
self.assertRaises(exception.InvalidParameterValue,
pxe_utils.validate_boot_mode_capability, self.node)
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
@mock.patch('ironic.common.dhcp_factory.DHCPFactory.provider')