grub2 bootloader support for uefi boot mode
This implements support for grub2 bootloader for uefi. The elilo bootloader would be default bootloader. One can configure to use grub2 by updating ironic.conf as:- [pxe] uefi_pxe_config_template=$pybasedir/drivers/modules/pxe_grub_config.template uefi_pxe_bootfile_name=bootx64.efi Implements: blueprint uefi-secure-boot-pxe-ilo Depends-On: I5745fe5c30b0ba1eb67065218dec9c334884007a Change-Id: Idf7b24cf2f14bef9bebb5efd49a30869c8661572
This commit is contained in:
parent
29dd15092f
commit
efa74c5fee
@ -719,8 +719,10 @@ node(s) where ``ironic-conductor`` is running.
|
||||
|
||||
#. Create a map file in the tftp boot directory (``/tftpboot``)::
|
||||
|
||||
echo 'r ^([^/]) /tftpboot/\1' > /tftpboot/map-file
|
||||
echo 'r ^(/tftpboot/) /tftpboot/\2' >> /tftpboot/map-file
|
||||
echo 're ^(/tftpboot/) /tftpboot/\2' > /tftpboot/map-file
|
||||
echo 're ^/tftpboot/ /tftpboot/' >> /tftpboot/map-file
|
||||
echo 're ^(^/) /tftpboot/\1' >> /tftpboot/map-file
|
||||
echo 're ^([^/]) /tftpboot/\1' >> /tftpboot/map-file
|
||||
|
||||
#. Enable tftp map file, modify ``/etc/xinetd.d/tftp`` as below and restart xinetd
|
||||
service::
|
||||
@ -749,6 +751,67 @@ steps on the Ironic conductor node to configure PXE UEFI environment.
|
||||
|
||||
sudo cp ./elilo-3.16-x86_64.efi /tftpboot/elilo.efi
|
||||
|
||||
#. Grub2 is an alternate UEFI bootloader supported in Ironic. Install grub2 and
|
||||
shim packages::
|
||||
|
||||
Ubuntu: (14.04LTS and later)
|
||||
sudo apt-get install grub-efi-amd64-signed shim-signed
|
||||
|
||||
Fedora: (21 and later)
|
||||
CentOS: (7 and later)
|
||||
sudo yum install grub2-efi shim
|
||||
|
||||
#. Copy grub and shim boot loader images to ``/tftpboot`` directory::
|
||||
|
||||
Ubuntu: (14.04LTS and later)
|
||||
sudo cp /usr/lib/shim/shim.efi.signed /tftpboot/bootx64.efi
|
||||
sudo cp /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed \
|
||||
/tftpboot/grubx64.efi
|
||||
|
||||
Fedora: (21 and later)
|
||||
sudo cp /boot/efi/EFI/fedora/shim.efi /tftpboot/bootx64.efi
|
||||
sudo cp /boot/efi/EFI/fedora/grubx64.efi /tftpboot/grubx64.efi
|
||||
|
||||
CentOS: (7 and later)
|
||||
sudo cp /boot/efi/EFI/centos/shim.efi /tftpboot/bootx64.efi
|
||||
sudo cp /boot/efi/EFI/centos/grubx64.efi /tftpboot/grubx64.efi
|
||||
|
||||
#. Create master grub.cfg::
|
||||
|
||||
Ubuntu: Create grub.cfg under ``/tftpboot/grub`` directory.
|
||||
GRUB_DIR=/tftpboot/grub
|
||||
|
||||
Fedora: Create grub.cfg under ``/tftpboot/EFI/fedora`` directory.
|
||||
GRUB_DIR=/tftpboot/EFI/fedora
|
||||
|
||||
CentOS: Create grub.cfg under ``/tftpboot/EFI/centos`` directory.
|
||||
GRUB_DIR=/tftpboot/EFI/centos
|
||||
|
||||
Create directory GRUB_DIR
|
||||
sudo mkdir $GRUB_DIR
|
||||
|
||||
This file is used to redirect grub to baremetal node specific config file.
|
||||
It redirects it to specific grub config file based on DHCP IP assigned to
|
||||
baremetal node.
|
||||
|
||||
.. literalinclude:: ../../../ironic/drivers/modules/master_grub_cfg.txt
|
||||
|
||||
Change the permission of grub.cfg::
|
||||
|
||||
sudo chmod 644 $GRUB_DIR/grub.cfg
|
||||
|
||||
#. Update bootfile and template file configuration parameters for UEFI PXE boot
|
||||
in the Bare Metal Service's configuration file (/etc/ironic/ironic.conf)::
|
||||
|
||||
[pxe]
|
||||
|
||||
# Bootfile DHCP parameter for UEFI boot mode. (string value)
|
||||
uefi_pxe_bootfile_name=bootx64.efi
|
||||
|
||||
# Template file for PXE configuration for UEFI boot loader.
|
||||
# (string value)
|
||||
uefi_pxe_config_template=$pybasedir/drivers/modules/pxe_grub_config.template
|
||||
|
||||
#. Update the Ironic node with ``boot_mode`` capability in node's properties
|
||||
field::
|
||||
|
||||
|
@ -54,7 +54,7 @@ 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):
|
||||
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
|
||||
@ -62,6 +62,8 @@ def _build_pxe_config(pxe_options, template):
|
||||
|
||||
: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.
|
||||
|
||||
"""
|
||||
@ -69,8 +71,8 @@ def _build_pxe_config(pxe_options, template):
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(tmpl_path))
|
||||
template = env.get_template(tmpl_file)
|
||||
return template.render({'pxe_options': pxe_options,
|
||||
'ROOT': '{{ ROOT }}',
|
||||
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}',
|
||||
'ROOT': root_tag,
|
||||
'DISK_IDENTIFIER': disk_ident_tag,
|
||||
})
|
||||
|
||||
|
||||
@ -95,10 +97,12 @@ def _link_mac_pxe_configs(task):
|
||||
create_link(_get_pxe_mac_path(mac, delimiter=''))
|
||||
|
||||
|
||||
def _link_ip_address_pxe_configs(task):
|
||||
def _link_ip_address_pxe_configs(task, hex_form):
|
||||
"""Link each IP address with the PXE configuration file.
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
:param hex_form: Boolean value indicating if the conf file name should be
|
||||
hexadecimal equivalent of supplied ipv4 address.
|
||||
:raises: FailedToGetIPAddressOnPort
|
||||
:raises: InvalidIPv4Address
|
||||
|
||||
@ -112,7 +116,8 @@ def _link_ip_address_pxe_configs(task):
|
||||
"Failed to get IP address for any port on node %s.") %
|
||||
task.node.uuid)
|
||||
for port_ip_address in ip_addrs:
|
||||
ip_address_path = _get_pxe_ip_address_path(port_ip_address)
|
||||
ip_address_path = _get_pxe_ip_address_path(port_ip_address,
|
||||
hex_form)
|
||||
utils.unlink_without_raise(ip_address_path)
|
||||
utils.create_link_without_raise(pxe_config_file_path,
|
||||
ip_address_path)
|
||||
@ -136,18 +141,23 @@ def _get_pxe_mac_path(mac, delimiter=None):
|
||||
return os.path.join(get_root_dir(), PXE_CFG_DIR_NAME, mac_file_name)
|
||||
|
||||
|
||||
def _get_pxe_ip_address_path(ip_address):
|
||||
def _get_pxe_ip_address_path(ip_address, hex_form):
|
||||
"""Convert an ipv4 address into a PXE config file name.
|
||||
|
||||
:param ip_address: A valid IPv4 address string in the format 'n.n.n.n'.
|
||||
:param hex_form: Boolean value indicating if the conf file name should be
|
||||
hexadecimal equivalent of supplied ipv4 address.
|
||||
:returns: the path to the config file.
|
||||
|
||||
"""
|
||||
ip = ip_address.split('.')
|
||||
hex_ip = '{0:02X}{1:02X}{2:02X}{3:02X}'.format(*map(int, ip))
|
||||
# elilo bootloader needs hex based config file name.
|
||||
if hex_form:
|
||||
ip = ip_address.split('.')
|
||||
ip_address = '{0:02X}{1:02X}{2:02X}{3:02X}'.format(*map(int, ip))
|
||||
|
||||
# grub2 bootloader needs ip based config file name.
|
||||
return os.path.join(
|
||||
CONF.pxe.tftp_root, hex_ip + ".conf"
|
||||
CONF.pxe.tftp_root, ip_address + ".conf"
|
||||
)
|
||||
|
||||
|
||||
@ -181,9 +191,14 @@ def create_pxe_config(task, pxe_options, template=None):
|
||||
|
||||
This method will generate the PXE configuration file for the task's
|
||||
node under a directory named with the UUID of that node. For each
|
||||
MAC address (port) of that node, a symlink for the configuration file
|
||||
will be created under the PXE configuration directory, so regardless
|
||||
of which port boots first they'll get the same PXE configuration.
|
||||
MAC address or DHCP IP address (port) of that node, a symlink for
|
||||
the configuration file will be created under the PXE configuration
|
||||
directory, so regardless of which port boots first they'll get the
|
||||
same PXE configuration.
|
||||
If elilo is the bootloader in use, then its configuration file will
|
||||
be created based on hex form of DHCP IP address.
|
||||
If grub2 bootloader is in use, then its configuration will be created
|
||||
based on DHCP IP address in the form nn.nn.nn.nn.
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
:param pxe_options: A dictionary with the PXE configuration
|
||||
@ -200,11 +215,32 @@ def create_pxe_config(task, pxe_options, template=None):
|
||||
_ensure_config_dirs_exist(task.node.uuid)
|
||||
|
||||
pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
|
||||
pxe_config = _build_pxe_config(pxe_options, template)
|
||||
is_uefi_boot_mode = (deploy_utils.get_boot_mode_for_deploy(task.node) ==
|
||||
'uefi')
|
||||
|
||||
# grub bootloader panics with '{}' around any of its tags in its
|
||||
# config file. To overcome that 'ROOT' and 'DISK_IDENTIFIER' are enclosed
|
||||
# with '(' and ')' in uefi boot mode.
|
||||
# These changes do not have any impact on elilo bootloader.
|
||||
hex_form = True
|
||||
if is_uefi_boot_mode and utils.is_regex_string_in_file(template,
|
||||
'^menuentry'):
|
||||
hex_form = False
|
||||
pxe_config_root_tag = '(( ROOT ))'
|
||||
pxe_config_disk_ident = '(( DISK_IDENTIFIER ))'
|
||||
else:
|
||||
# TODO(stendulker): We should use '(' ')' as the delimiters for all our
|
||||
# config files so that we do not need special handling for each of the
|
||||
# bootloaders. Should be removed once the M release starts.
|
||||
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)
|
||||
utils.write_to_file(pxe_config_file_path, pxe_config)
|
||||
|
||||
if deploy_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
|
||||
_link_ip_address_pxe_configs(task)
|
||||
if is_uefi_boot_mode:
|
||||
_link_ip_address_pxe_configs(task, hex_form)
|
||||
else:
|
||||
_link_mac_pxe_configs(task)
|
||||
|
||||
@ -225,10 +261,18 @@ def clean_up_pxe_config(task):
|
||||
|
||||
for port_ip_address in ip_addresses:
|
||||
try:
|
||||
ip_address_path = _get_pxe_ip_address_path(port_ip_address)
|
||||
# Get xx.xx.xx.xx based grub config file
|
||||
ip_address_path = _get_pxe_ip_address_path(port_ip_address,
|
||||
False)
|
||||
# Get 0AOAOAOA based elilo config file
|
||||
hex_ip_path = _get_pxe_ip_address_path(port_ip_address,
|
||||
True)
|
||||
except exception.InvalidIPv4Address:
|
||||
continue
|
||||
# Cleaning up config files created for grub2.
|
||||
utils.unlink_without_raise(ip_address_path)
|
||||
# Cleaning up config files created for elilo.
|
||||
utils.unlink_without_raise(hex_ip_path)
|
||||
else:
|
||||
for mac in driver_utils.get_node_mac_addresses(task):
|
||||
utils.unlink_without_raise(_get_pxe_mac_path(mac))
|
||||
|
@ -653,3 +653,8 @@ def get_updated_capabilities(current_capabilities, new_capabilities):
|
||||
cap_dict.update(new_capabilities)
|
||||
return ','.join('%(key)s:%(value)s' % {'key': key, 'value': value}
|
||||
for key, value in six.iteritems(cap_dict))
|
||||
|
||||
|
||||
def is_regex_string_in_file(path, string):
|
||||
with open(path, 'r') as inf:
|
||||
return any(re.search(string, line) for line in inf.readlines())
|
||||
|
@ -357,7 +357,7 @@ def _replace_lines_in_file(path, regex_pattern, replacement):
|
||||
|
||||
def _replace_root_uuid(path, root_uuid):
|
||||
root = 'UUID=%s' % root_uuid
|
||||
pattern = r'\{\{ ROOT \}\}'
|
||||
pattern = r'(\(\(|\{\{) ROOT (\)\)|\}\})'
|
||||
_replace_lines_in_file(path, pattern, root)
|
||||
|
||||
|
||||
@ -371,8 +371,8 @@ def _replace_boot_line(path, boot_mode, is_whole_disk_image,
|
||||
boot_disk_type = 'boot_partition'
|
||||
|
||||
if boot_mode == 'uefi':
|
||||
pattern = '^default=.*$'
|
||||
boot_line = 'default=%s' % boot_disk_type
|
||||
pattern = '^((set )?default)=.*$'
|
||||
boot_line = '\\1=%s' % boot_disk_type
|
||||
else:
|
||||
pxe_cmd = 'goto' if CONF.pxe.ipxe_enabled else 'default'
|
||||
pattern = '^%s .*$' % pxe_cmd
|
||||
@ -382,7 +382,7 @@ def _replace_boot_line(path, boot_mode, is_whole_disk_image,
|
||||
|
||||
|
||||
def _replace_disk_identifier(path, disk_identifier):
|
||||
pattern = r'\{\{ DISK_IDENTIFIER \}\}'
|
||||
pattern = r'(\(\(|\{\{) DISK_IDENTIFIER (\)\)|\}\})'
|
||||
_replace_lines_in_file(path, pattern, disk_identifier)
|
||||
|
||||
|
||||
|
7
ironic/drivers/modules/master_grub_cfg.txt
Normal file
7
ironic/drivers/modules/master_grub_cfg.txt
Normal file
@ -0,0 +1,7 @@
|
||||
set default=master
|
||||
set timeout=5
|
||||
set hidden_timeout_quiet=false
|
||||
|
||||
menuentry "master" {
|
||||
configfile /tftpboot/$net_default_ip.conf
|
||||
}
|
17
ironic/drivers/modules/pxe_grub_config.template
Normal file
17
ironic/drivers/modules/pxe_grub_config.template
Normal file
@ -0,0 +1,17 @@
|
||||
set default=deploy
|
||||
set timeout=5
|
||||
set hidden_timeout_quiet=false
|
||||
|
||||
menuentry "deploy" {
|
||||
linuxefi {{ pxe_options.deployment_aki_path }} selinux=0 troubleshoot=0 text disk={{ pxe_options.disk }} iscsi_target_iqn={{ pxe_options.iscsi_target_iqn }} deployment_id={{ pxe_options.deployment_id }} deployment_key={{ pxe_options.deployment_key }} ironic_api_url={{ pxe_options.ironic_api_url }} {{ pxe_options.pxe_append_params|default("", true) }} boot_server={{pxe_options.tftp_server}} {% if pxe_options.root_device %}root_device={{ pxe_options.root_device }}{% endif %} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }} boot_option={{ pxe_options.boot_option }} boot_mode={{ pxe_options['boot_mode'] }} coreos.configdrive=0
|
||||
initrdefi {{ pxe_options.deployment_ari_path }}
|
||||
}
|
||||
|
||||
menuentry "boot_partition" {
|
||||
linuxefi {{ pxe_options.aki_path }} root={{ ROOT }} ro text {{ pxe_options.pxe_append_params|default("", true) }} boot_server={{pxe_options.tftp_server}}
|
||||
initrdefi {{ pxe_options.ari_path }}
|
||||
}
|
||||
|
||||
menuentry "boot_whole_disk" {
|
||||
linuxefi chain.c32 mbr:{{ DISK_IDENTIFIER }}
|
||||
}
|
18
ironic/tests/drivers/pxe_grub_config.template
Normal file
18
ironic/tests/drivers/pxe_grub_config.template
Normal file
@ -0,0 +1,18 @@
|
||||
set default=deploy
|
||||
set timeout=5
|
||||
set hidden_timeout_quiet=false
|
||||
|
||||
menuentry "deploy" {
|
||||
linuxefi /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_kernel selinux=0 troubleshoot=0 text disk=cciss/c0d0,sda,hda,vda iscsi_target_iqn=iqn-1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_id=1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_key=0123456789ABCDEFGHIJKLMNOPQRSTUV ironic_api_url=http://192.168.122.184:6385 test_param boot_server=192.0.2.1 root_device=vendor=fake,size=123 ipa-api-url=http://192.168.122.184:6385 ipa-driver-name=pxe_ssh boot_option=netboot boot_mode=uefi coreos.configdrive=0
|
||||
initrdefi /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_partition" {
|
||||
linuxefi /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel root=(( ROOT )) ro text test_param boot_server=192.0.2.1
|
||||
initrdefi /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_whole_disk" {
|
||||
linuxefi chain.c32 mbr:(( DISK_IDENTIFIER ))
|
||||
}
|
||||
|
@ -262,6 +262,66 @@ image=chain.c32
|
||||
append="mbr:0x12345678"
|
||||
"""
|
||||
|
||||
_UEFI_PXECONF_DEPLOY_GRUB = b"""
|
||||
set default=deploy
|
||||
set timeout=5
|
||||
set hidden_timeout_quiet=false
|
||||
|
||||
menuentry "deploy" {
|
||||
linuxefi deploy_kernel "ro text"
|
||||
initrdefi deploy_ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_partition" {
|
||||
linuxefi kernel "root=(( ROOT ))"
|
||||
initrdefi ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_whole_disk" {
|
||||
linuxefi chain.c32 mbr:(( DISK_IDENTIFIER ))
|
||||
}
|
||||
"""
|
||||
|
||||
_UEFI_PXECONF_BOOT_PARTITION_GRUB = """
|
||||
set default=boot_partition
|
||||
set timeout=5
|
||||
set hidden_timeout_quiet=false
|
||||
|
||||
menuentry "deploy" {
|
||||
linuxefi deploy_kernel "ro text"
|
||||
initrdefi deploy_ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_partition" {
|
||||
linuxefi kernel "root=UUID=12345678-1234-1234-1234-1234567890abcdef"
|
||||
initrdefi ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_whole_disk" {
|
||||
linuxefi chain.c32 mbr:(( DISK_IDENTIFIER ))
|
||||
}
|
||||
"""
|
||||
|
||||
_UEFI_PXECONF_BOOT_WHOLE_DISK_GRUB = """
|
||||
set default=boot_whole_disk
|
||||
set timeout=5
|
||||
set hidden_timeout_quiet=false
|
||||
|
||||
menuentry "deploy" {
|
||||
linuxefi deploy_kernel "ro text"
|
||||
initrdefi deploy_ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_partition" {
|
||||
linuxefi kernel "root=(( ROOT ))"
|
||||
initrdefi ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_whole_disk" {
|
||||
linuxefi chain.c32 mbr:0x12345678
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@mock.patch.object(time, 'sleep', lambda seconds: None)
|
||||
class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
@ -917,10 +977,13 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
|
||||
class SwitchPxeConfigTestCase(tests_base.TestCase):
|
||||
|
||||
def _create_config(self, ipxe=False, boot_mode=None):
|
||||
def _create_config(self, ipxe=False, boot_mode=None, boot_loader='elilo'):
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
if boot_mode == 'uefi':
|
||||
pxe_cfg = _UEFI_PXECONF_DEPLOY
|
||||
if boot_loader == 'grub':
|
||||
pxe_cfg = _UEFI_PXECONF_DEPLOY_GRUB
|
||||
else:
|
||||
pxe_cfg = _UEFI_PXECONF_DEPLOY
|
||||
else:
|
||||
pxe_cfg = _IPXECONF_DEPLOY if ipxe else _PXECONF_DEPLOY
|
||||
os.write(fd, pxe_cfg)
|
||||
@ -985,7 +1048,7 @@ class SwitchPxeConfigTestCase(tests_base.TestCase):
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_IPXECONF_BOOT_WHOLE_DISK, pxeconf)
|
||||
|
||||
def test_switch_uefi_pxe_config_partition_image(self):
|
||||
def test_switch_uefi_elilo_pxe_config_partition_image(self):
|
||||
boot_mode = 'uefi'
|
||||
fname = self._create_config(boot_mode=boot_mode)
|
||||
utils.switch_pxe_config(fname,
|
||||
@ -996,7 +1059,7 @@ class SwitchPxeConfigTestCase(tests_base.TestCase):
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_UEFI_PXECONF_BOOT_PARTITION, pxeconf)
|
||||
|
||||
def test_switch_uefi_config_whole_disk_image(self):
|
||||
def test_switch_uefi_elilo_config_whole_disk_image(self):
|
||||
boot_mode = 'uefi'
|
||||
fname = self._create_config(boot_mode=boot_mode)
|
||||
utils.switch_pxe_config(fname,
|
||||
@ -1007,6 +1070,28 @@ class SwitchPxeConfigTestCase(tests_base.TestCase):
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_UEFI_PXECONF_BOOT_WHOLE_DISK, pxeconf)
|
||||
|
||||
def test_switch_uefi_grub_pxe_config_partition_image(self):
|
||||
boot_mode = 'uefi'
|
||||
fname = self._create_config(boot_mode=boot_mode, boot_loader='grub')
|
||||
utils.switch_pxe_config(fname,
|
||||
'12345678-1234-1234-1234-1234567890abcdef',
|
||||
boot_mode,
|
||||
False)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_UEFI_PXECONF_BOOT_PARTITION_GRUB, pxeconf)
|
||||
|
||||
def test_switch_uefi_grub_config_whole_disk_image(self):
|
||||
boot_mode = 'uefi'
|
||||
fname = self._create_config(boot_mode=boot_mode, boot_loader='grub')
|
||||
utils.switch_pxe_config(fname,
|
||||
'0x12345678',
|
||||
boot_mode,
|
||||
True)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_UEFI_PXECONF_BOOT_WHOLE_DISK_GRUB, pxeconf)
|
||||
|
||||
|
||||
@mock.patch('time.sleep', lambda sec: None)
|
||||
class OtherFunctionTestCase(db_base.DbTestCase):
|
||||
|
@ -58,10 +58,19 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
'disk': 'cciss/c0d0,sda,hda,vda',
|
||||
'boot_option': 'netboot',
|
||||
'ipa-driver-name': 'pxe_ssh',
|
||||
'boot_mode': 'bios',
|
||||
}
|
||||
self.pxe_options.update(common_pxe_options)
|
||||
|
||||
self.pxe_options_bios = {
|
||||
'boot_mode': 'bios',
|
||||
}
|
||||
self.pxe_options_bios.update(self.pxe_options)
|
||||
|
||||
self.pxe_options_uefi = {
|
||||
'boot_mode': 'uefi',
|
||||
}
|
||||
self.pxe_options_uefi.update(self.pxe_options)
|
||||
|
||||
self.agent_pxe_options = {
|
||||
'ipa-driver-name': 'agent_ipmitool',
|
||||
}
|
||||
@ -80,7 +89,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
def test__build_pxe_config(self):
|
||||
|
||||
rendered_template = pxe_utils._build_pxe_config(
|
||||
self.pxe_options, CONF.pxe.pxe_config_template)
|
||||
self.pxe_options_bios, CONF.pxe.pxe_config_template,
|
||||
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
|
||||
|
||||
expected_template = open(
|
||||
'ironic/tests/drivers/pxe_config.template').read().rstrip()
|
||||
@ -90,7 +100,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
def test__build_pxe_config_with_agent(self):
|
||||
|
||||
rendered_template = pxe_utils._build_pxe_config(
|
||||
self.agent_pxe_options, CONF.agent.agent_pxe_config_template)
|
||||
self.agent_pxe_options, CONF.agent.agent_pxe_config_template,
|
||||
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
|
||||
|
||||
expected_template = open(
|
||||
'ironic/tests/drivers/agent_pxe_config.template').read().rstrip()
|
||||
@ -108,7 +119,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
)
|
||||
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)
|
||||
self.ipxe_options, CONF.pxe.pxe_config_template,
|
||||
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
|
||||
|
||||
expected_template = open(
|
||||
'ironic/tests/drivers/ipxe_config.template').read().rstrip()
|
||||
@ -119,7 +131,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
pxe_opts = self.pxe_options
|
||||
pxe_opts['boot_mode'] = 'uefi'
|
||||
rendered_template = pxe_utils._build_pxe_config(
|
||||
pxe_opts, CONF.pxe.uefi_pxe_config_template)
|
||||
pxe_opts, CONF.pxe.uefi_pxe_config_template,
|
||||
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
|
||||
|
||||
expected_template = open(
|
||||
'ironic/tests/drivers/elilo_efi_pxe_config.template'
|
||||
@ -127,6 +140,19 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
|
||||
self.assertEqual(six.text_type(expected_template), rendered_template)
|
||||
|
||||
def test__build_grub_config(self):
|
||||
pxe_opts = self.pxe_options
|
||||
pxe_opts['boot_mode'] = 'uefi'
|
||||
pxe_opts['tftp_server'] = '192.0.2.1'
|
||||
grub_tmplte = "ironic/drivers/modules/pxe_grub_config.template"
|
||||
rendered_template = pxe_utils._build_pxe_config(
|
||||
pxe_opts, grub_tmplte, '(( ROOT ))', '(( DISK_IDENTIFIER ))')
|
||||
|
||||
expected_template = open(
|
||||
'ironic/tests/drivers/pxe_grub_config.template').read().rstrip()
|
||||
|
||||
self.assertEqual(six.text_type(expected_template), rendered_template)
|
||||
|
||||
@mock.patch('ironic.common.utils.create_link_without_raise', autospec=True)
|
||||
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
||||
@mock.patch('ironic.drivers.utils.get_node_mac_addresses', autospec=True)
|
||||
@ -200,12 +226,12 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
provider_mock.get_ip_addresses.return_value = [ip_address]
|
||||
create_link_calls = [
|
||||
mock.call(u'/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/config',
|
||||
u'/tftpboot/0A0A0001.conf'),
|
||||
u'/tftpboot/10.10.0.1.conf'),
|
||||
]
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
pxe_utils._link_ip_address_pxe_configs(task)
|
||||
pxe_utils._link_ip_address_pxe_configs(task, False)
|
||||
|
||||
unlink_mock.assert_called_once_with('/tftpboot/0A0A0001.conf')
|
||||
unlink_mock.assert_called_once_with('/tftpboot/10.10.0.1.conf')
|
||||
create_link_mock.assert_has_calls(create_link_calls)
|
||||
|
||||
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
|
||||
@ -213,12 +239,14 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
|
||||
def test_create_pxe_config(self, ensure_tree_mock, build_mock,
|
||||
write_mock):
|
||||
build_mock.return_value = self.pxe_options
|
||||
build_mock.return_value = self.pxe_options_bios
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
pxe_utils.create_pxe_config(task, self.pxe_options,
|
||||
pxe_utils.create_pxe_config(task, self.pxe_options_bios,
|
||||
CONF.pxe.pxe_config_template)
|
||||
build_mock.assert_called_with(self.pxe_options,
|
||||
CONF.pxe.pxe_config_template)
|
||||
build_mock.assert_called_with(self.pxe_options_bios,
|
||||
CONF.pxe.pxe_config_template,
|
||||
'{{ ROOT }}',
|
||||
'{{ 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'))
|
||||
@ -226,7 +254,62 @@ 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, self.pxe_options_bios)
|
||||
|
||||
@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('oslo_utils.fileutils.ensure_tree', autospec=True)
|
||||
def test_create_pxe_config_uefi_elilo(self, ensure_tree_mock, build_mock,
|
||||
write_mock, link_ip_configs_mock):
|
||||
build_mock.return_value = self.pxe_options_uefi
|
||||
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_uefi,
|
||||
CONF.pxe.uefi_pxe_config_template)
|
||||
|
||||
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'))
|
||||
]
|
||||
ensure_tree_mock.assert_has_calls(ensure_calls)
|
||||
build_mock.assert_called_with(self.pxe_options_uefi,
|
||||
CONF.pxe.uefi_pxe_config_template,
|
||||
'{{ ROOT }}',
|
||||
'{{ 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_uefi)
|
||||
|
||||
@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('oslo_utils.fileutils.ensure_tree', autospec=True)
|
||||
def test_create_pxe_config_uefi_grub(self, ensure_tree_mock, build_mock,
|
||||
write_mock, link_ip_configs_mock):
|
||||
build_mock.return_value = self.pxe_options_uefi
|
||||
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'
|
||||
pxe_utils.create_pxe_config(task, self.pxe_options_uefi,
|
||||
grub_tmplte)
|
||||
|
||||
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'))
|
||||
]
|
||||
ensure_tree_mock.assert_has_calls(ensure_calls)
|
||||
build_mock.assert_called_with(self.pxe_options_uefi,
|
||||
grub_tmplte,
|
||||
'(( ROOT ))',
|
||||
'(( 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_uefi)
|
||||
|
||||
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
|
||||
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
||||
@ -257,8 +340,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
|
||||
def test__get_pxe_ip_address_path(self):
|
||||
ipaddress = '10.10.0.1'
|
||||
self.assertEqual('/tftpboot/0A0A0001.conf',
|
||||
pxe_utils._get_pxe_ip_address_path(ipaddress))
|
||||
self.assertEqual('/tftpboot/10.10.0.1.conf',
|
||||
pxe_utils._get_pxe_ip_address_path(ipaddress, False))
|
||||
|
||||
def test_get_root_dir(self):
|
||||
expected_dir = '/tftproot'
|
||||
@ -381,7 +464,11 @@ 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')
|
||||
unlink_calls = [
|
||||
mock.call('/tftpboot/10.10.0.1.conf'),
|
||||
mock.call('/tftpboot/0A0A0001.conf')
|
||||
]
|
||||
unlink_mock.assert_has_calls(unlink_calls)
|
||||
rmtree_mock.assert_called_once_with(
|
||||
os.path.join(CONF.pxe.tftp_root, self.node.uuid))
|
||||
|
||||
@ -402,6 +489,10 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
task.node.instance_info['deploy_boot_mode'] = 'uefi'
|
||||
pxe_utils.clean_up_pxe_config(task)
|
||||
|
||||
unlink_mock.assert_called_once_with('/tftpboot/0A0A0001.conf')
|
||||
unlink_calls = [
|
||||
mock.call('/tftpboot/10.10.0.1.conf'),
|
||||
mock.call('/tftpboot/0A0A0001.conf')
|
||||
]
|
||||
unlink_mock.assert_has_calls(unlink_calls)
|
||||
rmtree_mock.assert_called_once_with(
|
||||
os.path.join(CONF.pxe.tftp_root, self.node.uuid))
|
||||
|
Loading…
Reference in New Issue
Block a user