diff --git a/ironic/drivers/modules/ipxe.py b/ironic/drivers/modules/ipxe.py index fc1ef59970..0c0ddb3a75 100644 --- a/ironic/drivers/modules/ipxe.py +++ b/ironic/drivers/modules/ipxe.py @@ -17,19 +17,13 @@ iPXE Boot Interface from ironic_lib import metrics_utils from oslo_log import log as logging -from oslo_utils import strutils -from ironic.common import boot_devices -from ironic.common import dhcp_factory from ironic.common import exception from ironic.common.glance_service import service_utils from ironic.common.i18n import _ from ironic.common import pxe_utils -from ironic.common import states -from ironic.conductor import utils as manager_utils from ironic.conf import CONF from ironic.drivers import base -from ironic.drivers.modules import boot_mode_utils from ironic.drivers.modules import deploy_utils from ironic.drivers.modules import pxe from ironic.drivers.modules import pxe_base @@ -128,203 +122,3 @@ class iPXEBoot(pxe_base.PXEBaseMixin, base.BootInterface): # Fall back to non-managed in-band inspection raise exception.UnsupportedDriverExtension( driver=task.node.driver, extension='inspection') - - @METRICS.timer('iPXEBoot.prepare_ramdisk') - def prepare_ramdisk(self, task, ramdisk_params): - """Prepares the boot of Ironic ramdisk using PXE. - - This method prepares the boot of the deploy or rescue kernel/ramdisk - after reading relevant information from the node's driver_info and - instance_info. - - :param task: a task from TaskManager. - :param ramdisk_params: the parameters to be passed to the ramdisk. - pxe driver passes these parameters as kernel command-line - arguments. - :returns: None - :raises: MissingParameterValue, if some information is missing in - node's driver_info or instance_info. - :raises: InvalidParameterValue, if some information provided is - invalid. - :raises: IronicException, if some power or set boot boot device - operation failed on the node. - """ - node = task.node - - # Label indicating a deploy or rescue operation being carried out on - # the node, 'deploy' or 'rescue'. Unless the node is in a rescue like - # state, the mode is set to 'deploy', indicating deploy operation is - # being carried out. - mode = deploy_utils.rescue_or_deploy_mode(node) - - # NOTE(mjturek): At this point, the ipxe boot script should - # already exist as it is created at startup time. However, we - # call the boot script create method here to assert its - # existence and handle the unlikely case that it wasn't created - # or was deleted. - pxe_utils.create_ipxe_boot_script() - - dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=True) - provider = dhcp_factory.DHCPFactory() - provider.update_dhcp(task, dhcp_opts) - - pxe_info = pxe_utils.get_image_info(node, mode=mode, - ipxe_enabled=True) - - # NODE: Try to validate and fetch instance images only - # if we are in DEPLOYING state. - if node.provision_state == states.DEPLOYING: - pxe_info.update( - pxe_utils.get_instance_image_info(task, ipxe_enabled=True)) - boot_mode_utils.sync_boot_mode(task) - - pxe_options = pxe_utils.build_pxe_config_options( - task, pxe_info, ipxe_enabled=True, ramdisk_params=ramdisk_params) - # TODO(dtantsur): backwards compability hack, remove in the V release - if ramdisk_params.get("ipa-api-url"): - pxe_options["ipa-api-url"] = ramdisk_params["ipa-api-url"] - - pxe_config_template = deploy_utils.get_pxe_config_template(node) - - pxe_utils.create_pxe_config(task, pxe_options, - pxe_config_template, - ipxe_enabled=True) - persistent = False - value = node.driver_info.get('force_persistent_boot_device', - 'Default') - if value in {'Always', 'Default', 'Never'}: - if value == 'Always': - persistent = True - else: - persistent = strutils.bool_from_string(value, False) - manager_utils.node_set_boot_device(task, boot_devices.PXE, - persistent=persistent) - - if CONF.pxe.ipxe_use_swift: - kernel_label = '%s_kernel' % mode - ramdisk_label = '%s_ramdisk' % mode - pxe_info.pop(kernel_label, None) - pxe_info.pop(ramdisk_label, None) - - if pxe_info: - pxe_utils.cache_ramdisk_kernel(task, pxe_info, ipxe_enabled=True) - - LOG.debug('Ramdisk iPXE boot for node %(node)s has been prepared ' - 'with kernel params %(params)s', - {'node': node.uuid, 'params': pxe_options}) - - @METRICS.timer('iPXEBoot.prepare_instance') - def prepare_instance(self, task): - """Prepares the boot of instance. - - This method prepares the boot of the instance after reading - relevant information from the node's instance_info. In case of netboot, - it updates the dhcp entries and switches the PXE config. In case of - localboot, it cleans up the PXE config. - - :param task: a task from TaskManager. - :returns: None - """ - boot_mode_utils.sync_boot_mode(task) - - node = task.node - boot_option = deploy_utils.get_boot_option(node) - boot_device = None - instance_image_info = {} - - if boot_option == "ramdisk": - instance_image_info = pxe_utils.get_instance_image_info( - task, ipxe_enabled=True) - pxe_utils.cache_ramdisk_kernel(task, instance_image_info, - ipxe_enabled=True) - - if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk": - pxe_utils.prepare_instance_pxe_config( - task, instance_image_info, - iscsi_boot=deploy_utils.is_iscsi_boot(task), - ramdisk_boot=(boot_option == "ramdisk"), - ipxe_enabled=True) - boot_device = boot_devices.PXE - - elif boot_option != "local": - if task.driver.storage.should_write_image(task): - # Make sure that the instance kernel/ramdisk is cached. - # This is for the takeover scenario for active nodes. - instance_image_info = pxe_utils.get_instance_image_info( - task, ipxe_enabled=True) - pxe_utils.cache_ramdisk_kernel(task, instance_image_info, - ipxe_enabled=True) - - # If it's going to PXE boot we need to update the DHCP server - dhcp_opts = pxe_utils.dhcp_options_for_instance(task, - ipxe_enabled=True) - provider = dhcp_factory.DHCPFactory() - provider.update_dhcp(task, dhcp_opts) - - iwdi = task.node.driver_internal_info.get('is_whole_disk_image') - try: - root_uuid_or_disk_id = task.node.driver_internal_info[ - 'root_uuid_or_disk_id' - ] - except KeyError: - if not task.driver.storage.should_write_image(task): - pass - elif not iwdi: - LOG.warning("The UUID for the root partition can't be " - "found, unable to switch the pxe config from " - "deployment mode to service (boot) mode for " - "node %(node)s", {"node": task.node.uuid}) - else: - LOG.warning("The disk id for the whole disk image can't " - "be found, unable to switch the pxe config " - "from deployment mode to service (boot) mode " - "for node %(node)s. Booting the instance " - "from disk.", {"node": task.node.uuid}) - pxe_utils.clean_up_pxe_config(task, ipxe_enabled=True) - boot_device = boot_devices.DISK - else: - pxe_utils.build_service_pxe_config(task, instance_image_info, - root_uuid_or_disk_id, - ipxe_enabled=True) - boot_device = boot_devices.PXE - else: - # If it's going to boot from the local disk, we don't need - # PXE config files. They still need to be generated as part - # of the prepare() because the deployment does PXE boot the - # deploy ramdisk - pxe_utils.clean_up_pxe_config(task, ipxe_enabled=True) - boot_device = boot_devices.DISK - - # NOTE(pas-ha) do not re-set boot device on ACTIVE nodes - # during takeover - if boot_device and task.node.provision_state != states.ACTIVE: - persistent = True - if node.driver_info.get('force_persistent_boot_device', - 'Default') == 'Never': - persistent = False - manager_utils.node_set_boot_device(task, boot_device, - persistent=persistent) - - @METRICS.timer('iPXEBoot.clean_up_instance') - def clean_up_instance(self, task): - """Cleans up the boot of instance. - - This method cleans up the environment that was setup for booting - the instance. It unlinks the instance kernel/ramdisk in node's - directory in tftproot and removes the PXE config. - - :param task: a task from TaskManager. - :returns: None - """ - node = task.node - - try: - images_info = pxe_utils.get_instance_image_info(task, - ipxe_enabled=True) - except exception.MissingParameterValue as e: - LOG.warning('Could not get instance image info ' - 'to clean up images for node %(node)s: %(err)s', - {'node': node.uuid, 'err': e}) - else: - pxe_utils.clean_up_pxe_env(task, images_info, ipxe_enabled=True) diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py index d783443cd6..268ba2c2d1 100644 --- a/ironic/drivers/modules/pxe.py +++ b/ironic/drivers/modules/pxe.py @@ -18,8 +18,6 @@ PXE Boot Interface from ironic_lib import metrics_utils from oslo_log import log as logging -from ironic.common import boot_devices -from ironic.common import dhcp_factory from ironic.common import exception from ironic.common.glance_service import service_utils from ironic.common.i18n import _ @@ -27,10 +25,8 @@ from ironic.common import pxe_utils from ironic.common import states from ironic.conductor import task_manager from ironic.conductor import utils as manager_utils -from ironic.conf import CONF from ironic.drivers import base from ironic.drivers.modules import agent -from ironic.drivers.modules import boot_mode_utils from ironic.drivers.modules import deploy_utils from ironic.drivers.modules import pxe_base from ironic.drivers import utils as driver_utils @@ -54,12 +50,6 @@ class PXEBoot(pxe_base.PXEBaseMixin, base.BootInterface): # interface. capabilities = ['iscsi_volume_boot', 'ramdisk_boot', 'pxe_boot'] - def __init__(self): - # TODO(TheJulia): Once the pxe/ipxe interfaces split is complete, - # this can be removed. - if CONF.pxe.ipxe_enabled: - pxe_utils.create_ipxe_boot_script() - def _validate_common(self, task): node = task.node @@ -68,15 +58,6 @@ class PXEBoot(pxe_base.PXEBaseMixin, base.BootInterface): _("Node %s does not have any port associated with it.") % node.uuid) - # TODO(TheJulia): Once ipxe support is remove from the pxe - # interface, this can be removed. - if CONF.pxe.ipxe_enabled: - if (not CONF.deploy.http_url - or not CONF.deploy.http_root): - raise exception.MissingParameterValue(_( - "iPXE boot is enabled but no HTTP URL or HTTP " - "root was specified.")) - # Check the trusted_boot capabilities value. deploy_utils.validate_capabilities(node) if deploy_utils.is_trusted_boot_requested(node): @@ -132,200 +113,6 @@ class PXEBoot(pxe_base.PXEBaseMixin, base.BootInterface): raise exception.UnsupportedDriverExtension( driver=task.node.driver, extension='inspection') - @METRICS.timer('PXEBoot.prepare_ramdisk') - def prepare_ramdisk(self, task, ramdisk_params): - """Prepares the boot of Ironic ramdisk using PXE. - - This method prepares the boot of the deploy or rescue kernel/ramdisk - after reading relevant information from the node's driver_info and - instance_info. - - :param task: a task from TaskManager. - :param ramdisk_params: the parameters to be passed to the ramdisk. - pxe driver passes these parameters as kernel command-line - arguments. - :returns: None - :raises: MissingParameterValue, if some information is missing in - node's driver_info or instance_info. - :raises: InvalidParameterValue, if some information provided is - invalid. - :raises: IronicException, if some power or set boot boot device - operation failed on the node. - """ - node = task.node - - # Label indicating a deploy or rescue operation being carried out on - # the node, 'deploy' or 'rescue'. Unless the node is in a rescue like - # state, the mode is set to 'deploy', indicating deploy operation is - # being carried out. - mode = deploy_utils.rescue_or_deploy_mode(node) - ipxe_enabled = CONF.pxe.ipxe_enabled - if ipxe_enabled: - # NOTE(mjturek): At this point, the ipxe boot script should - # already exist as it is created at startup time. However, we - # call the boot script create method here to assert its - # existence and handle the unlikely case that it wasn't created - # or was deleted. - pxe_utils.create_ipxe_boot_script() - - dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=ipxe_enabled) - provider = dhcp_factory.DHCPFactory() - provider.update_dhcp(task, dhcp_opts) - - pxe_info = pxe_utils.get_image_info(node, mode=mode) - - # NODE: Try to validate and fetch instance images only - # if we are in DEPLOYING state. - if node.provision_state == states.DEPLOYING: - pxe_info.update(pxe_utils.get_instance_image_info(task)) - boot_mode_utils.sync_boot_mode(task) - - pxe_options = pxe_utils.build_pxe_config_options( - task, pxe_info, ipxe_enabled=ipxe_enabled, - ramdisk_params=ramdisk_params) - # TODO(dtantsur): backwards compability hack, remove in the V release - if ramdisk_params.get("ipa-api-url"): - pxe_options["ipa-api-url"] = ramdisk_params["ipa-api-url"] - - pxe_config_template = deploy_utils.get_pxe_config_template(node) - - pxe_utils.create_pxe_config(task, pxe_options, - pxe_config_template, - ipxe_enabled=CONF.pxe.ipxe_enabled) - - persistent = self._persistent_ramdisk_boot(node) - manager_utils.node_set_boot_device(task, boot_devices.PXE, - persistent=persistent) - - if CONF.pxe.ipxe_enabled and CONF.pxe.ipxe_use_swift: - kernel_label = '%s_kernel' % mode - ramdisk_label = '%s_ramdisk' % mode - pxe_info.pop(kernel_label, None) - pxe_info.pop(ramdisk_label, None) - - if pxe_info: - pxe_utils.cache_ramdisk_kernel(task, pxe_info, - ipxe_enabled=CONF.pxe.ipxe_enabled) - LOG.debug('Ramdisk PXE boot for node %(node)s has been prepared ' - 'with kernel params %(params)s', - {'node': node.uuid, 'params': pxe_options}) - - @METRICS.timer('PXEBoot.prepare_instance') - def prepare_instance(self, task): - """Prepares the boot of instance. - - This method prepares the boot of the instance after reading - relevant information from the node's instance_info. In case of netboot, - it updates the dhcp entries and switches the PXE config. In case of - localboot, it cleans up the PXE config. - - :param task: a task from TaskManager. - :returns: None - """ - ipxe_enabled = CONF.pxe.ipxe_enabled - boot_mode_utils.sync_boot_mode(task) - - node = task.node - boot_option = deploy_utils.get_boot_option(node) - boot_device = None - instance_image_info = {} - if boot_option == "ramdisk": - instance_image_info = pxe_utils.get_instance_image_info(task) - pxe_utils.cache_ramdisk_kernel(task, instance_image_info, - ipxe_enabled=CONF.pxe.ipxe_enabled) - - if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk": - pxe_utils.prepare_instance_pxe_config( - task, instance_image_info, - iscsi_boot=deploy_utils.is_iscsi_boot(task), - ramdisk_boot=(boot_option == "ramdisk"), - ipxe_enabled=CONF.pxe.ipxe_enabled) - boot_device = boot_devices.PXE - - elif boot_option != "local": - if task.driver.storage.should_write_image(task): - # Make sure that the instance kernel/ramdisk is cached. - # This is for the takeover scenario for active nodes. - instance_image_info = pxe_utils.get_instance_image_info(task) - pxe_utils.cache_ramdisk_kernel( - task, instance_image_info, - ipxe_enabled=CONF.pxe.ipxe_enabled) - - # If it's going to PXE boot we need to update the DHCP server - dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled) - provider = dhcp_factory.DHCPFactory() - provider.update_dhcp(task, dhcp_opts) - - iwdi = task.node.driver_internal_info.get('is_whole_disk_image') - try: - root_uuid_or_disk_id = task.node.driver_internal_info[ - 'root_uuid_or_disk_id' - ] - except KeyError: - if not task.driver.storage.should_write_image(task): - pass - elif not iwdi: - LOG.warning("The UUID for the root partition can't be " - "found, unable to switch the pxe config from " - "deployment mode to service (boot) mode for " - "node %(node)s", {"node": task.node.uuid}) - else: - LOG.warning("The disk id for the whole disk image can't " - "be found, unable to switch the pxe config " - "from deployment mode to service (boot) mode " - "for node %(node)s. Booting the instance " - "from disk.", {"node": task.node.uuid}) - pxe_utils.clean_up_pxe_config( - task, ipxe_enabled=CONF.pxe.ipxe_enabled) - boot_device = boot_devices.DISK - else: - pxe_utils.build_service_pxe_config(task, instance_image_info, - root_uuid_or_disk_id, - ipxe_enabled=ipxe_enabled) - boot_device = boot_devices.PXE - else: - # If it's going to boot from the local disk, we don't need - # PXE config files. They still need to be generated as part - # of the prepare() because the deployment does PXE boot the - # deploy ramdisk - pxe_utils.clean_up_pxe_config( - task, ipxe_enabled=CONF.pxe.ipxe_enabled) - boot_device = boot_devices.DISK - - # NOTE(pas-ha) do not re-set boot device on ACTIVE nodes - # during takeover - if boot_device and task.node.provision_state != states.ACTIVE: - persistent = True - if node.driver_info.get('force_persistent_boot_device', - 'Default') == 'Never': - persistent = False - manager_utils.node_set_boot_device(task, boot_device, - persistent=persistent) - - @METRICS.timer('PXEBoot.clean_up_instance') - def clean_up_instance(self, task): - """Cleans up the boot of instance. - - This method cleans up the environment that was setup for booting - the instance. It unlinks the instance kernel/ramdisk in node's - directory in tftproot and removes the PXE config. - - :param task: a task from TaskManager. - :returns: None - """ - node = task.node - - try: - images_info = pxe_utils.get_instance_image_info(task) - except exception.MissingParameterValue as e: - LOG.warning('Could not get instance image info ' - 'to clean up images for node %(node)s: %(err)s', - {'node': node.uuid, 'err': e}) - else: - pxe_utils.clean_up_pxe_env(task, images_info) - class PXERamdiskDeploy(agent.AgentDeploy): diff --git a/ironic/drivers/modules/pxe_base.py b/ironic/drivers/modules/pxe_base.py index 3f0bb0d193..600781af20 100644 --- a/ironic/drivers/modules/pxe_base.py +++ b/ironic/drivers/modules/pxe_base.py @@ -20,12 +20,14 @@ from oslo_log import log as logging from oslo_utils import strutils from ironic.common import boot_devices +from ironic.common import dhcp_factory from ironic.common import exception from ironic.common.i18n import _ from ironic.common import pxe_utils from ironic.common import states from ironic.conductor import task_manager from ironic.conductor import utils as manager_utils +from ironic.drivers.modules import boot_mode_utils from ironic.drivers.modules import deploy_utils @@ -105,6 +107,205 @@ class PXEBaseMixin(object): pxe_utils.clean_up_pxe_env( task, images_info, ipxe_enabled=self.ipxe_enabled) + @METRICS.timer('PXEBaseMixin.clean_up_instance') + def clean_up_instance(self, task): + """Cleans up the boot of instance. + + This method cleans up the environment that was setup for booting + the instance. It unlinks the instance kernel/ramdisk in node's + directory in tftproot and removes the PXE config. + + :param task: a task from TaskManager. + :returns: None + """ + node = task.node + + try: + images_info = pxe_utils.get_instance_image_info( + task, ipxe_enabled=self.ipxe_enabled) + except exception.MissingParameterValue as e: + LOG.warning('Could not get instance image info ' + 'to clean up images for node %(node)s: %(err)s', + {'node': node.uuid, 'err': e}) + else: + pxe_utils.clean_up_pxe_env(task, images_info, + ipxe_enabled=self.ipxe_enabled) + + @METRICS.timer('PXEBaseMixin.prepare_ramdisk') + def prepare_ramdisk(self, task, ramdisk_params): + """Prepares the boot of Ironic ramdisk using PXE. + + This method prepares the boot of the deploy or rescue kernel/ramdisk + after reading relevant information from the node's driver_info and + instance_info. + + :param task: a task from TaskManager. + :param ramdisk_params: the parameters to be passed to the ramdisk. + pxe driver passes these parameters as kernel command-line + arguments. + :returns: None + :raises: MissingParameterValue, if some information is missing in + node's driver_info or instance_info. + :raises: InvalidParameterValue, if some information provided is + invalid. + :raises: IronicException, if some power or set boot boot device + operation failed on the node. + """ + node = task.node + + # Label indicating a deploy or rescue operation being carried out on + # the node, 'deploy' or 'rescue'. Unless the node is in a rescue like + # state, the mode is set to 'deploy', indicating deploy operation is + # being carried out. + mode = deploy_utils.rescue_or_deploy_mode(node) + + if self.ipxe_enabled: + # NOTE(mjturek): At this point, the ipxe boot script should + # already exist as it is created at startup time. However, we + # call the boot script create method here to assert its + # existence and handle the unlikely case that it wasn't created + # or was deleted. + pxe_utils.create_ipxe_boot_script() + + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=self.ipxe_enabled) + provider = dhcp_factory.DHCPFactory() + provider.update_dhcp(task, dhcp_opts) + + pxe_info = pxe_utils.get_image_info(node, mode=mode, + ipxe_enabled=self.ipxe_enabled) + + # NODE: Try to validate and fetch instance images only + # if we are in DEPLOYING state. + if node.provision_state == states.DEPLOYING: + pxe_info.update( + pxe_utils.get_instance_image_info( + task, ipxe_enabled=self.ipxe_enabled)) + boot_mode_utils.sync_boot_mode(task) + + pxe_options = pxe_utils.build_pxe_config_options( + task, pxe_info, ipxe_enabled=self.ipxe_enabled, + ramdisk_params=ramdisk_params) + # TODO(dtantsur): backwards compability hack, remove in the V release + if ramdisk_params.get("ipa-api-url"): + pxe_options["ipa-api-url"] = ramdisk_params["ipa-api-url"] + + pxe_config_template = deploy_utils.get_pxe_config_template(node) + + pxe_utils.create_pxe_config(task, pxe_options, + pxe_config_template, + ipxe_enabled=self.ipxe_enabled) + persistent = self._persistent_ramdisk_boot(node) + manager_utils.node_set_boot_device(task, boot_devices.PXE, + persistent=persistent) + + if self.ipxe_enabled and CONF.pxe.ipxe_use_swift: + kernel_label = '%s_kernel' % mode + ramdisk_label = '%s_ramdisk' % mode + pxe_info.pop(kernel_label, None) + pxe_info.pop(ramdisk_label, None) + + if pxe_info: + pxe_utils.cache_ramdisk_kernel(task, pxe_info, + ipxe_enabled=self.ipxe_enabled) + + LOG.debug('Ramdisk (i)PXE boot for node %(node)s has been prepared ' + 'with kernel params %(params)s', + {'node': node.uuid, 'params': pxe_options}) + + @METRICS.timer('PXEBaseMixin.prepare_instance') + def prepare_instance(self, task): + """Prepares the boot of instance. + + This method prepares the boot of the instance after reading + relevant information from the node's instance_info. In case of netboot, + it updates the dhcp entries and switches the PXE config. In case of + localboot, it cleans up the PXE config. + + :param task: a task from TaskManager. + :returns: None + """ + boot_mode_utils.sync_boot_mode(task) + + node = task.node + boot_option = deploy_utils.get_boot_option(node) + boot_device = None + instance_image_info = {} + + if boot_option == "ramdisk": + instance_image_info = pxe_utils.get_instance_image_info( + task, ipxe_enabled=self.ipxe_enabled) + pxe_utils.cache_ramdisk_kernel(task, instance_image_info, + ipxe_enabled=self.ipxe_enabled) + + if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk": + pxe_utils.prepare_instance_pxe_config( + task, instance_image_info, + iscsi_boot=deploy_utils.is_iscsi_boot(task), + ramdisk_boot=(boot_option == "ramdisk"), + ipxe_enabled=self.ipxe_enabled) + boot_device = boot_devices.PXE + + elif boot_option != "local": + if task.driver.storage.should_write_image(task): + # Make sure that the instance kernel/ramdisk is cached. + # This is for the takeover scenario for active nodes. + instance_image_info = pxe_utils.get_instance_image_info( + task, ipxe_enabled=self.ipxe_enabled) + pxe_utils.cache_ramdisk_kernel(task, instance_image_info, + ipxe_enabled=self.ipxe_enabled) + + # If it's going to PXE boot we need to update the DHCP server + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=self.ipxe_enabled) + provider = dhcp_factory.DHCPFactory() + provider.update_dhcp(task, dhcp_opts) + + iwdi = task.node.driver_internal_info.get('is_whole_disk_image') + try: + root_uuid_or_disk_id = task.node.driver_internal_info[ + 'root_uuid_or_disk_id' + ] + except KeyError: + if not task.driver.storage.should_write_image(task): + pass + elif not iwdi: + LOG.warning("The UUID for the root partition can't be " + "found, unable to switch the pxe config from " + "deployment mode to service (boot) mode for " + "node %(node)s", {"node": task.node.uuid}) + else: + LOG.warning("The disk id for the whole disk image can't " + "be found, unable to switch the pxe config " + "from deployment mode to service (boot) mode " + "for node %(node)s. Booting the instance " + "from disk.", {"node": task.node.uuid}) + pxe_utils.clean_up_pxe_config( + task, ipxe_enabled=self.ipxe_enabled) + boot_device = boot_devices.DISK + else: + pxe_utils.build_service_pxe_config( + task, instance_image_info, root_uuid_or_disk_id, + ipxe_enabled=self.ipxe_enabled) + boot_device = boot_devices.PXE + else: + # If it's going to boot from the local disk, we don't need + # PXE config files. They still need to be generated as part + # of the prepare() because the deployment does PXE boot the + # deploy ramdisk + pxe_utils.clean_up_pxe_config(task, ipxe_enabled=self.ipxe_enabled) + boot_device = boot_devices.DISK + + # NOTE(pas-ha) do not re-set boot device on ACTIVE nodes + # during takeover + if boot_device and task.node.provision_state != states.ACTIVE: + persistent = True + if node.driver_info.get('force_persistent_boot_device', + 'Default') == 'Never': + persistent = False + manager_utils.node_set_boot_device(task, boot_device, + persistent=persistent) + @METRICS.timer('PXEBaseMixin.validate_rescue') def validate_rescue(self, task): """Validate that the node has required properties for rescue. diff --git a/ironic/tests/unit/drivers/modules/test_ipxe.py b/ironic/tests/unit/drivers/modules/test_ipxe.py index f81be61964..1273bbef43 100644 --- a/ironic/tests/unit/drivers/modules/test_ipxe.py +++ b/ironic/tests/unit/drivers/modules/test_ipxe.py @@ -36,6 +36,7 @@ from ironic.drivers import base as drivers_base from ironic.drivers.modules import agent_base from ironic.drivers.modules import deploy_utils from ironic.drivers.modules import ipxe +from ironic.drivers.modules import pxe_base from ironic.drivers.modules.storage import noop as noop_storage from ironic.tests.unit.db import base as db_base from ironic.tests.unit.db import utils as db_utils @@ -730,7 +731,7 @@ class iPXEBootTestCase(db_base.DbTestCase): # NOTE(TheJulia): The log mock below is attached to the iPXE interface # which directly logs the warning that is being checked for. - @mock.patch.object(ipxe.LOG, 'warning', autospec=True) + @mock.patch.object(pxe_base.LOG, 'warning', autospec=True) @mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True) @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) @mock.patch.object(dhcp_factory, 'DHCPFactory') diff --git a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py index 0e4a28f8cd..fb7c3f770b 100644 --- a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py +++ b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py @@ -1275,7 +1275,8 @@ class CleanUpFullFlowTestCase(db_base.DbTestCase): with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: task.driver.deploy.clean_up(task) - mock_get_instance_image_info.assert_called_with(task) + mock_get_instance_image_info.assert_called_with(task, + ipxe_enabled=False) mock_get_deploy_image_info.assert_called_with( task.node, mode='deploy', ipxe_enabled=False) set_dhcp_provider_mock.assert_called_once_with() diff --git a/ironic/tests/unit/drivers/modules/test_pxe.py b/ironic/tests/unit/drivers/modules/test_pxe.py index 3c13aa3da5..3534e50039 100644 --- a/ironic/tests/unit/drivers/modules/test_pxe.py +++ b/ironic/tests/unit/drivers/modules/test_pxe.py @@ -31,7 +31,6 @@ from ironic.common import exception from ironic.common.glance_service import image_service from ironic.common import pxe_utils from ironic.common import states -from ironic.common import utils as common_utils from ironic.conductor import task_manager from ironic.conductor import utils as manager_utils from ironic.drivers import base as drivers_base @@ -40,6 +39,7 @@ from ironic.drivers.modules import deploy_utils from ironic.drivers.modules import fake from ironic.drivers.modules import ipxe from ironic.drivers.modules import pxe +from ironic.drivers.modules import pxe_base from ironic.drivers.modules.storage import noop as noop_storage from ironic.tests.unit.db import base as db_base from ironic.tests.unit.db import utils as db_utils @@ -268,7 +268,9 @@ class PXEBootTestCase(db_base.DbTestCase): dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=CONF.pxe.ipxe_enabled) task.driver.boot.prepare_ramdisk(task, {'foo': 'bar'}) - mock_deploy_img_info.assert_called_once_with(task.node, mode=mode) + mock_deploy_img_info.assert_called_once_with(task.node, + mode=mode, + ipxe_enabled=False) provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) if self.node.provision_state == states.DEPLOYING: get_boot_mode_mock.assert_called_once_with(task) @@ -282,33 +284,35 @@ class PXEBootTestCase(db_base.DbTestCase): mock_cache_r_k.assert_called_once_with( task, {'kernel': 'b'}, - ipxe_enabled=CONF.pxe.ipxe_enabled) - mock_instance_img_info.assert_called_once_with(task) + ipxe_enabled=False) + mock_instance_img_info.assert_called_once_with( + task, ipxe_enabled=False) elif not cleaning and mode == 'deploy': mock_cache_r_k.assert_called_once_with( task, {'deploy_kernel': 'a', 'deploy_ramdisk': 'r', 'kernel': 'b'}, - ipxe_enabled=CONF.pxe.ipxe_enabled) - mock_instance_img_info.assert_called_once_with(task) + ipxe_enabled=False) + mock_instance_img_info.assert_called_once_with( + task, ipxe_enabled=False) elif mode == 'deploy': mock_cache_r_k.assert_called_once_with( task, {'deploy_kernel': 'a', 'deploy_ramdisk': 'r'}, - ipxe_enabled=CONF.pxe.ipxe_enabled) + ipxe_enabled=False) elif mode == 'rescue': mock_cache_r_k.assert_called_once_with( task, {'rescue_kernel': 'a', 'rescue_ramdisk': 'r'}, - ipxe_enabled=CONF.pxe.ipxe_enabled) + ipxe_enabled=False) if uefi: mock_pxe_config.assert_called_once_with( task, {}, CONF.pxe.uefi_pxe_config_template, - ipxe_enabled=CONF.pxe.ipxe_enabled) + ipxe_enabled=False) else: mock_pxe_config.assert_called_once_with( task, {}, CONF.pxe.pxe_config_template, - ipxe_enabled=CONF.pxe.ipxe_enabled) + ipxe_enabled=False) def test_prepare_ramdisk(self): self.node.provision_state = states.DEPLOYING @@ -403,93 +407,6 @@ class PXEBootTestCase(db_base.DbTestCase): self.node.save() self._test_prepare_ramdisk(uefi=True) - @mock.patch.object(os.path, 'isfile', lambda path: True) - @mock.patch.object(common_utils, 'file_has_content', lambda *args: False) - @mock.patch('ironic.common.utils.write_to_file', autospec=True) - @mock.patch('ironic.common.utils.render_template', autospec=True) - def test_prepare_ramdisk_ipxe_with_copy_file_different( - self, render_mock, write_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') - render_mock.return_value = 'foo' - self._test_prepare_ramdisk() - write_mock.assert_called_once_with( - os.path.join( - CONF.deploy.http_root, - os.path.basename(CONF.pxe.ipxe_boot_script)), - 'foo') - render_mock.assert_called_once_with( - CONF.pxe.ipxe_boot_script, - {'ipxe_for_mac_uri': 'pxelinux.cfg/'}) - - @mock.patch.object(os.path, 'isfile', lambda path: False) - @mock.patch('ironic.common.utils.file_has_content', autospec=True) - @mock.patch('ironic.common.utils.write_to_file', autospec=True) - @mock.patch('ironic.common.utils.render_template', autospec=True) - def test_prepare_ramdisk_ipxe_with_copy_no_file( - self, render_mock, write_mock, file_has_content_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') - render_mock.return_value = 'foo' - self._test_prepare_ramdisk() - self.assertFalse(file_has_content_mock.called) - write_mock.assert_called_once_with( - os.path.join( - CONF.deploy.http_root, - os.path.basename(CONF.pxe.ipxe_boot_script)), - 'foo') - render_mock.assert_called_once_with( - CONF.pxe.ipxe_boot_script, - {'ipxe_for_mac_uri': 'pxelinux.cfg/'}) - - @mock.patch.object(os.path, 'isfile', lambda path: True) - @mock.patch.object(common_utils, 'file_has_content', lambda *args: True) - @mock.patch('ironic.common.utils.write_to_file', autospec=True) - @mock.patch('ironic.common.utils.render_template', autospec=True) - def test_prepare_ramdisk_ipxe_without_copy( - self, render_mock, write_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') - self._test_prepare_ramdisk() - self.assertFalse(write_mock.called) - - @mock.patch.object(common_utils, 'render_template', lambda *args: 'foo') - @mock.patch('ironic.common.utils.write_to_file', autospec=True) - def test_prepare_ramdisk_ipxe_swift(self, write_mock): - self.node.provision_state = states.DEPLOYING - self.node.save() - self.config(group='pxe', ipxe_enabled=True) - self.config(group='pxe', ipxe_use_swift=True) - self.config(group='deploy', http_url='http://myserver') - self._test_prepare_ramdisk(ipxe_use_swift=True) - write_mock.assert_called_once_with( - os.path.join( - CONF.deploy.http_root, - os.path.basename(CONF.pxe.ipxe_boot_script)), - 'foo') - - @mock.patch.object(common_utils, 'render_template', lambda *args: 'foo') - @mock.patch('ironic.common.utils.write_to_file', autospec=True) - def test_prepare_ramdisk_ipxe_swift_whole_disk_image( - self, write_mock): - self.node.provision_state = states.DEPLOYING - self.node.save() - self.config(group='pxe', ipxe_enabled=True) - self.config(group='pxe', ipxe_use_swift=True) - self.config(group='deploy', http_url='http://myserver') - self._test_prepare_ramdisk(ipxe_use_swift=True, whole_disk_image=True) - write_mock.assert_called_once_with( - os.path.join( - CONF.deploy.http_root, - os.path.basename(CONF.pxe.ipxe_boot_script)), - 'foo') - def test_prepare_ramdisk_cleaning(self): self.node.provision_state = states.CLEANING self.node.save() @@ -635,7 +552,7 @@ class PXEBootTestCase(db_base.DbTestCase): get_image_info_mock.return_value = image_info with task_manager.acquire(self.context, self.node.uuid) as task: dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, ipxe_enabled=False) pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid) task.node.properties['capabilities'] = 'boot_mode:bios' @@ -646,9 +563,9 @@ class PXEBootTestCase(db_base.DbTestCase): task.driver.boot.prepare_instance(task) get_image_info_mock.assert_called_once_with( - task) + task, ipxe_enabled=False) cache_mock.assert_called_once_with( - task, image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, image_info, ipxe_enabled=False) provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) switch_pxe_config_mock.assert_called_once_with( pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50", @@ -688,9 +605,9 @@ class PXEBootTestCase(db_base.DbTestCase): task.driver.boot.prepare_instance(task) get_image_info_mock.assert_called_once_with( - task) + task, ipxe_enabled=False) cache_mock.assert_called_once_with( - task, image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, image_info, ipxe_enabled=False) provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) create_pxe_config_mock.assert_called_once_with( task, mock.ANY, CONF.pxe.pxe_config_template, @@ -716,20 +633,21 @@ class PXEBootTestCase(db_base.DbTestCase): get_image_info_mock.return_value = image_info with task_manager.acquire(self.context, self.node.uuid) as task: dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, ipxe_enabled=False) task.node.properties['capabilities'] = 'boot_mode:bios' task.node.driver_internal_info['is_whole_disk_image'] = False task.driver.boot.prepare_instance(task) - get_image_info_mock.assert_called_once_with(task) + get_image_info_mock.assert_called_once_with(task, + ipxe_enabled=False) cache_mock.assert_called_once_with( - task, image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, image_info, ipxe_enabled=False) provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) self.assertFalse(switch_pxe_config_mock.called) self.assertFalse(set_boot_device_mock.called) - @mock.patch.object(pxe.LOG, 'warning', autospec=True) + @mock.patch.object(pxe_base.LOG, 'warning', autospec=True) @mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True) @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) @mock.patch.object(dhcp_factory, 'DHCPFactory') @@ -744,70 +662,21 @@ class PXEBootTestCase(db_base.DbTestCase): get_image_info_mock.return_value = {} with task_manager.acquire(self.context, self.node.uuid) as task: dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, CONF.pxe.ipxe_enabled) + task, ipxe_enabled=False) task.node.properties['capabilities'] = 'boot_mode:bios' task.node.driver_internal_info['is_whole_disk_image'] = True task.driver.boot.prepare_instance(task) - get_image_info_mock.assert_called_once_with(task) + get_image_info_mock.assert_called_once_with(task, + ipxe_enabled=False) cache_mock.assert_called_once_with( - task, {}, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, {}, ipxe_enabled=False) provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) self.assertTrue(log_mock.called) clean_up_pxe_mock.assert_called_once_with( - task, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, ipxe_enabled=False) set_boot_device_mock.assert_called_once_with( task, boot_devices.DISK, persistent=True) - @mock.patch('os.path.isfile', lambda filename: False) - @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) - @mock.patch.object(deploy_utils, 'is_iscsi_boot', lambda task: True) - @mock.patch.object(noop_storage.NoopStorage, 'should_write_image', - lambda task: False) - @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) - @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) - @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) - @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) - @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) - def test_prepare_instance_netboot_iscsi( - self, get_image_info_mock, cache_mock, - dhcp_factory_mock, switch_pxe_config_mock, - set_boot_device_mock, create_pxe_config_mock): - http_url = 'http://192.1.2.3:1234' - self.config(ipxe_enabled=True, group='pxe') - self.config(http_url=http_url, group='deploy') - provider_mock = mock.MagicMock() - dhcp_factory_mock.return_value = provider_mock - vol_id = uuidutils.generate_uuid() - obj_utils.create_test_volume_target( - self.context, node_id=self.node.id, volume_type='iscsi', - boot_index=0, volume_id='1234', uuid=vol_id, - properties={'target_lun': 0, - 'target_portal': 'fake_host:3260', - 'target_iqn': 'fake_iqn', - 'auth_username': 'fake_username', - 'auth_password': 'fake_password'}) - with task_manager.acquire(self.context, self.node.uuid) as task: - task.node.driver_internal_info = { - 'boot_from_volume': vol_id} - dhcp_opts = pxe_utils.dhcp_options_for_instance(task, - ipxe_enabled=True) - pxe_config_path = pxe_utils.get_pxe_config_file_path( - task.node.uuid, ipxe_enabled=True) - task.node.properties['capabilities'] = 'boot_mode:bios' - task.driver.boot.prepare_instance(task) - self.assertFalse(get_image_info_mock.called) - self.assertFalse(cache_mock.called) - provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) - create_pxe_config_mock.assert_called_once_with( - task, mock.ANY, CONF.pxe.pxe_config_template, - ipxe_enabled=True) - switch_pxe_config_mock.assert_called_once_with( - pxe_config_path, None, boot_modes.LEGACY_BIOS, False, - ipxe_enabled=True, ramdisk_boot=False, iscsi_boot=True) - set_boot_device_mock.assert_called_once_with(task, - boot_devices.PXE, - persistent=True) - @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) @mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True) def test_prepare_instance_localboot(self, clean_up_pxe_config_mock, @@ -869,7 +738,8 @@ class PXEBootTestCase(db_base.DbTestCase): task.node.uuid) task.driver.boot.prepare_instance(task) - get_image_info_mock.assert_called_once_with(task) + get_image_info_mock.assert_called_once_with(task, + ipxe_enabled=False) cache_mock.assert_called_once_with( task, image_info, CONF.pxe.ipxe_enabled) provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) @@ -904,8 +774,10 @@ class PXEBootTestCase(db_base.DbTestCase): 'ramdisk': ['', '/path/to/ramdisk']} get_image_info_mock.return_value = image_info task.driver.boot.clean_up_instance(task) - clean_up_pxe_env_mock.assert_called_once_with(task, image_info) - get_image_info_mock.assert_called_once_with(task) + clean_up_pxe_env_mock.assert_called_once_with(task, image_info, + ipxe_enabled=False) + get_image_info_mock.assert_called_once_with(task, + ipxe_enabled=False) class PXERamdiskDeployTestCase(db_base.DbTestCase): @@ -957,7 +829,7 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase): get_image_info_mock.return_value = image_info with task_manager.acquire(self.context, self.node.uuid) as task: dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, ipxe_enabled=False) pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid) task.node.properties['capabilities'] = 'boot_option:netboot' @@ -965,9 +837,10 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase): task.driver.deploy.prepare(task) task.driver.deploy.deploy(task) - get_image_info_mock.assert_called_once_with(task) + get_image_info_mock.assert_called_once_with(task, + ipxe_enabled=False) cache_mock.assert_called_once_with( - task, image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, image_info, ipxe_enabled=False) provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) switch_pxe_config_mock.assert_called_once_with( pxe_config_path, None, @@ -993,9 +866,9 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase): self.node.save() with task_manager.acquire(self.context, self.node.uuid) as task: self.assertIsNone(task.driver.deploy.deploy(task)) - mock_image_info.assert_called_once_with(task) + mock_image_info.assert_called_once_with(task, ipxe_enabled=False) mock_cache.assert_called_once_with( - task, image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, image_info, ipxe_enabled=False) self.assertFalse(mock_warning.called) i_info['configdrive'] = 'meow' self.node.instance_info = i_info @@ -1110,9 +983,9 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase): with task_manager.acquire(self.context, self.node.uuid) as task: power_on_node_if_needed_mock.return_value = states.POWER_OFF self.assertIsNone(task.driver.deploy.deploy(task)) - mock_image_info.assert_called_once_with(task) + mock_image_info.assert_called_once_with(task, ipxe_enabled=False) mock_cache.assert_called_once_with( - task, image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) + task, image_info, ipxe_enabled=False) self.assertFalse(mock_warning.called) power_on_node_if_needed_mock.assert_called_once_with(task) restore_power_state_mock.assert_called_once_with(