Merge "Support multi arch deployment"

This commit is contained in:
Jenkins 2016-10-13 08:58:43 +00:00 committed by Gerrit Code Review
commit cde866db2d
8 changed files with 216 additions and 12 deletions

View File

@ -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

View File

@ -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
--------------------------------

View File

@ -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)

View File

@ -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.')),

View File

@ -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

View File

@ -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)

View File

@ -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):

View 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.