Copy iPXE script over only when needed

currently the iPXE boot script is overwritten on every `prepare_ramdisk`
call, which is inefficient.

Instead, copy it over only when it is missing from HTTPROOT or is
different from the one specified in the config.
The comparison is performed using `filecmp` from Python standard lib,
which in turn uses `os.stat()`, so although being not very strict,
comparison does not call to any extra utilities and is fast.

Change-Id: I1213fc9a9945e11f963d10f0581a7d69ef13ee53
This commit is contained in:
Pavlo Shchelokovskyy 2016-08-17 18:09:07 +03:00
parent 8783e8d036
commit 4a0767a14c
2 changed files with 43 additions and 3 deletions

View File

@ -15,6 +15,7 @@
PXE Boot Interface
"""
import filecmp
import os
import shutil
@ -413,13 +414,14 @@ class PXEBoot(base.BootInterface):
"""
node = task.node
# TODO(deva): optimize this if rerun on existing files
if CONF.pxe.ipxe_enabled:
# Copy the iPXE boot script to HTTP root directory
bootfile_path = os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script))
shutil.copyfile(CONF.pxe.ipxe_boot_script, bootfile_path)
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)
dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
provider = dhcp_factory.DHCPFactory()

View File

@ -15,6 +15,7 @@
"""Test class for PXE driver."""
import filecmp
import os
import shutil
import tempfile
@ -698,12 +699,17 @@ class PXEBootTestCase(db_base.DbTestCase):
self.node.save()
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)
def test_prepare_ramdisk_ipxe(self, copyfile_mock):
def test_prepare_ramdisk_ipxe_with_copy_file_different(
self, copyfile_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
self._test_prepare_ramdisk()
copyfile_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
@ -711,6 +717,38 @@ class PXEBootTestCase(db_base.DbTestCase):
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script)))
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(filecmp, 'cmp', autospec=True)
@mock.patch.object(shutil, 'copyfile', autospec=True)
def test_prepare_ramdisk_ipxe_with_copy_no_file(
self, copyfile_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
self._test_prepare_ramdisk()
self.assertFalse(cmp_mock.called)
copyfile_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script)))
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(filecmp, 'cmp', autospec=True)
@mock.patch.object(shutil, 'copyfile', autospec=True)
def test_prepare_ramdisk_ipxe_without_copy(
self, copyfile_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 = True
self._test_prepare_ramdisk()
self.assertFalse(copyfile_mock.called)
def test_prepare_ramdisk_cleaning(self):
self.node.provision_state = states.CLEANING
self.node.save()