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:
parent
7571df1366
commit
d5bf6233f9
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user