Merge "grub2 bootloader support for uefi boot mode"
This commit is contained in:
commit
68ec9be6a0
@ -724,8 +724,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::
|
||||
@ -754,6 +756,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())
|
||||
|
@ -364,7 +364,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)
|
||||
|
||||
|
||||
@ -378,8 +378,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
|
||||
@ -389,7 +389,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):
|
||||
@ -922,10 +982,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)
|
||||
@ -990,7 +1053,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,
|
||||
@ -1001,7 +1064,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,
|
||||
@ -1012,6 +1075,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…
x
Reference in New Issue
Block a user