Convert iPXE boot script to Jinja template

This will not affect those using custom out-of-tree boot scripts,
as such files will be rendered as-is.

This patch also moves template rendering to a common place and sets
create_pxe_config to use it.

Change-Id: Ie5c94fee26054a209d35a25a99f3e9f57bd39d14
Partial-Bug: #1526275
This commit is contained in:
Pavlo Shchelokovskyy 2016-11-04 13:38:08 +02:00
parent 2b5fdc991a
commit 687a16bd75
8 changed files with 241 additions and 116 deletions

View File

@ -17,7 +17,6 @@
import os
from ironic_lib import utils as ironic_utils
import jinja2
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import fileutils
@ -54,28 +53,6 @@ def _ensure_config_dirs_exist(node_uuid):
fileutils.ensure_tree(os.path.join(root_dir, PXE_CFG_DIR_NAME))
def _build_pxe_config(pxe_options, template, root_tag, disk_ident_tag):
"""Build the PXE boot configuration file.
This method builds the PXE boot configuration file by rendering the
template with the given parameters.
:param pxe_options: A dict of values to set on the configuration file.
:param template: The PXE configuration template.
:param root_tag: Root tag used in the PXE config file.
:param disk_ident_tag: Disk identifier tag used in the PXE config file.
:returns: A formatted string with the file content.
"""
tmpl_path, tmpl_file = os.path.split(template)
env = jinja2.Environment(loader=jinja2.FileSystemLoader(tmpl_path))
template = env.get_template(tmpl_file)
return template.render({'pxe_options': pxe_options,
'ROOT': root_tag,
'DISK_IDENTIFIER': disk_ident_tag,
})
def _link_mac_pxe_configs(task):
"""Link each MAC address with the PXE configuration file.
@ -237,8 +214,11 @@ def create_pxe_config(task, pxe_options, template=None):
pxe_config_root_tag = '{{ ROOT }}'
pxe_config_disk_ident = '{{ DISK_IDENTIFIER }}'
pxe_config = _build_pxe_config(pxe_options, template, pxe_config_root_tag,
pxe_config_disk_ident)
params = {'pxe_options': pxe_options,
'ROOT': pxe_config_root_tag,
'DISK_IDENTIFIER': pxe_config_disk_ident}
pxe_config = utils.render_template(template, params)
utils.write_to_file(pxe_config_file_path, pxe_config)
if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled:

View File

@ -27,6 +27,7 @@ import re
import shutil
import tempfile
import jinja2
from oslo_concurrency import processutils
from oslo_log import log as logging
from oslo_utils import netutils
@ -283,6 +284,22 @@ def hash_file(file_like_object, hash_algo='md5'):
return checksum.hexdigest()
def file_has_content(path, content, hash_algo='md5'):
"""Checks that content of the file is the same as provided reference.
:param path: path to file
:param content: reference content to check against
:param hash_algo: hashing algo from hashlib to use, default is 'md5'
:returns: True if the hash of reference content is the same as
the hash of file's content, False otherwise
"""
with open(path) as existing:
file_hash_hex = hash_file(existing, hash_algo=hash_algo)
ref_hash = _get_hash_object(hash_algo)
ref_hash.update(content)
return file_hash_hex == ref_hash.hexdigest()
@contextlib.contextmanager
def tempdir(**kwargs):
tempfile.tempdir = CONF.tempdir
@ -500,3 +517,22 @@ def validate_network_port(port, port_name="Port"):
'numbers must be between 1 and 65535.') %
{'port_name': port_name, 'port': port})
return port
def render_template(template, params, is_file=True):
"""Renders Jinja2 template file with given parameters.
:param template: full path to the Jinja2 template file
:param params: dictionary with parameters to use when rendering
:param is_file: whether template is file or string with template itself
:returns: the rendered template as a string
"""
if is_file:
tmpl_path, tmpl_name = os.path.split(template)
loader = jinja2.FileSystemLoader(tmpl_path)
else:
tmpl_name = 'template'
loader = jinja2.DictLoader({tmpl_name: template})
env = jinja2.Environment(loader=loader)
tmpl = env.get_template(tmpl_name)
return tmpl.render(params)

View File

@ -5,10 +5,10 @@
# https://bugs.launchpad.net/ironic/+bug/1504482
set netid:int32 -1
:loop
inc netid || chain pxelinux.cfg/${mac:hexhyp} || goto old_rom
inc netid || chain {{ ipxe_for_mac_uri }}${mac:hexhyp} || goto old_rom
isset ${net${netid}/mac} || goto loop_done
echo Attempting to boot from MAC ${net${netid}/mac:hexhyp}
chain pxelinux.cfg/${net${netid}/mac:hexhyp} || goto loop
chain {{ ipxe_for_mac_uri }}${net${netid}/mac:hexhyp} || goto loop
:loop_done
echo PXE boot failed! No configuration found for any of the present NICs.

View File

@ -15,9 +15,7 @@
PXE Boot Interface
"""
import filecmp
import os
import shutil
from ironic_lib import metrics_utils
from ironic_lib import utils as ironic_utils
@ -33,6 +31,7 @@ from ironic.common import image_service as service
from ironic.common import images
from ironic.common import pxe_utils
from ironic.common import states
from ironic.common import utils
from ironic.conf import CONF
from ironic.drivers import base
from ironic.drivers.modules import deploy_utils
@ -382,13 +381,20 @@ class PXEBoot(base.BootInterface):
node = task.node
if CONF.pxe.ipxe_enabled:
# Copy the iPXE boot script to HTTP root directory
# Render the iPXE boot script template and save it
# to HTTP root directory
boot_script = utils.render_template(
CONF.pxe.ipxe_boot_script,
{'ipxe_for_mac_uri': pxe_utils.PXE_CFG_DIR_NAME + '/'})
bootfile_path = os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script))
# NOTE(pas-ha) to prevent unneeded writes,
# only write to file if its content is different from required,
# which should be rather rare
if (not os.path.isfile(bootfile_path) or
not filecmp.cmp(CONF.pxe.ipxe_boot_script, bootfile_path)):
shutil.copyfile(CONF.pxe.ipxe_boot_script, bootfile_path)
not utils.file_has_content(bootfile_path, boot_script)):
utils.write_to_file(bootfile_path, boot_script)
dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
provider = dhcp_factory.DHCPFactory()

