diff options
Diffstat (limited to 'ironic/drivers/modules/irmc/deploy.py')
-rw-r--r-- | ironic/drivers/modules/irmc/deploy.py | 797 |
1 files changed, 797 insertions, 0 deletions
diff --git a/ironic/drivers/modules/irmc/deploy.py b/ironic/drivers/modules/irmc/deploy.py new file mode 100644 index 000000000..5370d32d8 --- /dev/null +++ b/ironic/drivers/modules/irmc/deploy.py @@ -0,0 +1,797 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +iRMC Deploy Driver +""" + +import os +import tempfile + +from oslo_config import cfg +from oslo_log import log as logging +from oslo_utils import importutils + +from ironic.common import boot_devices +from ironic.common import exception +from ironic.common.glance_service import service_utils +from ironic.common.i18n import _ +from ironic.common.i18n import _LE +from ironic.common.i18n import _LI +from ironic.common import images +from ironic.common import states +from ironic.common import utils +from ironic.conductor import task_manager +from ironic.conductor import utils as manager_utils +from ironic.drivers import base +from ironic.drivers.modules import agent +from ironic.drivers.modules import deploy_utils +from ironic.drivers.modules.irmc import common as irmc_common +from ironic.drivers.modules import iscsi_deploy + +scci = importutils.try_import('scciclient.irmc.scci') + +CONF = cfg.CONF + +try: + if CONF.debug: + scci.DEBUG = True +except Exception: + pass + +opts = [ + cfg.StrOpt('remote_image_share_root', + default='/remote_image_share_root', + help='Ironic conductor node\'s "NFS" or "CIFS" root path'), + cfg.StrOpt('remote_image_server', + help='IP of remote image server'), + cfg.StrOpt('remote_image_share_type', + default='CIFS', + help='Share type of virtual media, either "NFS" or "CIFS"'), + cfg.StrOpt('remote_image_share_name', + default='share', + help='share name of remote_image_server'), + cfg.StrOpt('remote_image_user_name', + help='User name of remote_image_server'), + cfg.StrOpt('remote_image_user_password', + help='Password of remote_image_user_name'), + cfg.StrOpt('remote_image_user_domain', + default='', + help='Domain name of remote_image_user_name'), +] + +CONF.register_opts(opts, group='irmc') + +LOG = logging.getLogger(__name__) + +REQUIRED_PROPERTIES = { + 'irmc_deploy_iso': _("Deployment ISO image file name. " + "Required."), +} + +COMMON_PROPERTIES = REQUIRED_PROPERTIES + +CONF.import_opt('pxe_append_params', 'ironic.drivers.modules.iscsi_deploy', + group='pxe') + + +def _parse_config_option(): + """Parse config file options. + + This method checks config file options validity. + + :raises: InvalidParameterValue, if config option has invalid value. + """ + error_msgs = [] + if not os.path.isdir(CONF.irmc.remote_image_share_root): + error_msgs.append( + _("Value '%s' for remote_image_share_root isn't a directory " + "or doesn't exist.") % + CONF.irmc.remote_image_share_root) + if CONF.irmc.remote_image_share_type.lower() not in ('nfs', 'cifs'): + error_msgs.append( + _("Value '%s' for remote_image_share_type is not supported " + "value either 'NFS' or 'CIFS'.") % + CONF.irmc.remote_image_share_type) + if error_msgs: + msg = (_("The following errors were encountered while parsing " + "config file:%s") % error_msgs) + raise exception.InvalidParameterValue(msg) + + +def _parse_driver_info(node): + """Gets the driver specific Node deployment info. + + This method validates whether the 'driver_info' property of the + supplied node contains the required or optional information properly + for this driver to deploy images to the node. + + :param node: a target node of the deployment + :returns: the driver_info values of the node. + :raises: MissingParameterValue, if any of the required parameters are + missing. + :raises: InvalidParameterValue, if any of the parameters have invalid + value. + """ + d_info = node.driver_info + deploy_info = {} + + deploy_info['irmc_deploy_iso'] = d_info.get('irmc_deploy_iso') + error_msg = _("Error validating iRMC virtual media deploy. Some parameters" + " were missing in node's driver_info") + deploy_utils.check_for_missing_params(deploy_info, error_msg) + + if service_utils.is_image_href_ordinary_file_name( + deploy_info['irmc_deploy_iso']): + deploy_iso = os.path.join(CONF.irmc.remote_image_share_root, + deploy_info['irmc_deploy_iso']) + if not os.path.isfile(deploy_iso): + msg = (_("Deploy ISO file, %(deploy_iso)s, " + "not found for node: %(node)s.") % + {'deploy_iso': deploy_iso, 'node': node.uuid}) + raise exception.InvalidParameterValue(msg) + + return deploy_info + + +def _parse_instance_info(node): + """Gets the instance specific Node deployment info. + + This method validates whether the 'instance_info' property of the + supplied node contains the required or optional information properly + for this driver to deploy images to the node. + + :param node: a target node of the deployment + :returns: the instance_info values of the node. + :raises: InvalidParameterValue, if any of the parameters have invalid + value. + """ + i_info = node.instance_info + deploy_info = {} + + if i_info.get('irmc_boot_iso'): + deploy_info['irmc_boot_iso'] = i_info['irmc_boot_iso'] + + if service_utils.is_image_href_ordinary_file_name( + deploy_info['irmc_boot_iso']): + boot_iso = os.path.join(CONF.irmc.remote_image_share_root, + deploy_info['irmc_boot_iso']) + + if not os.path.isfile(boot_iso): + msg = (_("Boot ISO file, %(boot_iso)s, " + "not found for node: %(node)s.") % + {'boot_iso': boot_iso, 'node': node.uuid}) + raise exception.InvalidParameterValue(msg) + + return deploy_info + + +def _parse_deploy_info(node): + """Gets the instance and driver specific Node deployment info. + + This method validates whether the 'instance_info' and 'driver_info' + property of the supplied node contains the required information for + this driver to deploy images to the node. + + :param node: a target node of the deployment + :returns: a dict with the instance_info and driver_info values. + :raises: MissingParameterValue, if any of the required parameters are + missing. + :raises: InvalidParameterValue, if any of the parameters have invalid + value. + """ + deploy_info = {} + deploy_info.update(iscsi_deploy.parse_instance_info(node)) + deploy_info.update(_parse_driver_info(node)) + deploy_info.update(_parse_instance_info(node)) + + return deploy_info + + +def _reboot_into_deploy_iso(task, ramdisk_options): + """Reboots the node into a given deploy ISO. + + This method attaches the given deploy ISO as virtual media, prepares the + arguments for ramdisk in virtual media floppy, and then reboots the node. + + :param task: a TaskManager instance containing the node to act on. + :param ramdisk_options: the options to be passed to the ramdisk in virtual + media floppy. + :raises: ImageRefValidationFailed if no image service can handle specified + href. + :raises: ImageCreationFailed, if it failed while creating the floppy image. + :raises: IRMCOperationError, if some operation on iRMC failed. + :raises: InvalidParameterValue if the validation of the + PowerInterface or ManagementInterface fails. + """ + d_info = task.node.driver_info + + deploy_iso_href = d_info['irmc_deploy_iso'] + if service_utils.is_image_href_ordinary_file_name(deploy_iso_href): + deploy_iso_file = deploy_iso_href + else: + deploy_iso_file = _get_deploy_iso_name(task.node) + deploy_iso_fullpathname = os.path.join( + CONF.irmc.remote_image_share_root, deploy_iso_file) + images.fetch(task.context, deploy_iso_href, deploy_iso_fullpathname) + + setup_vmedia_for_boot(task, deploy_iso_file, ramdisk_options) + manager_utils.node_set_boot_device(task, boot_devices.CDROM) + manager_utils.node_power_action(task, states.REBOOT) + + +def _get_deploy_iso_name(node): + """Returns the deploy ISO file name for a given node. + + :param node: the node for which ISO file name is to be provided. + """ + return "deploy-%s.iso" % node.uuid + + +def _get_boot_iso_name(node): + """Returns the boot ISO file name for a given node. + + :param node: the node for which ISO file name is to be provided. + """ + return "boot-%s.iso" % node.uuid + + +def _prepare_boot_iso(task, root_uuid): + """Prepare a boot ISO to boot the node. + + :param task: a TaskManager instance containing the node to act on. + :param root_uuid: the uuid of the root partition. + :raises: MissingParameterValue, if any of the required parameters are + missing. + :raises: InvalidParameterValue, if any of the parameters have invalid + value. + :raises: ImageCreationFailed, if creating boot ISO + for BIOS boot_mode failed. + """ + deploy_info = _parse_deploy_info(task.node) + driver_internal_info = task.node.driver_internal_info + + # fetch boot iso + if deploy_info.get('irmc_boot_iso'): + boot_iso_href = deploy_info['irmc_boot_iso'] + if service_utils.is_image_href_ordinary_file_name(boot_iso_href): + driver_internal_info['irmc_boot_iso'] = boot_iso_href + else: + boot_iso_filename = _get_boot_iso_name(task.node) + boot_iso_fullpathname = os.path.join( + CONF.irmc.remote_image_share_root, boot_iso_filename) + images.fetch(task.context, boot_iso_href, boot_iso_fullpathname) + + driver_internal_info['irmc_boot_iso'] = boot_iso_filename + + # create boot iso + else: + image_href = deploy_info['image_source'] + image_props = ['kernel_id', 'ramdisk_id'] + image_properties = images.get_image_properties( + task.context, image_href, image_props) + kernel_href = (task.node.instance_info.get('kernel') or + image_properties['kernel_id']) + ramdisk_href = (task.node.instance_info.get('ramdisk') or + image_properties['ramdisk_id']) + + deploy_iso_filename = _get_deploy_iso_name(task.node) + deploy_iso = ('file://' + os.path.join( + CONF.irmc.remote_image_share_root, deploy_iso_filename)) + boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) + kernel_params = CONF.pxe.pxe_append_params + + boot_iso_filename = _get_boot_iso_name(task.node) + boot_iso_fullpathname = os.path.join( + CONF.irmc.remote_image_share_root, boot_iso_filename) + + images.create_boot_iso(task.context, boot_iso_fullpathname, + kernel_href, ramdisk_href, + deploy_iso, root_uuid, + kernel_params, boot_mode) + + driver_internal_info['irmc_boot_iso'] = boot_iso_filename + + # save driver_internal_info['irmc_boot_iso'] + task.node.driver_internal_info = driver_internal_info + task.node.save() + + +def _get_floppy_image_name(node): + """Returns the floppy image name for a given node. + + :param node: the node for which image name is to be provided. + """ + return "image-%s.img" % node.uuid + + +def _prepare_floppy_image(task, params): + """Prepares the floppy image for passing the parameters. + + This method prepares a temporary vfat filesystem image, which + contains the parameters to be passed to the ramdisk. + Then it uploads the file NFS or CIFS server. + + :param task: a TaskManager instance containing the node to act on. + :param params: a dictionary containing 'parameter name'->'value' mapping + to be passed to the deploy ramdisk via the floppy image. + :returns: floppy image filename + :raises: ImageCreationFailed, if it failed while creating the floppy image. + :raises: IRMCOperationError, if copying floppy image file failed. + """ + floppy_filename = _get_floppy_image_name(task.node) + floppy_fullpathname = os.path.join( + CONF.irmc.remote_image_share_root, floppy_filename) + + with tempfile.NamedTemporaryFile() as vfat_image_tmpfile_obj: + images.create_vfat_image(vfat_image_tmpfile_obj.name, + parameters=params) + try: + utils.execute('cp', vfat_image_tmpfile_obj.name, + floppy_fullpathname, check_exit_code=[0]) + except Exception as e: + operation = _("Copying floppy image file") + raise exception.IRMCOperationError( + operation=operation, error=e) + + return floppy_filename + + +def setup_vmedia_for_boot(task, bootable_iso_filename, parameters=None): + """Sets up the node to boot from the boot ISO image. + + This method attaches a boot_iso on the node and passes + the required parameters to it via a virtual floppy image. + + :param task: a TaskManager instance containing the node to act on. + :param bootable_iso_filename: a bootable ISO image to attach to. + The iso file should be present in NFS/CIFS server. + :param parameters: the parameters to pass in a virtual floppy image + in a dictionary. This is optional. + :raises: ImageCreationFailed, if it failed while creating a floppy image. + :raises: IRMCOperationError, if attaching a virtual media failed. + """ + LOG.info(_LI("Setting up node %s to boot from virtual media"), + task.node.uuid) + + if parameters: + floppy_image_filename = _prepare_floppy_image(task, parameters) + _attach_virtual_fd(task.node, floppy_image_filename) + + _attach_virtual_cd(task.node, bootable_iso_filename) + + +def _cleanup_vmedia_boot(task): + """Cleans a node after a virtual media boot. + + This method cleans up a node after a virtual media boot. + It deletes a floppy image if it exists in NFS/CIFS server. + It also ejects both the virtual media cdrom and the virtual media floppy. + + :param task: a TaskManager instance containing the node to act on. + :raises: IRMCOperationError if ejecting virtual media failed. + """ + LOG.debug("Cleaning up node %s after virtual media boot", task.node.uuid) + + node = task.node + _detach_virtual_cd(node) + _detach_virtual_fd(node) + + _remove_share_file(_get_floppy_image_name(node)) + _remove_share_file(_get_deploy_iso_name(node)) + + +def _remove_share_file(share_filename): + """remove a file in the share file system. + + :param share_filename: a file name to be removed. + """ + share_fullpathname = os.path.join( + CONF.irmc.remote_image_share_name, share_filename) + utils.unlink_without_raise(share_fullpathname) + + +def _attach_virtual_cd(node, bootable_iso_filename): + """Attaches the given url as virtual media on the node. + + :param node: an ironic node object. + :param bootable_iso_filename: a bootable ISO image to attach to. + The iso file should be present in NFS/CIFS server. + :raises: IRMCOperationError if attaching virtual media failed. + """ + try: + irmc_client = irmc_common.get_irmc_client(node) + + cd_set_params = scci.get_virtual_cd_set_params_cmd( + CONF.irmc.remote_image_server, + CONF.irmc.remote_image_user_domain, + scci.get_share_type(CONF.irmc.remote_image_share_type), + CONF.irmc.remote_image_share_name, + bootable_iso_filename, + CONF.irmc.remote_image_user_name, + CONF.irmc.remote_image_user_password) + + irmc_client(cd_set_params, async=False) + irmc_client(scci.MOUNT_CD, async=False) + + except scci.SCCIClientError as irmc_exception: + LOG.exception(_LE("Error while inserting virtual cdrom " + "from node %(uuid)s. Error: %(error)s"), + {'uuid': node.uuid, 'error': irmc_exception}) + operation = _("Inserting virtual cdrom") + raise exception.IRMCOperationError(operation=operation, + error=irmc_exception) + + LOG.info(_LI("Attached virtual cdrom successfully" + " for node %s"), node.uuid) + + +def _detach_virtual_cd(node): + """Detaches virtual cdrom on the node. + + :param node: an ironic node object. + :raises: IRMCOperationError if eject virtual cdrom failed. + """ + try: + irmc_client = irmc_common.get_irmc_client(node) + + irmc_client(scci.UNMOUNT_CD) + + except scci.SCCIClientError as irmc_exception: + LOG.exception(_LE("Error while ejecting virtual cdrom " + "from node %(uuid)s. Error: %(error)s"), + {'uuid': node.uuid, 'error': irmc_exception}) + operation = _("Ejecting virtual cdrom") + raise exception.IRMCOperationError(operation=operation, + error=irmc_exception) + + LOG.info(_LI("Detached virtual cdrom successfully" + " for node %s"), node.uuid) + + +def _attach_virtual_fd(node, floppy_image_filename): + """Attaches virtual floppy on the node. + + :param node: an ironic node object. + :raises: IRMCOperationError if insert virtual floppy failed. + """ + try: + irmc_client = irmc_common.get_irmc_client(node) + + fd_set_params = scci.get_virtual_fd_set_params_cmd( + CONF.irmc.remote_image_server, + CONF.irmc.remote_image_user_domain, + scci.get_share_type(CONF.irmc.remote_image_share_type), + CONF.irmc.remote_image_share_name, + floppy_image_filename, + CONF.irmc.remote_image_user_name, + CONF.irmc.remote_image_user_password) + + irmc_client(fd_set_params, async=False) + irmc_client(scci.MOUNT_FD, async=False) + + except scci.SCCIClientError as irmc_exception: + LOG.exception(_LE("Error while inserting virtual floppy " + "from node %(uuid)s. Error: %(error)s"), + {'uuid': node.uuid, 'error': irmc_exception}) + operation = _("Inserting virtual floppy") + raise exception.IRMCOperationError(operation=operation, + error=irmc_exception) + + LOG.info(_LI("Attached virtual floppy successfully" + " for node %s"), node.uuid) + + +def _detach_virtual_fd(node): + """Detaches virtual media on the node. + + :param node: an ironic node object. + :raises: IRMCOperationError if eject virtual media failed. + """ + try: + irmc_client = irmc_common.get_irmc_client(node) + + irmc_client(scci.UNMOUNT_FD) + + except scci.SCCIClientError as irmc_exception: + LOG.exception(_LE("Error while ejecting virtual floppy " + "from node %(uuid)s. Error: %(error)s"), + {'uuid': node.uuid, 'error': irmc_exception}) + operation = _("Ejecting virtual floppy") + raise exception.IRMCOperationError(operation=operation, + error=irmc_exception) + + LOG.info(_LI("Detached virtual floppy successfully" + " for node %s"), node.uuid) + + +def _check_share_fs_mounted(): + """Check if Share File System (NFS or CIFS) is mounted. + + :raises: InvalidParameterValue, if config option has invalid value. + :raises: IRMCSharedFileSystemNotMounted, if shared file system is + not mounted. + """ + _parse_config_option() + if not os.path.ismount(CONF.irmc.remote_image_share_root): + raise exception.IRMCSharedFileSystemNotMounted( + share=CONF.irmc.remote_image_share_root) + + +class IRMCVirtualMediaIscsiDeploy(base.DeployInterface): + """Interface for iSCSI deploy-related actions.""" + + def __init__(self): + """Constructor of IRMCVirtualMediaIscsiDeploy. + + :raises: IRMCSharedFileSystemNotMounted, if shared file system is + not mounted. + :raises: InvalidParameterValue, if config option has invalid value. + """ + _check_share_fs_mounted() + super(IRMCVirtualMediaIscsiDeploy, self).__init__() + + def get_properties(self): + return COMMON_PROPERTIES + + def validate(self, task): + """Validate the deployment information for the task's node. + + :param task: a TaskManager instance containing the node to act on. + :raises: InvalidParameterValue, if config option has invalid value. + :raises: IRMCSharedFileSystemNotMounted, if shared file system is + not mounted. + :raises: InvalidParameterValue, if some information is invalid. + :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are + missing in the Glance image, or if 'kernel' and 'ramdisk' are + missing in the Non Glance image. + """ + _check_share_fs_mounted() + iscsi_deploy.validate(task) + + d_info = _parse_deploy_info(task.node) + if service_utils.is_glance_image(d_info['image_source']): + props = ['kernel_id', 'ramdisk_id'] + else: + props = ['kernel', 'ramdisk'] + iscsi_deploy.validate_image_properties(task.context, d_info, + props) + deploy_utils.validate_capabilities(task.node) + + @task_manager.require_exclusive_lock + def deploy(self, task): + """Start deployment of the task's node. + + Fetches the instance image, prepares the options for the deployment + ramdisk, sets the node to boot from virtual media cdrom, and reboots + the given node. + + :param task: a TaskManager instance containing the node to act on. + :returns: deploy state DEPLOYWAIT. + :raises: InstanceDeployFailure, if image size if greater than root + partition. + :raises: ImageCreationFailed, if it failed while creating the floppy + image. + :raises: IRMCOperationError, if some operation on iRMC fails. + """ + node = task.node + manager_utils.node_power_action(task, states.POWER_OFF) + + iscsi_deploy.cache_instance_image(task.context, node) + iscsi_deploy.check_image_size(task) + + deploy_ramdisk_opts = iscsi_deploy.build_deploy_ramdisk_options(node) + deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) + deploy_ramdisk_opts['BOOTIF'] = deploy_nic_mac + + _reboot_into_deploy_iso(task, deploy_ramdisk_opts) + + return states.DEPLOYWAIT + + @task_manager.require_exclusive_lock + def tear_down(self, task): + """Tear down a previous deployment on the task's node. + + Power off the node. All actual clean-up is done in the clean_up() + method which should be called separately. + + :param task: a TaskManager instance containing the node to act on. + :returns: deploy state DELETED. + """ + _remove_share_file(_get_boot_iso_name(task.node)) + driver_internal_info = task.node.driver_internal_info + driver_internal_info.pop('irmc_boot_iso', None) + task.node.driver_internal_info = driver_internal_info + task.node.save() + manager_utils.node_power_action(task, states.POWER_OFF) + return states.DELETED + + def prepare(self, task): + """Prepare the deployment environment for the task's node. + + If preparation of the deployment environment ahead of time is possible, + this method should be implemented by the driver. + + If implemented, this method must be idempotent. It may be called + multiple times for the same node on the same conductor, and it may be + called by multiple conductors in parallel. Therefore, it must not + require an exclusive lock. + + This method is called before `deploy`. + + :param task: a TaskManager instance containing the node to act on. + """ + pass + + def clean_up(self, task): + """Clean up the deployment environment for the task's node. + + Unlinks instance image and triggers image cache cleanup. + + :param task: a TaskManager instance containing the node to act on. + """ + _cleanup_vmedia_boot(task) + iscsi_deploy.destroy_images(task.node.uuid) + + def take_over(self, task): + pass + + +class IRMCVirtualMediaAgentDeploy(base.DeployInterface): + + def __init__(self): + """Constructor of IRMCVirtualMediaAgentDeploy. + + :raises: IRMCSharedFileSystemNotMounted, if shared file system is + not mounted. + :raises: InvalidParameterValue, if config option has invalid value. + """ + _check_share_fs_mounted() + super(IRMCVirtualMediaAgentDeploy, self).__init__() + + """Interface for Agent deploy-related actions.""" + def get_properties(self): + """Return the properties of the interface. + + :returns: dictionary of <property name>:<property description> entries. + """ + return COMMON_PROPERTIES + + def validate(self, task): + """Validate the driver-specific Node deployment info. + + :param task: a TaskManager instance + :raises: IRMCSharedFileSystemNotMounted, if shared file system is + not mounted. + :raises: InvalidParameterValue, if config option has invalid value. + :raises: MissingParameterValue if some parameters are missing. + """ + _check_share_fs_mounted() + _parse_driver_info(task.node) + deploy_utils.validate_capabilities(task.node) + + @task_manager.require_exclusive_lock + def deploy(self, task): + """Perform a deployment to a node. + + Prepares the options for the agent ramdisk and sets the node to boot + from virtual media cdrom. + + :param task: a TaskManager instance. + :returns: states.DEPLOYWAIT + :raises: ImageCreationFailed, if it failed while creating the floppy + image. + :raises: IRMCOperationError, if some operation on iRMC fails. + """ + deploy_ramdisk_opts = agent.build_agent_options(task.node) + _reboot_into_deploy_iso(task, deploy_ramdisk_opts) + + return states.DEPLOYWAIT + + @task_manager.require_exclusive_lock + def tear_down(self, task): + """Tear down a previous deployment on the task's node. + + :param task: a TaskManager instance. + :returns: states.DELETED + """ + manager_utils.node_power_action(task, states.POWER_OFF) + return states.DELETED + + def prepare(self, task): + """Prepare the deployment environment for this node. + + :param task: a TaskManager instance. + """ + node = task.node + node.instance_info = agent.build_instance_info_for_deploy(task) + node.save() + + def clean_up(self, task): + """Clean up the deployment environment for this node. + + Ejects the attached virtual media from the iRMC and also removes + the floppy image from the share file system, if it exists. + + :param task: a TaskManager instance. + """ + _cleanup_vmedia_boot(task) + + def take_over(self, task): + """Take over management of this node from a dead conductor. + + :param task: a TaskManager instance. + """ + pass + + +class VendorPassthru(base.VendorInterface): + """Vendor-specific interfaces for iRMC deploy drivers.""" + + def get_properties(self): + return COMMON_PROPERTIES + + def validate(self, task, method, **kwargs): + """Validate vendor-specific actions. + + Checks if a valid vendor passthru method was passed and validates + the parameters for the vendor passthru method. + + :param task: a TaskManager instance containing the node to act on. + :param method: method to be validated. + :param kwargs: kwargs containing the vendor passthru method's + parameters. + :raises: MissingParameterValue, if some required parameters were not + passed. + :raises: InvalidParameterValue, if any of the parameters have invalid + value. + """ + iscsi_deploy.get_deploy_info(task.node, **kwargs) + + @base.passthru(['POST']) + @task_manager.require_exclusive_lock + def pass_deploy_info(self, task, **kwargs): + """Continues the iSCSI deployment from where ramdisk left off. + + Continues the iSCSI deployment from the conductor node, finds the + boot ISO to boot the node, and sets the node to boot from boot ISO. + + :param task: a TaskManager instance containing the node to act on. + :param kwargs: kwargs containing parameters for iSCSI deployment. + :raises: InvalidState + """ + node = task.node + task.process_event('resume') + + root_dict = iscsi_deploy.continue_deploy(task, **kwargs) + root_uuid = root_dict.get('root uuid') + + try: + _cleanup_vmedia_boot(task) + _prepare_boot_iso(task, root_uuid) + setup_vmedia_for_boot(task, + node.driver_internal_info['irmc_boot_iso']) + manager_utils.node_set_boot_device(task, boot_devices.CDROM) + + address = kwargs.get('address') + deploy_utils.notify_ramdisk_to_proceed(address) + + LOG.info(_LI('Deployment to node %s done'), node.uuid) + + task.process_event('done') + except Exception as e: + LOG.exception(_LE('Deploy failed for instance %(instance)s. ' + 'Error: %(error)s'), + {'instance': node.instance_uuid, 'error': e}) + msg = _('Failed to continue iSCSI deployment.') + deploy_utils.set_failed_state(task, msg) |