Merge "Support multi arch deployment"
This commit is contained in:
commit
cde866db2d
@ -2723,6 +2723,11 @@
|
||||
# configuration for UEFI boot loader. (string value)
|
||||
#uefi_pxe_config_template = $pybasedir/drivers/modules/pxe_grub_config.template
|
||||
|
||||
# On ironic-conductor node, template file for PXE
|
||||
# configuration per node architecture. For example:
|
||||
# aarch64:/opt/share/grubaa64_pxe_config.template (dict value)
|
||||
#pxe_config_template_by_arch =
|
||||
|
||||
# IP address of ironic-conductor node's TFTP server. (string
|
||||
# value)
|
||||
#tftp_server = $my_ip
|
||||
@ -2742,6 +2747,10 @@
|
||||
# Bootfile DHCP parameter for UEFI boot mode. (string value)
|
||||
#uefi_pxe_bootfile_name = bootx64.efi
|
||||
|
||||
# Bootfile DHCP parameter per node architecture. For example:
|
||||
# aarch64:grubaa64.efi (dict value)
|
||||
#pxe_bootfile_name_by_arch =
|
||||
|
||||
# Enable iPXE boot. (boolean value)
|
||||
#ipxe_enabled = false
|
||||
|
||||
|
@ -354,6 +354,55 @@ on the Bare Metal service node(s) where ``ironic-conductor`` is running.
|
||||
sudo service ironic-conductor restart
|
||||
|
||||
|
||||
PXE Multi-Arch setup
|
||||
--------------------
|
||||
|
||||
It is possible to deploy servers of different architecture by one conductor.
|
||||
|
||||
To support this feature, architecture specific boot and template files must
|
||||
be configured correctly in the options listed below:
|
||||
|
||||
* ``pxe_bootfile_name_by_arch``
|
||||
* ``pxe_config_template_by_arch``
|
||||
|
||||
These two options are dictionary values. Node's ``cpu_arch`` property is used
|
||||
as the key to find according boot file and template. If according ``cpu_arch``
|
||||
is not found in the dictionary, ``pxe_bootfile_name``, ``pxe_config_template``,
|
||||
``uefi_pxe_bootfile_name`` and ``uefi_pxe_config_template`` are referenced as
|
||||
usual.
|
||||
|
||||
In below example, x86 and x86_64 nodes will be deployed by bootf1 or bootf2
|
||||
based on ``boot_mode`` capability('bios' or 'uefi') as there's no 'x86' or
|
||||
'x86_64' keys available in ``pxe_bootfile_name_by_arch``. While aarch64 nodes
|
||||
will be deployed by bootf3, and ppc64 nodes by bootf4::
|
||||
|
||||
pxe_bootfile_name = bootf1
|
||||
uefi_pxe_bootfile_name = bootf2
|
||||
pxe_bootfile_name_by_arch = aarch64:bootf3,ppc64:bootf4
|
||||
|
||||
Following example assumes you are provisioning x86_64 and aarch64 servers, both
|
||||
in UEFI boot mode.
|
||||
|
||||
Update bootfile and template file configuration parameters 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
|
||||
|
||||
# Bootfile DHCP parameter per node architecture. (dictionary value)
|
||||
pxe_bootfile_name_by_arch=aarch64:grubaa64.efi
|
||||
|
||||
# Template file for PXE configuration per node architecture.
|
||||
# (dictionary value)
|
||||
pxe_config_template_by_arch=aarch64:pxe_grubaa64_config.template
|
||||
|
||||
|
||||
Networking service configuration
|
||||
--------------------------------
|
||||
|
||||
|
@ -206,13 +206,13 @@ def create_pxe_config(task, pxe_options, template=None):
|
||||
:param pxe_options: A dictionary with the PXE configuration
|
||||
parameters.
|
||||
:param template: The PXE configuration template. If no template is
|
||||
given the CONF.pxe.pxe_config_template will be used.
|
||||
given the node specific template will be used.
|
||||
|
||||
"""
|
||||
LOG.debug("Building PXE config for node %s", task.node.uuid)
|
||||
|
||||
if template is None:
|
||||
template = CONF.pxe.pxe_config_template
|
||||
template = deploy_utils.get_pxe_config_template(task.node)
|
||||
|
||||
_ensure_config_dirs_exist(task.node.uuid)
|
||||
|
||||
@ -294,10 +294,7 @@ def dhcp_options_for_instance(task):
|
||||
"""
|
||||
dhcp_opts = []
|
||||
|
||||
if deploy_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
|
||||
boot_file = deploy_utils.get_pxe_boot_file(task.node)
|
||||
|
||||
if CONF.pxe.ipxe_enabled:
|
||||
script_name = os.path.basename(CONF.pxe.ipxe_boot_script)
|
||||
|
@ -58,6 +58,12 @@ opts = [
|
||||
'drivers/modules/pxe_grub_config.template'),
|
||||
help=_('On ironic-conductor node, template file for PXE '
|
||||
'configuration for UEFI boot loader.')),
|
||||
cfg.DictOpt('pxe_config_template_by_arch',
|
||||
default={},
|
||||
help=_('On ironic-conductor node, template file for PXE '
|
||||
'configuration per node architecture. '
|
||||
'For example: '
|
||||
'aarch64:/opt/share/grubaa64_pxe_config.template')),
|
||||
cfg.StrOpt('tftp_server',
|
||||
default='$my_ip',
|
||||
help=_("IP address of ironic-conductor node's TFTP server.")),
|
||||
@ -71,14 +77,16 @@ opts = [
|
||||
help=_('On ironic-conductor node, directory where master TFTP '
|
||||
'images are stored on disk. '
|
||||
'Setting to <None> disables image caching.')),
|
||||
# NOTE(dekehn): Additional boot files options may be created in the event
|
||||
# other architectures require different boot files.
|
||||
cfg.StrOpt('pxe_bootfile_name',
|
||||
default='pxelinux.0',
|
||||
help=_('Bootfile DHCP parameter.')),
|
||||
cfg.StrOpt('uefi_pxe_bootfile_name',
|
||||
default='bootx64.efi',
|
||||
help=_('Bootfile DHCP parameter for UEFI boot mode.')),
|
||||
cfg.DictOpt('pxe_bootfile_name_by_arch',
|
||||
default={},
|
||||
help=_('Bootfile DHCP parameter per node architecture. '
|
||||
'For example: aarch64:grubaa64.efi')),
|
||||
cfg.BoolOpt('ipxe_enabled',
|
||||
default=False,
|
||||
help=_('Enable iPXE boot.')),
|
||||
|
@ -819,6 +819,48 @@ def get_boot_mode_for_deploy(node):
|
||||
return boot_mode.lower() if boot_mode else boot_mode
|
||||
|
||||
|
||||
def get_pxe_boot_file(node):
|
||||
"""Return the PXE boot file name requested for deploy.
|
||||
|
||||
This method returns PXE boot file name to be used for deploy.
|
||||
Architecture specific boot file is searched first. BIOS/UEFI
|
||||
boot file is used if no valid architecture specific file found.
|
||||
|
||||
:param node: A single Node.
|
||||
:returns: The PXE boot file name.
|
||||
"""
|
||||
cpu_arch = node.properties.get('cpu_arch')
|
||||
boot_file = CONF.pxe.pxe_bootfile_name_by_arch.get(cpu_arch)
|
||||
if boot_file is None:
|
||||
if get_boot_mode_for_deploy(node) == 'uefi':
|
||||
boot_file = CONF.pxe.uefi_pxe_bootfile_name
|
||||
else:
|
||||
boot_file = CONF.pxe.pxe_bootfile_name
|
||||
|
||||
return boot_file
|
||||
|
||||
|
||||
def get_pxe_config_template(node):
|
||||
"""Return the PXE config template file name requested for deploy.
|
||||
|
||||
This method returns PXE config template file to be used for deploy.
|
||||
Architecture specific template file is searched first. BIOS/UEFI
|
||||
template file is used if no valid architecture specific file found.
|
||||
|
||||
:param node: A single Node.
|
||||
:returns: The PXE config template file name.
|
||||
"""
|
||||
cpu_arch = node.properties.get('cpu_arch')
|
||||
config_template = CONF.pxe.pxe_config_template_by_arch.get(cpu_arch)
|
||||
if config_template is None:
|
||||
if get_boot_mode_for_deploy(node) == 'uefi':
|
||||
config_template = CONF.pxe.uefi_pxe_config_template
|
||||
else:
|
||||
config_template = CONF.pxe.pxe_config_template
|
||||
|
||||
return config_template
|
||||
|
||||
|
||||
def validate_capabilities(node):
|
||||
"""Validates that specified supported capabilities have valid value
|
||||
|
||||
|
@ -399,10 +399,7 @@ class PXEBoot(base.BootInterface):
|
||||
pxe_options = _build_pxe_config_options(task, pxe_info)
|
||||
pxe_options.update(ramdisk_params)
|
||||
|
||||
if deploy_utils.get_boot_mode_for_deploy(node) == 'uefi':
|
||||
pxe_config_template = CONF.pxe.uefi_pxe_config_template
|
||||
else:
|
||||
pxe_config_template = CONF.pxe.pxe_config_template
|
||||
pxe_config_template = deploy_utils.get_pxe_config_template(node)
|
||||
|
||||
pxe_utils.create_pxe_config(task, pxe_options,
|
||||
pxe_config_template)
|
||||
|
@ -1281,6 +1281,95 @@ class SwitchPxeConfigTestCase(tests_base.TestCase):
|
||||
self.assertEqual(_IPXECONF_BOOT_WHOLE_DISK, pxeconf)
|
||||
|
||||
|
||||
class GetPxeBootConfigTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GetPxeBootConfigTestCase, self).setUp()
|
||||
self.node = obj_utils.get_test_node(self.context, driver='fake')
|
||||
self.config(pxe_bootfile_name='bios-bootfile', group='pxe')
|
||||
self.config(uefi_pxe_bootfile_name='uefi-bootfile', group='pxe')
|
||||
self.config(pxe_config_template='bios-template', group='pxe')
|
||||
self.config(uefi_pxe_config_template='uefi-template', group='pxe')
|
||||
self.bootfile_by_arch = {'aarch64': 'aarch64-bootfile',
|
||||
'ppc64': 'ppc64-bootfile'}
|
||||
self.template_by_arch = {'aarch64': 'aarch64-template',
|
||||
'ppc64': 'ppc64-template'}
|
||||
|
||||
def test_get_pxe_boot_file_bios_without_by_arch(self):
|
||||
properties = {'cpu_arch': 'x86', 'capabilities': 'boot_mode:bios'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_bootfile_name_by_arch={}, group='pxe')
|
||||
result = utils.get_pxe_boot_file(self.node)
|
||||
self.assertEqual('bios-bootfile', result)
|
||||
|
||||
def test_get_pxe_config_template_bios_without_by_arch(self):
|
||||
properties = {'cpu_arch': 'x86', 'capabilities': 'boot_mode:bios'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_config_template_by_arch={}, group='pxe')
|
||||
result = utils.get_pxe_config_template(self.node)
|
||||
self.assertEqual('bios-template', result)
|
||||
|
||||
def test_get_pxe_boot_file_uefi_without_by_arch(self):
|
||||
properties = {'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_bootfile_name_by_arch={}, group='pxe')
|
||||
result = utils.get_pxe_boot_file(self.node)
|
||||
self.assertEqual('uefi-bootfile', result)
|
||||
|
||||
def test_get_pxe_config_template_uefi_without_by_arch(self):
|
||||
properties = {'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_config_template_by_arch={}, group='pxe')
|
||||
result = utils.get_pxe_config_template(self.node)
|
||||
self.assertEqual('uefi-template', result)
|
||||
|
||||
def test_get_pxe_boot_file_cpu_not_in_by_arch(self):
|
||||
properties = {'cpu_arch': 'x86', 'capabilities': 'boot_mode:bios'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_bootfile_name_by_arch=self.bootfile_by_arch,
|
||||
group='pxe')
|
||||
result = utils.get_pxe_boot_file(self.node)
|
||||
self.assertEqual('bios-bootfile', result)
|
||||
|
||||
def test_get_pxe_config_template_cpu_not_in_by_arch(self):
|
||||
properties = {'cpu_arch': 'x86', 'capabilities': 'boot_mode:bios'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_config_template_by_arch=self.template_by_arch,
|
||||
group='pxe')
|
||||
result = utils.get_pxe_config_template(self.node)
|
||||
self.assertEqual('bios-template', result)
|
||||
|
||||
def test_get_pxe_boot_file_cpu_in_by_arch(self):
|
||||
properties = {'cpu_arch': 'aarch64', 'capabilities': 'boot_mode:uefi'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_bootfile_name_by_arch=self.bootfile_by_arch,
|
||||
group='pxe')
|
||||
result = utils.get_pxe_boot_file(self.node)
|
||||
self.assertEqual('aarch64-bootfile', result)
|
||||
|
||||
def test_get_pxe_config_template_cpu_in_by_arch(self):
|
||||
properties = {'cpu_arch': 'aarch64', 'capabilities': 'boot_mode:uefi'}
|
||||
self.node.properties = properties
|
||||
self.config(pxe_config_template_by_arch=self.template_by_arch,
|
||||
group='pxe')
|
||||
result = utils.get_pxe_config_template(self.node)
|
||||
self.assertEqual('aarch64-template', result)
|
||||
|
||||
def test_get_pxe_boot_file_emtpy_property(self):
|
||||
self.node.properties = {}
|
||||
self.config(pxe_bootfile_name_by_arch=self.bootfile_by_arch,
|
||||
group='pxe')
|
||||
result = utils.get_pxe_boot_file(self.node)
|
||||
self.assertEqual('bios-bootfile', result)
|
||||
|
||||
def test_get_pxe_config_template_emtpy_property(self):
|
||||
self.node.properties = {}
|
||||
self.config(pxe_config_template_by_arch=self.template_by_arch,
|
||||
group='pxe')
|
||||
result = utils.get_pxe_config_template(self.node)
|
||||
self.assertEqual('bios-template', result)
|
||||
|
||||
|
||||
@mock.patch('time.sleep', lambda sec: None)
|
||||
class OtherFunctionTestCase(db_base.DbTestCase):
|
||||
|
||||
|
13
releasenotes/notes/multi-arch-deploy-bcf840107fc94bef.yaml
Normal file
13
releasenotes/notes/multi-arch-deploy-bcf840107fc94bef.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
features:
|
||||
- Support multi architecture deployment. E.g., to
|
||||
deploy x86_64, aarch64 servers by one ironic conductor.
|
||||
Two new config options, ``pxe_config_template_by_arch``
|
||||
and ``pxe_bootfile_name_by_arch``, are introduced to
|
||||
support multi architecture deployment. They are
|
||||
dictionary values to hold pxe config templates and
|
||||
boot files for multiple architectures, with cpu_arch
|
||||
property per node as the key. If cpu_arch is not found
|
||||
in dictionary, options ``pxe_config_template``,
|
||||
``pxe_bootfile_name``, ``uefi_pxe_config_template``,
|
||||
``uefi_pxe_bootfile_name`` will be used as usual.
|
Loading…
Reference in New Issue
Block a user