View File

@ -22,6 +22,7 @@ from oslo_utils import uuidutils
import six
from ironic.common import pxe_utils
from ironic.common import utils
from ironic.conductor import task_manager
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
@ -65,18 +66,30 @@ class TestPXEUtils(db_base.DbTestCase):
self.node = object_utils.create_test_node(self.context)
def test__build_pxe_config(self):
def test_default_pxe_config(self):
rendered_template = pxe_utils._build_pxe_config(
self.pxe_options, CONF.pxe.pxe_config_template,
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
rendered_template = utils.render_template(
CONF.pxe.pxe_config_template,
{'pxe_options': self.pxe_options,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})
expected_template = open(
'ironic/tests/unit/drivers/pxe_config.template').read().rstrip()
self.assertEqual(six.text_type(expected_template), rendered_template)
def test__build_ipxe_config(self):
def test_default_ipxe_boot_script(self):
rendered_template = utils.render_template(
CONF.pxe.ipxe_boot_script,
{'ipxe_for_mac_uri': 'pxelinux.cfg/'})
expected_template = open(
'ironic/tests/unit/drivers/boot.ipxe').read().rstrip()
self.assertEqual(six.text_type(expected_template), rendered_template)
def test_default_ipxe_config(self):
# NOTE(lucasagomes): iPXE is just an extension of the PXE driver,
# it doesn't have it's own configuration option for template.
# More info:
@ -86,16 +99,18 @@ class TestPXEUtils(db_base.DbTestCase):
group='pxe'
)
self.config(http_url='http://1.2.3.4:1234', group='deploy')
rendered_template = pxe_utils._build_pxe_config(
self.ipxe_options, CONF.pxe.pxe_config_template,
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
rendered_template = utils.render_template(
CONF.pxe.pxe_config_template,
{'pxe_options': self.ipxe_options,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})
expected_template = open(
'ironic/tests/unit/drivers/ipxe_config.template').read().rstrip()
self.assertEqual(six.text_type(expected_template), rendered_template)
def test__build_ipxe_timeout_config(self):
def test_default_ipxe_timeout_config(self):
# NOTE(lucasagomes): iPXE is just an extension of the PXE driver,
# it doesn't have it's own configuration option for template.
# More info:
@ -105,16 +120,18 @@ class TestPXEUtils(db_base.DbTestCase):
group='pxe'
)
self.config(http_url='http://1.2.3.4:1234', group='deploy')
rendered_template = pxe_utils._build_pxe_config(
self.ipxe_options_timeout, CONF.pxe.pxe_config_template,
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
rendered_template = utils.render_template(
CONF.pxe.pxe_config_template,
{'pxe_options': self.ipxe_options_timeout,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})
tpl_file = 'ironic/tests/unit/drivers/ipxe_config_timeout.template'
expected_template = open(tpl_file).read().rstrip()
self.assertEqual(six.text_type(expected_template), rendered_template)
def test__build_elilo_config(self):
def test_default_elilo_config(self):
pxe_opts = self.pxe_options
pxe_opts['boot_mode'] = 'uefi'
self.config(
@ -122,9 +139,11 @@ class TestPXEUtils(db_base.DbTestCase):
'elilo_efi_pxe_config.template'),
group='pxe'
)
rendered_template = pxe_utils._build_pxe_config(
pxe_opts, CONF.pxe.uefi_pxe_config_template,
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
rendered_template = utils.render_template(
CONF.pxe.uefi_pxe_config_template,
{'pxe_options': pxe_opts,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})
expected_template = open(
'ironic/tests/unit/drivers/elilo_efi_pxe_config.template'
@ -132,13 +151,15 @@ class TestPXEUtils(db_base.DbTestCase):
self.assertEqual(six.text_type(expected_template), rendered_template)
def test__build_grub_config(self):
def test_default_grub_config(self):
pxe_opts = self.pxe_options
pxe_opts['boot_mode'] = 'uefi'
pxe_opts['tftp_server'] = '192.0.2.1'
rendered_template = pxe_utils._build_pxe_config(
pxe_opts, CONF.pxe.uefi_pxe_config_template,
'(( ROOT ))', '(( DISK_IDENTIFIER ))')
rendered_template = utils.render_template(
CONF.pxe.uefi_pxe_config_template,
{'pxe_options': pxe_opts,
'ROOT': '(( ROOT ))',
'DISK_IDENTIFIER': '(( DISK_IDENTIFIER ))'})
template_file = 'ironic/tests/unit/drivers/pxe_grub_config.template'
expected_template = open(template_file).read().rstrip()
@ -254,18 +275,19 @@ class TestPXEUtils(db_base.DbTestCase):
create_link_mock.assert_has_calls(create_link_calls)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch.object(pxe_utils, '_build_pxe_config', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config(self, ensure_tree_mock, build_mock,
def test_create_pxe_config(self, ensure_tree_mock, render_mock,
write_mock):
build_mock.return_value = self.pxe_options
with task_manager.acquire(self.context, self.node.uuid) as task:
pxe_utils.create_pxe_config(task, self.pxe_options,
CONF.pxe.pxe_config_template)
build_mock.assert_called_with(self.pxe_options,
CONF.pxe.pxe_config_template,
'{{ ROOT }}',
'{{ DISK_IDENTIFIER }}')
render_mock.assert_called_with(
CONF.pxe.pxe_config_template,
{'pxe_options': self.pxe_options,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'}
)
ensure_calls = [
mock.call(os.path.join(CONF.pxe.tftp_root, self.node.uuid)),
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg'))
@ -273,21 +295,21 @@ class TestPXEUtils(db_base.DbTestCase):
ensure_tree_mock.assert_has_calls(ensure_calls)
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
write_mock.assert_called_with(pxe_cfg_file_path, self.pxe_options)
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch('ironic.common.pxe_utils._link_ip_address_pxe_configs',
autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.pxe_utils._build_pxe_config', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_uefi_elilo(self, ensure_tree_mock, build_mock,
def test_create_pxe_config_uefi_elilo(self, ensure_tree_mock, render_mock,
write_mock, link_ip_configs_mock):
self.config(
uefi_pxe_config_template=('ironic/drivers/modules/'
'elilo_efi_pxe_config.template'),
group='pxe'
)
build_mock.return_value = self.pxe_options
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
pxe_utils.create_pxe_config(task, self.pxe_options,
@ -298,23 +320,24 @@ class TestPXEUtils(db_base.DbTestCase):
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg'))
]
ensure_tree_mock.assert_has_calls(ensure_calls)
build_mock.assert_called_with(self.pxe_options,
CONF.pxe.uefi_pxe_config_template,
'{{ ROOT }}',
'{{ DISK_IDENTIFIER }}')
render_mock.assert_called_with(
CONF.pxe.uefi_pxe_config_template,
{'pxe_options': self.pxe_options,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})
link_ip_configs_mock.assert_called_once_with(task, True)
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
write_mock.assert_called_with(pxe_cfg_file_path, self.pxe_options)
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch('ironic.common.pxe_utils._link_ip_address_pxe_configs',
autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.pxe_utils._build_pxe_config', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_uefi_grub(self, ensure_tree_mock, build_mock,
def test_create_pxe_config_uefi_grub(self, ensure_tree_mock, render_mock,
write_mock, link_ip_configs_mock):
build_mock.return_value = self.pxe_options
grub_tmplte = "ironic/drivers/modules/pxe_grub_config.template"
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
@ -326,24 +349,25 @@ class TestPXEUtils(db_base.DbTestCase):
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg'))
]
ensure_tree_mock.assert_has_calls(ensure_calls)
build_mock.assert_called_with(self.pxe_options,
grub_tmplte,
'(( ROOT ))',
'(( DISK_IDENTIFIER ))')
render_mock.assert_called_with(
grub_tmplte,
{'pxe_options': self.pxe_options,
'ROOT': '(( ROOT ))',
'DISK_IDENTIFIER': '(( DISK_IDENTIFIER ))'})
link_ip_configs_mock.assert_called_once_with(task, False)
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
write_mock.assert_called_with(pxe_cfg_file_path, self.pxe_options)
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch('ironic.common.pxe_utils._link_mac_pxe_configs',
autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.pxe_utils._build_pxe_config', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_uefi_ipxe(self, ensure_tree_mock, build_mock,
def test_create_pxe_config_uefi_ipxe(self, ensure_tree_mock, render_mock,
write_mock, link_mac_pxe_mock):
self.config(ipxe_enabled=True, group='pxe')
build_mock.return_value = self.ipxe_options
ipxe_template = "ironic/drivers/modules/ipxe_config.template"
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
@ -355,15 +379,16 @@ class TestPXEUtils(db_base.DbTestCase):
mock.call(os.path.join(CONF.deploy.http_root, 'pxelinux.cfg'))
]
ensure_tree_mock.assert_has_calls(ensure_calls)
build_mock.assert_called_with(self.ipxe_options,
ipxe_template,
'{{ ROOT }}',
'{{ DISK_IDENTIFIER }}')
render_mock.assert_called_with(
ipxe_template,
{'pxe_options': self.ipxe_options,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})
link_mac_pxe_mock.assert_called_once_with(task)
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
write_mock.assert_called_with(pxe_cfg_file_path,
self.ipxe_options)
render_mock.return_value)
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
@mock.patch('ironic_lib.utils.unlink_without_raise', autospec=True)

View File

@ -21,6 +21,7 @@ import os.path
import shutil
import tempfile
import jinja2
import mock
from oslo_concurrency import processutils
from oslo_config import cfg
@ -248,6 +249,20 @@ class GenericUtilsTestCase(base.TestCase):
self.assertRaises(exception.InvalidParameterValue, utils.hash_file,
file_like_object, 'hickory-dickory-dock')
def test_file_has_content_equal(self):
data = b'Mary had a little lamb, its fleece as white as snow'
ref = data
with mock.patch('ironic.common.utils.open',
mock.mock_open(read_data=data)):
self.assertTrue(utils.file_has_content('foo', ref))
def test_file_has_content_differ(self):
data = b'Mary had a little lamb, its fleece as white as snow'
ref = data + b'!'
with mock.patch('ironic.common.utils.open',
mock.mock_open(read_data=data)):
self.assertFalse(utils.file_has_content('foo', ref))
def test_is_valid_datapath_id(self):
self.assertTrue(utils.is_valid_datapath_id("525400cf2d319fdf"))
self.assertTrue(utils.is_valid_datapath_id("525400CF2D319FDF"))
@ -613,3 +628,28 @@ class GetUpdatedCapabilitiesTestCase(base.TestCase):
'Port "invalid" is not a valid integer.',
utils.validate_network_port,
'invalid')
class JinjaTemplatingTestCase(base.TestCase):
def setUp(self):
super(JinjaTemplatingTestCase, self).setUp()
self.template = '{{ foo }} {{ bar }}'
self.params = {'foo': 'spam', 'bar': 'ham'}
self.expected = 'spam ham'
def test_render_string(self):
self.assertEqual(self.expected,
utils.render_template(self.template,
self.params,
is_file=False))
@mock.patch('ironic.common.utils.jinja2.FileSystemLoader')
def test_render_file(self, jinja_fsl_mock):
path = '/path/to/template.j2'
jinja_fsl_mock.return_value = jinja2.DictLoader(
{'template.j2': self.template})
self.assertEqual(self.expected,
utils.render_template(path,
self.params))
jinja_fsl_mock.assert_called_once_with('/path/to')

View File

@ -0,0 +1,24 @@
#!ipxe
# NOTE(lucasagomes): Loop over all network devices and boot from
# the first one capable of booting. For more information see:
# https://bugs.launchpad.net/ironic/+bug/1504482
set netid:int32 -1
:loop
inc netid || chain pxelinux.cfg/${mac:hexhyp} || goto old_rom
isset ${net${netid}/mac} || goto loop_done
echo Attempting to boot from MAC ${net${netid}/mac:hexhyp}
chain pxelinux.cfg/${net${netid}/mac:hexhyp} || goto loop
:loop_done
echo PXE boot failed! No configuration found for any of the present NICs.
echo Press any key to reboot...
prompt --timeout 180
reboot
:old_rom
echo PXE boot failed! No configuration found for NIC ${mac:hexhyp}.
echo Please update your iPXE ROM and retry.
echo Press any key to reboot...
prompt --timeout 180
reboot

View File

@ -15,9 +15,7 @@
"""Test class for PXE driver."""
import filecmp
import os
import shutil
import tempfile
from ironic_lib import utils as ironic_utils
@ -164,8 +162,8 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
image_info = pxe._get_instance_image_info(self.node, self.context)
self.assertEqual({}, image_info)
@mock.patch.object(pxe_utils, '_build_pxe_config', autospec=True)
def _test_build_pxe_config_options_pxe(self, build_pxe_mock,
@mock.patch('ironic.common.utils.render_template', autospec=True)
def _test_build_pxe_config_options_pxe(self, render_mock,
whle_dsk_img=False):
self.config(pxe_append_params='test_param', group='pxe')
# NOTE: right '/' should be removed from url string
@ -272,8 +270,8 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
@mock.patch('ironic.common.image_service.GlanceImageService',
autospec=True)
@mock.patch.object(pxe_utils, '_build_pxe_config', autospec=True)
def _test_build_pxe_config_options_ipxe(self, build_pxe_mock, glance_mock,
@mock.patch('ironic.common.utils.render_template', autospec=True)
def _test_build_pxe_config_options_ipxe(self, render_mock, glance_mock,
whle_dsk_img=False,
ipxe_timeout=0,
ipxe_use_swift=False):
@ -770,46 +768,57 @@ class PXEBootTestCase(db_base.DbTestCase):
self._test_prepare_ramdisk(uefi=True)
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(filecmp, 'cmp', autospec=True)
@mock.patch.object(shutil, 'copyfile', autospec=True)
@mock.patch('ironic.common.utils.file_has_content', autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
def test_prepare_ramdisk_ipxe_with_copy_file_different(
self, copyfile_mock, cmp_mock, isfile_mock):
self, render_mock, write_mock, cmp_mock, isfile_mock):
self.node.provision_state = states.DEPLOYING
self.node.save()
self.config(group='pxe', ipxe_enabled=True)
self.config(group='deploy', http_url='http://myserver')
isfile_mock.return_value = True
cmp_mock.return_value = False
render_mock.return_value = 'foo'
self._test_prepare_ramdisk()
copyfile_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
write_mock.assert_called_once_with(
os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script)))
os.path.basename(CONF.pxe.ipxe_boot_script)),
'foo')
render_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
{'ipxe_for_mac_uri': 'pxelinux.cfg/'})
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(filecmp, 'cmp', autospec=True)
@mock.patch.object(shutil, 'copyfile', autospec=True)
@mock.patch('ironic.common.utils.file_has_content', autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
def test_prepare_ramdisk_ipxe_with_copy_no_file(
self, copyfile_mock, cmp_mock, isfile_mock):
self, render_mock, write_mock, cmp_mock, isfile_mock):
self.node.provision_state = states.DEPLOYING
self.node.save()
self.config(group='pxe', ipxe_enabled=True)
self.config(group='deploy', http_url='http://myserver')
isfile_mock.return_value = False
render_mock.return_value = 'foo'
self._test_prepare_ramdisk()
self.assertFalse(cmp_mock.called)
copyfile_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
write_mock.assert_called_once_with(
os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script)))
os.path.basename(CONF.pxe.ipxe_boot_script)),
'foo')
render_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
{'ipxe_for_mac_uri': 'pxelinux.cfg/'})
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(filecmp, 'cmp', autospec=True)
@mock.patch.object(shutil, 'copyfile', autospec=True)
@mock.patch('ironic.common.utils.file_has_content', autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
def test_prepare_ramdisk_ipxe_without_copy(
self, copyfile_mock, cmp_mock, isfile_mock):
self, render_mock, write_mock, cmp_mock, isfile_mock):
self.node.provision_state = states.DEPLOYING
self.node.save()
self.config(group='pxe', ipxe_enabled=True)
@ -817,35 +826,40 @@ class PXEBootTestCase(db_base.DbTestCase):
isfile_mock.return_value = True
cmp_mock.return_value = True
self._test_prepare_ramdisk()
self.assertFalse(copyfile_mock.called)
self.assertFalse(write_mock.called)
@mock.patch.object(shutil, 'copyfile', autospec=True)
def test_prepare_ramdisk_ipxe_swift(self, copyfile_mock):
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
def test_prepare_ramdisk_ipxe_swift(self, render_mock, write_mock):
self.node.provision_state = states.DEPLOYING
self.node.save()
self.config(group='pxe', ipxe_enabled=True)
self.config(group='pxe', ipxe_use_swift=True)
self.config(group='deploy', http_url='http://myserver')
render_mock.return_value = 'foo'
self._test_prepare_ramdisk(ipxe_use_swift=True)
copyfile_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
write_mock.assert_called_once_with(
os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script)))
os.path.basename(CONF.pxe.ipxe_boot_script)),
'foo')
@mock.patch.object(shutil, 'copyfile', autospec=True)
def test_prepare_ramdisk_ipxe_swift_whole_disk_image(self, copyfile_mock):
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
def test_prepare_ramdisk_ipxe_swift_whole_disk_image(
self, render_mock, write_mock):
self.node.provision_state = states.DEPLOYING
self.node.save()
self.config(group='pxe', ipxe_enabled=True)
self.config(group='pxe', ipxe_use_swift=True)
self.config(group='deploy', http_url='http://myserver')
render_mock.return_value = 'foo'
self._test_prepare_ramdisk(ipxe_use_swift=True, whole_disk_image=True)
copyfile_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
write_mock.assert_called_once_with(
os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script)))
os.path.basename(CONF.pxe.ipxe_boot_script)),
'foo')
def test_prepare_ramdisk_cleaning(self):
self.node.provision_state = states.CLEANING