def test_is_glance_image(self): image_href = 'uuid' self.assertTrue(service_utils.is_glance_image(image_href)) image_href = 'glance://uuid' self.assertTrue(service_utils.is_glance_image(image_href)) image_href = 'http://aaa/bbb' self.assertFalse(service_utils.is_glance_image(image_href)) image_href = None self.assertFalse(service_utils.is_glance_image(image_href))
def test_is_glance_image(self): image_href = u'uui\u0111' self.assertFalse(service_utils.is_glance_image(image_href)) image_href = u'733d1c44-a2ea-414b-aca7-69decf20d810' self.assertTrue(service_utils.is_glance_image(image_href)) image_href = u'glance://uui\u0111' self.assertTrue(service_utils.is_glance_image(image_href)) image_href = 'http://aaa/bbb' self.assertFalse(service_utils.is_glance_image(image_href)) image_href = None self.assertFalse(service_utils.is_glance_image(image_href))
def start_deploy(task, manager, configdrive=None, event='deploy'): """Start deployment or rebuilding on a node. This function does not check the node suitability for deployment, it's left up to the caller. :param task: a TaskManager instance. :param manager: a ConductorManager to run tasks on. :param configdrive: a configdrive, if requested. :param event: event to process: deploy or rebuild. """ node = task.node if event == 'rebuild': # Note(gilliard) Clear these to force the driver to # check whether they have been changed in glance # NOTE(vdrok): If image_source is not from Glance we should # not clear kernel and ramdisk as they're input manually if glance_utils.is_glance_image( node.instance_info.get('image_source')): instance_info = node.instance_info instance_info.pop('kernel', None) instance_info.pop('ramdisk', None) node.instance_info = instance_info # Infer the image type to make sure the deploy driver # validates only the necessary variables for different # image types. # NOTE(sirushtim): The iwdi variable can be None. It's up to # the deploy driver to validate this. iwdi = images.is_whole_disk_image(task.context, node.instance_info) driver_internal_info = node.driver_internal_info driver_internal_info['is_whole_disk_image'] = iwdi node.driver_internal_info = driver_internal_info node.save() try: task.driver.power.validate(task) task.driver.deploy.validate(task) utils.validate_instance_info_traits(task.node) conductor_steps.validate_deploy_templates(task, skip_missing=True) except exception.InvalidParameterValue as e: raise exception.InstanceDeployFailure( _("Failed to validate deploy or power info for node " "%(node_uuid)s. Error: %(msg)s") % { 'node_uuid': node.uuid, 'msg': e }, code=e.code) try: task.process_event(event, callback=manager._spawn_worker, call_args=(do_node_deploy, task, manager.conductor.id, configdrive), err_handler=utils.provisioning_error_handler) except exception.InvalidState: raise exception.InvalidStateRequested(action=event, node=task.node.uuid, state=task.node.provision_state)
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() self._validate_volume_boot(task) if not task.driver.storage.should_write_image(task): LOG.debug( 'Node %(node)s skips image validation because of ' 'booting from a remote volume.', {'node': task.node.uuid}) return d_info = _parse_deploy_info(task.node) if task.node.driver_internal_info.get('is_whole_disk_image'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def _validate_instance_info(cls, task): """Validate instance image information for the task's node. This method validates whether the 'instance_info' property of the supplied node contains the required information for this driver. :param task: a TaskManager instance containing the node to act on. :raises: InvalidParameterValue if any parameters are incorrect :raises: MissingParameterValue if some mandatory information is missing on the node """ node = task.node d_info = cls._parse_deploy_info(node) if node.driver_internal_info.get('is_whole_disk_image'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def build_instance_info_for_deploy(task): """Build instance_info necessary for deploying to a node.""" node = task.node instance_info = node.instance_info image_source = instance_info['image_source'] if service_utils.is_glance_image(image_source): glance = image_service.GlanceImageService(version=2, context=task.context) image_info = glance.show(image_source) swift_temp_url = glance.swift_temp_url(image_info) LOG.debug('Got image info: %(info)s for node %(node)s.', {'info': image_info, 'node': node.uuid}) instance_info['image_url'] = swift_temp_url instance_info['image_checksum'] = image_info['checksum'] instance_info['image_disk_format'] = image_info['disk_format'] else: try: image_service.HttpImageService().validate_href(image_source) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error(_LE("Ansible deploy supports only HTTP(S) URLs as " "instance_info['image_source']. Either %s " "is not a valid HTTP(S) URL or " "is not reachable."), image_source) instance_info['image_url'] = image_source return instance_info
def validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ self._validate_common(task) # NOTE(TheJulia): If we're not writing an image, we can skip # the remainder of this method. if (not task.driver.storage.should_write_image(task)): return node = task.node d_info = deploy_utils.get_image_instance_info(node) if (node.driver_internal_info.get('is_whole_disk_image') or deploy_utils.get_boot_option(node) == 'local'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
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 some information is invalid. :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are missing in the Glance image or 'kernel' and 'ramdisk' not provided in instance_info for non-Glance image. """ node = task.node boot_option = deploy_utils.get_boot_option(node) boot_iso = node.instance_info.get('ilo_boot_iso') if (boot_option == "ramdisk" and boot_iso): if not service_utils.is_glance_image(boot_iso): try: image_service.HttpImageService().validate_href(boot_iso) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error("Virtual media deploy with 'ramdisk' " "boot_option accepts only Glance images or " "HTTP(S) URLs as " "instance_info['ilo_boot_iso']. Either %s " "is not a valid HTTP(S) URL or is not " "reachable.", boot_iso) return _validate_driver_info(task) if not task.driver.storage.should_write_image(task): return else: _validate_instance_image_info(task)
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 task.node.driver_internal_info.get('is_whole_disk_image'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props) deploy_utils.validate_capabilities(task.node)
def _validate_instance_info(self, task): """Validate instance image information for the task's node. This method validates whether the 'instance_info' property of the supplied node contains the required information for this driver. :param task: a TaskManager instance containing the node to act on. :raises: InvalidParameterValue if any parameters are incorrect :raises: MissingParameterValue if some mandatory information is missing on the node """ node = task.node # NOTE(dtantsur): if we're are writing an image with local boot # the boot interface does not care about image parameters and # must not validate them. if (not task.driver.storage.should_write_image(task) or deploy_utils.get_boot_option(node) == 'local'): return d_info = _parse_deploy_info(node) if node.driver_internal_info.get('is_whole_disk_image'): props = [] elif d_info.get('boot_iso'): props = ['boot_iso'] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def build_deploy_pxe_options(task, pxe_info, mode='deploy', ipxe_enabled=False): pxe_opts = {} node = task.node kernel_label = '%s_kernel' % mode ramdisk_label = '%s_ramdisk' % mode for label, option in ((kernel_label, 'deployment_aki_path'), (ramdisk_label, 'deployment_ari_path')): if ipxe_enabled: image_href = pxe_info[label][0] if (CONF.pxe.ipxe_use_swift and service_utils.is_glance_image(image_href)): pxe_opts[option] = images.get_temp_url_for_glance_image( task.context, image_href) else: pxe_opts[option] = '/'.join( [CONF.deploy.http_url, node.uuid, label]) else: pxe_opts[option] = get_path_relative_to_tftp_root( pxe_info[label][1]) if ipxe_enabled: pxe_opts['initrd_filename'] = ramdisk_label return pxe_opts
def build_instance_info_for_deploy(task): """Build instance_info necessary for deploying to a node. :param task: a TaskManager object containing the node :returns: a dictionary containing the properties to be updated in instance_info :raises: exception.ImageRefValidationFailed if image_source is not Glance href and is not HTTP(S) URL. """ def validate_image_url(url, secret=False): """Validates image URL through the HEAD request. :param url: URL to be validated :param secret: if URL is secret (e.g. swift temp url), it will not be shown in logs. """ try: image_service.HttpImageService().validate_href(url, secret) except exception.ImageRefValidationFailed as e: with excutils.save_and_reraise_exception(): LOG.error(_LE("Agent deploy supports only HTTP(S) URLs as " "instance_info['image_source'] or swift " "temporary URL. Either the specified URL is not " "a valid HTTP(S) URL or is not reachable " "for node %(node)s. Error: %(msg)s"), {'node': node.uuid, 'msg': e}) node = task.node instance_info = node.instance_info iwdi = node.driver_internal_info.get('is_whole_disk_image') image_source = instance_info['image_source'] if service_utils.is_glance_image(image_source): glance = image_service.GlanceImageService(version=2, context=task.context) image_info = glance.show(image_source) LOG.debug('Got image info: %(info)s for node %(node)s.', {'info': image_info, 'node': node.uuid}) swift_temp_url = glance.swift_temp_url(image_info) validate_image_url(swift_temp_url, secret=True) instance_info['image_url'] = swift_temp_url instance_info['image_checksum'] = image_info['checksum'] instance_info['image_disk_format'] = image_info['disk_format'] instance_info['image_container_format'] = ( image_info['container_format']) instance_info['image_tags'] = image_info.get('tags', []) instance_info['image_properties'] = image_info['properties'] if not iwdi: instance_info['kernel'] = image_info['properties']['kernel_id'] instance_info['ramdisk'] = image_info['properties']['ramdisk_id'] else: validate_image_url(image_source) instance_info['image_url'] = image_source if not iwdi: instance_info['image_type'] = 'partition' i_info = parse_instance_info(node) instance_info.update(i_info) else: instance_info['image_type'] = 'whole-disk-image' return instance_info
def _validate(task): """Validate the prerequisites for virtual media based deploy. This method validates whether the 'driver_info' property of the supplied node contains the required information for this driver. :param task: a TaskManager instance containing the node to act on. :raises: InvalidParameterValue if any parameters are incorrect :raises: MissingParameterValue if some mandatory information is missing on the node """ node = task.node ilo_common.parse_driver_info(node) if 'ilo_deploy_iso' not in node.driver_info: raise exception.MissingParameterValue(_( "Missing 'ilo_deploy_iso' parameter in node's 'driver_info'.")) deploy_iso = node.driver_info['ilo_deploy_iso'] if not service_utils.is_glance_image(deploy_iso): try: image_service.HttpImageService().validate_href(deploy_iso) except exception.ImageRefValidationFailed: raise exception.InvalidParameterValue(_( "Virtual media deploy accepts only Glance images or " "HTTP(S) as driver_info['ilo_deploy_iso']. Either '%s' " "is not a glance UUID or not a valid HTTP(S) URL or " "the given URL is not reachable.") % deploy_iso)
def get_image_instance_info(node): """Gets the image information from the node. Get image information for the given node instance from its 'instance_info' property. :param node: a single Node. :returns: A dict with required image properties retrieved from node's 'instance_info'. :raises: MissingParameterValue, if image_source is missing in node's instance_info. Also raises same exception if kernel/ramdisk is missing in instance_info for non-glance images. """ info = {} info['image_source'] = node.instance_info.get('image_source') is_whole_disk_image = node.driver_internal_info.get('is_whole_disk_image') if not is_whole_disk_image: if not service_utils.is_glance_image(info['image_source']): info['kernel'] = node.instance_info.get('kernel') info['ramdisk'] = node.instance_info.get('ramdisk') error_msg = (_("Cannot validate image information for node %s because one " "or more parameters are missing from its instance_info") % node.uuid) check_for_missing_params(info, error_msg) return info
def _delete_master_path_if_stale(master_path, href, ctx): """Delete image from cache if it is not up to date with href contents. :param master_path: path to an image in master cache :param href: image href :param ctx: context to use :returns: True if master_path is up to date with href contents, False if master_path was stale and was deleted or it didn't exist """ if service_utils.is_glance_image(href): # Glance image contents cannot be updated without changing image's UUID return os.path.exists(master_path) if os.path.exists(master_path): img_service = image_service.get_image_service(href, context=ctx) img_mtime = img_service.show(href).get('updated_at') if not img_mtime: # This means that href is not a glance image and doesn't have an # updated_at attribute LOG.warning("Image service couldn't determine last " "modification time of %(href)s, considering " "cached image up to date.", {'href': href}) return True master_mtime = utils.unix_file_modification_datetime(master_path) if img_mtime <= master_mtime: return True # Delete image from cache as it is outdated LOG.info('Image %(href)s was last modified at %(remote_time)s. ' 'Deleting the cached copy "%(cached_file)s since it was ' 'last modified at %(local_time)s and may be outdated.', {'href': href, 'remote_time': img_mtime, 'local_time': master_mtime, 'cached_file': master_path}) os.unlink(master_path) return False
def get_image_instance_info(node): """Gets the image information from the node. Get image information for the given node instance from its 'instance_info' property. :param node: a single Node. :returns: A dict with required image properties retrieved from node's 'instance_info'. :raises: MissingParameterValue, if image_source is missing in node's instance_info. Also raises same exception if kernel/ramdisk is missing in instance_info for non-glance images. """ info = {} info['image_source'] = node.instance_info.get('image_source') is_whole_disk_image = node.driver_internal_info.get('is_whole_disk_image') if not is_whole_disk_image: if not service_utils.is_glance_image(info['image_source']): info['kernel'] = node.instance_info.get('kernel') info['ramdisk'] = node.instance_info.get('ramdisk') error_msg = (_("Cannot validate image information for node %s because one " "or more parameters are missing from its instance_info.") % node.uuid) check_for_missing_params(info, error_msg) return info
def is_whole_disk_image(ctx, instance_info): """Find out if the image is a partition image or a whole disk image. :param ctx: an admin context :param instance_info: a node's instance info dict :returns True for whole disk images and False for partition images and None on no image_source or Error. """ image_source = instance_info.get('image_source') if not image_source: return is_whole_disk_image = False if glance_utils.is_glance_image(image_source): try: iproperties = get_image_properties(ctx, image_source) except Exception: return is_whole_disk_image = (not iproperties.get('kernel_id') and not iproperties.get('ramdisk_id')) else: # Non glance image ref if (not instance_info.get('kernel') and not instance_info.get('ramdisk')): is_whole_disk_image = True return is_whole_disk_image
def _delete_master_path_if_stale(master_path, href, ctx): """Delete image from cache if it is not up to date with href contents. :param master_path: path to an image in master cache :param href: image href :param ctx: context to use :returns: True if master_path is up to date with href contents, False if master_path was stale and was deleted or it didn't exist """ if service_utils.is_glance_image(href): # Glance image contents cannot be updated without changing image's UUID return os.path.exists(master_path) if os.path.exists(master_path): img_service = image_service.get_image_service(href, context=ctx) img_mtime = img_service.show(href).get('updated_at') if not img_mtime: # This means that href is not a glance image and doesn't have an # updated_at attribute LOG.warn(_LW("Image service couldn't determine last " "modification time of %(href)s, considering " "cached image up to date."), {'href': href}) return True master_mtime = utils.unix_file_modification_datetime(master_path) if img_mtime <= master_mtime: return True # Delete image from cache as it is outdated LOG.info(_LI('Image %(href)s was last modified at %(remote_time)s. ' 'Deleting the cached copy "%(cached_file)s since it was ' 'last modified at %(local_time)s and may be outdated.'), {'href': href, 'remote_time': img_mtime, 'local_time': master_mtime, 'cached_file': master_path}) os.unlink(master_path) return False
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 information for this driver to deploy images to the node. :param node: a single Node. :returns: A dict with the instance_info values. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ info = node.instance_info i_info = {} i_info["image_source"] = info.get("image_source") is_whole_disk_image = node.driver_internal_info.get("is_whole_disk_image") if not is_whole_disk_image: if i_info["image_source"] and not glance_service_utils.is_glance_image(i_info["image_source"]): i_info["kernel"] = info.get("kernel") i_info["ramdisk"] = info.get("ramdisk") i_info["root_gb"] = info.get("root_gb") error_msg = _("Cannot validate iSCSI deploy. Some parameters were missing" " in node's instance_info") deploy_utils.check_for_missing_params(i_info, error_msg) # Internal use only i_info["deploy_key"] = info.get("deploy_key") i_info["swap_mb"] = info.get("swap_mb", 0) i_info["ephemeral_gb"] = info.get("ephemeral_gb", 0) err_msg_invalid = _( "Cannot validate parameter for iSCSI deploy. " "Invalid parameter %(param)s. Reason: %(reason)s" ) for param in ("root_gb", "swap_mb", "ephemeral_gb"): try: int(i_info[param]) except ValueError: reason = _("%s is not an integer value.") % i_info[param] raise exception.InvalidParameterValue(err_msg_invalid % {"param": param, "reason": reason}) if is_whole_disk_image: if int(i_info["swap_mb"]) > 0 or int(i_info["ephemeral_gb"]) > 0: err_msg_invalid = _("Cannot deploy whole disk image with " "swap or ephemeral size set") raise exception.InvalidParameterValue(err_msg_invalid) return i_info i_info["ephemeral_format"] = info.get("ephemeral_format") i_info["configdrive"] = info.get("configdrive") if i_info["ephemeral_gb"] and not i_info["ephemeral_format"]: i_info["ephemeral_format"] = CONF.pxe.default_ephemeral_format preserve_ephemeral = info.get("preserve_ephemeral", False) try: i_info["preserve_ephemeral"] = strutils.bool_from_string(preserve_ephemeral, strict=True) except ValueError as e: raise exception.InvalidParameterValue(err_msg_invalid % {"param": "preserve_ephemeral", "reason": e}) return i_info
def _parse_instance_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 single Node. :returns: A dict with the instance_info and driver_info values. :raises: MissingParameterValue, image_source is missing in node's instance_info. Also raises same exception if kernel/ramdisk is missing in instance_info for non-glance images. """ info = {} info['image_source'] = node.instance_info.get('image_source') is_whole_disk_image = node.driver_internal_info.get('is_whole_disk_image') if not is_whole_disk_image: if not service_utils.is_glance_image(info['image_source']): info['kernel'] = node.instance_info.get('kernel') info['ramdisk'] = node.instance_info.get('ramdisk') error_msg = _("Cannot validate PXE bootloader. Some parameters were " "missing in node's instance_info.") deploy_utils.check_for_missing_params(info, error_msg) return info
def _get_ipxe_kernel_ramdisk(task, pxe_info): pxe_opts = {} node = task.node for label, option in (('deploy_kernel', 'deployment_aki_path'), ('deploy_ramdisk', 'deployment_ari_path')): image_href = pxe_info[label][0] if (CONF.pxe.ipxe_use_swift and service_utils.is_glance_image(image_href)): pxe_opts[option] = images.get_temp_url_for_glance_image( task.context, image_href) else: pxe_opts[option] = '/'.join([CONF.deploy.http_url, node.uuid, label]) # NOTE(pas-ha) do not use Swift TempURLs for kernel and ramdisk # of user image when boot_option is not local, # as this will break instance reboot later when temp urls have timed out. if 'kernel' in pxe_info: pxe_opts['aki_path'] = '/'.join( [CONF.deploy.http_url, node.uuid, 'kernel']) if 'ramdisk' in pxe_info: pxe_opts['ari_path'] = '/'.join( [CONF.deploy.http_url, node.uuid, 'ramdisk']) return pxe_opts
def build_instance_info_for_deploy(task): """Build instance_info necessary for deploying to a node.""" node = task.node instance_info = node.instance_info image_source = instance_info['image_source'] if service_utils.is_glance_image(image_source): glance = image_service.GlanceImageService(version=2, context=task.context) image_info = glance.show(image_source) swift_temp_url = glance.swift_temp_url(image_info) LOG.debug('Got image info: %(info)s for node %(node)s.', { 'info': image_info, 'node': node.uuid }) instance_info['image_url'] = swift_temp_url instance_info['image_checksum'] = image_info['checksum'] instance_info['image_disk_format'] = image_info['disk_format'] else: try: image_service.HttpImageService().validate_href(image_source) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error( _LE("Ansible deploy supports only HTTP(S) URLs as " "instance_info['image_source']. Either %s " "is not a valid HTTP(S) URL or " "is not reachable."), image_source) instance_info['image_url'] = image_source return instance_info
def is_whole_disk_image(ctx, instance_info): """Find out if the image is a partition image or a whole disk image. :param ctx: an admin context :param instance_info: a node's instance info dict :returns: True for whole disk images and False for partition images and None on no image_source or Error. """ image_source = instance_info.get('image_source') if not image_source: return is_whole_disk_image = False if glance_utils.is_glance_image(image_source): try: iproperties = get_image_properties(ctx, image_source) except Exception: return is_whole_disk_image = (not iproperties.get('kernel_id') and not iproperties.get('ramdisk_id')) else: # Non glance image ref if (not instance_info.get('kernel') and not instance_info.get('ramdisk')): is_whole_disk_image = True return is_whole_disk_image
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() self._validate_volume_boot(task) if not task.driver.storage.should_write_image(task): LOG.debug('Node %(node)s skips image validation because of ' 'booting from a remote volume.', {'node': task.node.uuid}) return d_info = _parse_deploy_info(task.node) if task.node.driver_internal_info.get('is_whole_disk_image'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def validate_http_provisioning_configuration(node): """Validate configuration options required to perform HTTP provisioning. :param node: an ironic node object :raises: MissingParameterValue if required option(s) is not set. """ image_source = node.instance_info.get('image_source') image_download_source = deploy_utils.get_image_download_source(node) if image_download_source not in ('swift', 'http', 'local'): raise exception.InvalidParameterValue( _('Invalid value for image_download_source: "%s". Valid values ' 'are swift, http or local.') % image_download_source) # NOTE(dtantsur): local HTTP configuration is required in two cases: # 1. Glance images with image_download_source == http # 2. File images (since we need to serve them to IPA) if (not image_source.startswith('file://') and image_download_source != 'local' and (not service_utils.is_glance_image(image_source) or image_download_source == 'swift')): return params = { '[deploy]http_url': CONF.deploy.http_url, '[deploy]http_root': CONF.deploy.http_root, '[deploy]http_image_subdir': CONF.deploy.http_image_subdir } error_msg = _('Node %s failed to validate http provisoning. Some ' 'configuration options were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node # Validate node capabilities deploy_utils.validate_capabilities(node) if not task.driver.storage.should_write_image(task): # NOTE(TheJulia): There is no reason to validate # image properties if we will not be writing an image # in a boot from volume case. As such, return to the caller. LOG.debug( 'Skipping complete deployment interface validation ' 'for node %s as it is set to boot from a remote ' 'volume.', node.uuid) return params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue( _("image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % { 'node': node.uuid, 'error': e }) validate_image_proxies(node)
def prepare_ramdisk(self, task, ramdisk_params): """Prepares the boot of deploy ramdisk using virtual media. This method prepares the boot of the deploy or rescue 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. :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. :raises: IloOperationError, if some operation on iLO failed. """ node = task.node # NOTE(TheJulia): If this method is being called by something # aside from deployment, clean and rescue, such as conductor takeover, # we should treat this as a no-op and move on otherwise we would # modify the state of the node due to virtual media operations. if node.provision_state not in (states.DEPLOYING, states.CLEANING, states.RESCUING, states.INSPECTING): return prepare_node_for_deploy(task) # Clear ilo_boot_iso if it's a glance image to force recreate # another one again (or use existing one in glance). # This is mainly for rebuild and rescue scenario. if service_utils.is_glance_image( node.instance_info.get('image_source')): instance_info = node.instance_info instance_info.pop('ilo_boot_iso', None) node.instance_info = instance_info node.save() # Eject all virtual media devices, as we are going to use them # during boot. ilo_common.eject_vmedia_devices(task) # NOTE(TheJulia): Since we're deploying, cleaning, or rescuing, # with virtual media boot, we should generate a token! manager_utils.add_secret_token(task.node, pregenerated=True) ramdisk_params['ipa-agent-token'] = \ task.node.driver_internal_info['agent_secret_token'] task.node.save() deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) ramdisk_params['BOOTIF'] = deploy_nic_mac if node.provision_state == states.RESCUING: iso = node.driver_info['ilo_rescue_iso'] else: iso = node.driver_info['ilo_deploy_iso'] ilo_common.setup_vmedia(task, iso, ramdisk_params)
def validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ node = task.node if not driver_utils.get_node_mac_addresses(task): raise exception.MissingParameterValue( _("Node %s does not have any port associated with it.") % node.uuid) 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): # Check if 'boot_option' and boot mode is compatible with # trusted boot. # NOTE(TheJulia): So in theory (huge theory here, not put to # practice or tested), that one can define the kernel as tboot # and define the actual kernel and ramdisk as appended data. # Similar to how one can iPXE load the XEN hypervisor. # tboot mailing list seem to indicate pxe/ipxe support, or # more specifically avoiding breaking the scenarios of use, # but there is also no definitive documentation on the subject. LOG.warning('Trusted boot has been requested for %(node)s in ' 'concert with iPXE. This is not a supported ' 'configuration for an ironic deployment.', {'node': node.uuid}) pxe.validate_boot_parameters_for_trusted_boot(node) pxe_utils.parse_driver_info(node) # NOTE(TheJulia): If we're not writing an image, we can skip # the remainder of this method. if (not task.driver.storage.should_write_image(task)): return d_info = deploy_utils.get_image_instance_info(node) if (node.driver_internal_info.get('is_whole_disk_image') or deploy_utils.get_boot_option(node) == 'local'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ node = task.node if not driver_utils.get_node_mac_addresses(task): raise exception.MissingParameterValue( _("Node %s does not have any port associated with it.") % node.uuid) 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): # Check if 'boot_option' and boot mode is compatible with # trusted boot. # NOTE(TheJulia): So in theory (huge theory here, not put to # practice or tested), that one can define the kernel as tboot # and define the actual kernel and ramdisk as appended data. # Similar to how one can iPXE load the XEN hypervisor. # tboot mailing list seem to indicate pxe/ipxe support, or # more specifically avoiding breaking the scenarios of use, # but there is also no definitive documentation on the subject. LOG.warning( 'Trusted boot has been requested for %(node)s in ' 'concert with iPXE. This is not a supported ' 'configuration for an ironic deployment.', {'node': node.uuid}) pxe.validate_boot_parameters_for_trusted_boot(node) pxe_utils.parse_driver_info(node) # NOTE(TheJulia): If we're not writing an image, we can skip # the remainder of this method. if (not task.driver.storage.should_write_image(task)): return d_info = deploy_utils.get_image_instance_info(node) if (node.driver_internal_info.get('is_whole_disk_image') or deploy_utils.get_boot_option(node) == 'local'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ node = task.node if not driver_utils.get_node_mac_addresses(task): raise exception.MissingParameterValue( _("Node %s does not have any port associated with it.") % node.uuid) # Get the boot_mode capability value. boot_mode = deploy_utils.get_boot_mode_for_deploy(node) 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.")) # iPXE and UEFI should not be configured together. if boot_mode == 'uefi': LOG.error(_LE("UEFI boot mode is not supported with " "iPXE boot enabled.")) raise exception.InvalidParameterValue(_( "Conflict: iPXE is enabled, but cannot be used with node" "%(node_uuid)s configured to use UEFI boot") % {'node_uuid': node.uuid}) if boot_mode == 'uefi': validate_boot_option_for_uefi(node) # Check the trusted_boot capabilities value. deploy_utils.validate_capabilities(node) if deploy_utils.is_trusted_boot_requested(node): # Check if 'boot_option' and boot mode is compatible with # trusted boot. validate_boot_parameters_for_trusted_boot(node) _parse_driver_info(node) d_info = _parse_instance_info(node) if node.driver_internal_info.get('is_whole_disk_image'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node # Validate node capabilities deploy_utils.validate_capabilities(node) if not task.driver.storage.should_write_image(task): # NOTE(TheJulia): There is no reason to validate # image properties if we will not be writing an image # in a boot from volume case. As such, return to the caller. LOG.debug('Skipping complete deployment interface validation ' 'for node %s as it is set to boot from a remote ' 'volume.', node.uuid) return params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue(_( "image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % {'node': node.uuid, 'error': e}) validate_image_proxies(node)
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 information for this driver to deploy images to the node. :param node: a single Node. :returns: A dict with the instance_info values. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ info = node.instance_info i_info = {} i_info['image_source'] = info.get('image_source') iwdi = node.driver_internal_info.get('is_whole_disk_image') if not iwdi: if (i_info['image_source'] and not service_utils.is_glance_image(i_info['image_source'])): i_info['kernel'] = info.get('kernel') i_info['ramdisk'] = info.get('ramdisk') i_info['root_gb'] = info.get('root_gb') error_msg = _("Cannot validate driver deploy. Some parameters were missing" " in node's instance_info") check_for_missing_params(i_info, error_msg) # This is used in many places, so keep it even for whole-disk images. # There is also a potential use case of creating an ephemeral partition via # cloud-init and telling ironic to avoid metadata wipe via setting # preserve_ephemeral (not saying it will work, but it seems possible). preserve_ephemeral = info.get('preserve_ephemeral', False) try: i_info['preserve_ephemeral'] = (strutils.bool_from_string( preserve_ephemeral, strict=True)) except ValueError as e: raise exception.InvalidParameterValue(_ERR_MSG_INVALID_DEPLOY % { 'param': 'preserve_ephemeral', 'reason': e }) if iwdi: if i_info.get('swap_mb') or i_info.get('ephemeral_mb'): err_msg_invalid = _("Cannot deploy whole disk image with " "swap or ephemeral size set") raise exception.InvalidParameterValue(err_msg_invalid) else: _validate_layout_properties(node, info, i_info) i_info['configdrive'] = info.get('configdrive') return i_info
def prepare_ramdisk(self, task, ramdisk_params): """Prepares the boot of deploy ramdisk using virtual media. This method prepares the boot of the deploy 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. :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. :raises: IloOperationError, if some operation on iLO failed. """ node = task.node # NOTE(TheJulia): If this method is being called by something # aside from deployment and clean, such as conductor takeover, we # should treat this as a no-op and move on otherwise we would modify # the state of the node due to virtual media operations. if (node.provision_state != states.DEPLOYING and node.provision_state != states.CLEANING): return # Powering off the Node before initiating boot for node cleaning. # If node is in system POST, setting boot device fails. manager_utils.node_power_action(task, states.POWER_OFF) if task.node.provision_state == states.DEPLOYING: prepare_node_for_deploy(task) # Clear ilo_boot_iso if it's a glance image to force recreate # another one again (or use existing one in glance). # This is mainly for rebuild scenario. if service_utils.is_glance_image( node.instance_info.get('image_source')): instance_info = node.instance_info instance_info.pop('ilo_boot_iso', None) node.instance_info = instance_info node.save() # Eject all virtual media devices, as we are going to use them # during boot. ilo_common.eject_vmedia_devices(task) deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) ramdisk_params['BOOTIF'] = deploy_nic_mac deploy_iso = node.driver_info['ilo_deploy_iso'] ilo_common.setup_vmedia(task, deploy_iso, ramdisk_params)
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. :raises: MissingParameterValue """ node = task.node # Check the boot_mode and boot_option capabilities values. deploy_utils.validate_capabilities(node) boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) if CONF.pxe.ipxe_enabled: if not CONF.pxe.http_url or not CONF.pxe.http_root: raise exception.MissingParameterValue( _("iPXE boot is enabled but no HTTP URL or HTTP " "root was specified.") ) # iPXE and UEFI should not be configured together. if boot_mode == "uefi": LOG.error(_LE("UEFI boot mode is not supported with " "iPXE boot enabled.")) raise exception.InvalidParameterValue( _( "Conflict: iPXE is enabled, but cannot be used with node" "%(node_uuid)s configured to use UEFI boot" ) % {"node_uuid": node.uuid} ) # Check if 'boot_option' is compatible with 'boot_mode' of uefi and # image being deployed if boot_mode == "uefi": validate_boot_option_for_uefi(task.node) if deploy_utils.is_trusted_boot_requested(task.node): # Check if 'boot_option' and boot mode is compatible with # trusted boot. validate_boot_parameters_for_trusted_boot(task.node) d_info = _parse_deploy_info(node) iscsi_deploy.validate(task) if node.driver_internal_info.get("is_whole_disk_image"): props = [] elif 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)
def build_instance_info_for_deploy(task): """Build instance_info necessary for deploying to a node. :param task: a TaskManager object containing the node :returns: a dictionary containing the properties to be updated in instance_info :raises: exception.ImageRefValidationFailed if image_source is not Glance href and is not HTTP(S) URL. """ node = task.node instance_info = node.instance_info iwdi = node.driver_internal_info.get('is_whole_disk_image') image_source = instance_info['image_source'] if service_utils.is_glance_image(image_source): glance = image_service.GlanceImageService(version=2, context=task.context) image_info = glance.show(image_source) swift_temp_url = glance.swift_temp_url(image_info) LOG.debug('Got image info: %(info)s for node %(node)s.', { 'info': image_info, 'node': node.uuid }) instance_info['image_url'] = swift_temp_url instance_info['image_checksum'] = image_info['checksum'] instance_info['image_disk_format'] = image_info['disk_format'] instance_info['image_container_format'] = ( image_info['container_format']) instance_info['image_tags'] = image_info.get('tags', []) instance_info['image_properties'] = image_info['properties'] if not iwdi: instance_info['kernel'] = image_info['properties']['kernel_id'] instance_info['ramdisk'] = image_info['properties']['ramdisk_id'] else: try: image_service.HttpImageService().validate_href(image_source) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error( _LE("Agent deploy supports only HTTP(S) URLs as " "instance_info['image_source']. Either %s " "is not a valid HTTP(S) URL or " "is not reachable."), image_source) instance_info['image_url'] = image_source if not iwdi: instance_info['image_type'] = 'partition' i_info = parse_instance_info(node) instance_info.update(i_info) else: instance_info['image_type'] = 'whole-disk-image' return instance_info
def _is_image_href_ordinary_file_name(image_href): """Check if image_href is a ordinary file name. This method judges if image_href is an ordinary file name or not, which is a file supposed to be stored in share file system. The ordinary file name is neither glance image href nor image service href. :returns: True if image_href is ordinary file name, False otherwise. """ return not (service_utils.is_glance_image(image_href) or urlparse.urlparse(image_href).scheme.lower() in image_service.protocol_mapping)
def validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ node = task.node if not driver_utils.get_node_mac_addresses(task): raise exception.MissingParameterValue( _("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): # Check if 'boot_option' and boot mode is compatible with # trusted boot. validate_boot_parameters_for_trusted_boot(node) pxe_utils.parse_driver_info(node) # NOTE(TheJulia): If we're not writing an image, we can skip # the remainder of this method. if (not task.driver.storage.should_write_image(task)): return d_info = deploy_utils.get_image_instance_info(node) if (node.driver_internal_info.get('is_whole_disk_image') or deploy_utils.get_boot_option(node) == 'local'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
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. :raises: MissingParameterValue """ node = task.node # Check the boot_mode and boot_option capabilities values. deploy_utils.validate_capabilities(node) boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) if CONF.pxe.ipxe_enabled: if not CONF.pxe.http_url or not CONF.pxe.http_root: raise exception.MissingParameterValue( _("iPXE boot is enabled but no HTTP URL or HTTP " "root was specified.")) # iPXE and UEFI should not be configured together. if boot_mode == 'uefi': LOG.error( _LE("UEFI boot mode is not supported with " "iPXE boot enabled.")) raise exception.InvalidParameterValue( _("Conflict: iPXE is enabled, but cannot be used with node" "%(node_uuid)s configured to use UEFI boot") % {'node_uuid': node.uuid}) # Check if 'boot_option' is compatible with 'boot_mode' of uefi and # image being deployed if boot_mode == 'uefi': validate_boot_option_for_uefi(task.node) if deploy_utils.is_trusted_boot_requested(task.node): # Check if 'boot_option' and boot mode is compatible with # trusted boot. validate_boot_parameters_for_trusted_boot(task.node) d_info = _parse_deploy_info(node) iscsi_deploy.validate(task) if node.driver_internal_info.get('is_whole_disk_image'): props = [] elif 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)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue( _("image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) is_whole_disk_image = node.driver_internal_info.get( 'is_whole_disk_image') # TODO(sirushtim): Remove once IPA has support for partition images. if is_whole_disk_image is False: raise exception.InvalidParameterValue( _("Node %(node)s is configured to use the %(driver)s driver " "which currently does not support deploying partition " "images.") % { 'node': node.uuid, 'driver': node.driver }) # Validate the root device hints deploy_utils.parse_root_device_hints(node) # Validate node capabilities deploy_utils.validate_capabilities(node) validate_image_proxies(node)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node params = {} image_source = node.instance_info.get("image_source") params["instance_info.image_source"] = image_source error_msg = _("Node %s failed to validate deploy image info. Some " "parameters were missing") % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get("image_checksum"): raise exception.MissingParameterValue( _("image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid ) check_image_size(task, image_source) is_whole_disk_image = node.driver_internal_info.get("is_whole_disk_image") # TODO(sirushtim): Remove once IPA has support for partition images. if is_whole_disk_image is False: raise exception.InvalidParameterValue( _( "Node %(node)s is configured to use the %(driver)s driver " "which currently does not support deploying partition " "images." ) % {"node": node.uuid, "driver": node.driver} ) # Validate the root device hints deploy_utils.parse_root_device_hints(node) # Validate node capabilities deploy_utils.validate_capabilities(node) validate_image_proxies(node)
def build_instance_info_for_deploy(task): """Build instance_info necessary for deploying to a node. :param task: a TaskManager object containing the node :returns: a dictionary containing the properties to be updated in instance_info :raises: exception.ImageRefValidationFailed if image_source is not Glance href and is not HTTP(S) URL. """ node = task.node instance_info = node.instance_info iwdi = node.driver_internal_info.get('is_whole_disk_image') image_source = instance_info['image_source'] if service_utils.is_glance_image(image_source): glance = image_service.GlanceImageService(version=2, context=task.context) image_info = glance.show(image_source) swift_temp_url = glance.swift_temp_url(image_info) LOG.debug('Got image info: %(info)s for node %(node)s.', {'info': image_info, 'node': node.uuid}) instance_info['image_url'] = swift_temp_url instance_info['image_checksum'] = image_info['checksum'] instance_info['image_disk_format'] = image_info['disk_format'] instance_info['image_container_format'] = ( image_info['container_format']) instance_info['image_tags'] = image_info.get('tags', []) instance_info['image_properties'] = image_info['properties'] if not iwdi: instance_info['kernel'] = image_info['properties']['kernel_id'] instance_info['ramdisk'] = image_info['properties']['ramdisk_id'] else: try: image_service.HttpImageService().validate_href(image_source) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error(_LE("Agent deploy supports only HTTP(S) URLs as " "instance_info['image_source']. Either %s " "is not a valid HTTP(S) URL or " "is not reachable."), image_source) instance_info['image_url'] = image_source if not iwdi: instance_info['image_type'] = 'partition' i_info = parse_instance_info(node) instance_info.update(i_info) else: instance_info['image_type'] = 'whole-disk-image' return instance_info
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue(_( "image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % {'node': node.uuid, 'error': e}) # Validate node capabilities deploy_utils.validate_capabilities(node) validate_image_proxies(node)
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: IloOperationError, if some operation on iLO fails. """ node = task.node # Clear ilo_boot_iso if it's a glance image to force recreate # another one again (or use existing one in glance). # This is mainly for rebuild scenario. if service_utils.is_glance_image( node.instance_info.get('image_source')): instance_info = node.instance_info instance_info.pop('ilo_boot_iso', None) node.instance_info = instance_info node.save() # Eject all virtual media devices, as we are going to use them # during deploy. ilo_common.eject_vmedia_devices(task) iscsi_deploy.cache_instance_image(task.context, node) iscsi_deploy.check_image_size(task) deploy_ramdisk_opts = iscsi_deploy.build_deploy_ramdisk_options(node) agent_opts = agent.build_agent_options(node) deploy_ramdisk_opts.update(agent_opts) deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) deploy_ramdisk_opts['BOOTIF'] = deploy_nic_mac deploy_iso = node.driver_info['ilo_deploy_iso'] _reboot_into(task, deploy_iso, deploy_ramdisk_opts) return states.DEPLOYWAIT
def _build_deploy_pxe_options(task, pxe_info): pxe_opts = {} node = task.node for label, option in (('deploy_kernel', 'deployment_aki_path'), ('deploy_ramdisk', 'deployment_ari_path')): if CONF.pxe.ipxe_enabled: image_href = pxe_info[label][0] if (CONF.pxe.ipxe_use_swift and service_utils.is_glance_image(image_href)): pxe_opts[option] = images.get_temp_url_for_glance_image( task.context, image_href) else: pxe_opts[option] = '/'.join([CONF.deploy.http_url, node.uuid, label]) else: pxe_opts[option] = pxe_utils.get_path_relative_to_tftp_root( pxe_info[label][1]) return pxe_opts
def setup_vmedia_for_boot(task, boot_iso, parameters=None): """Sets up the node to boot from the given ISO image. This method attaches the given boot_iso on the node and passes the required parameters to it via virtual floppy image. :param task: a TaskManager instance containing the node to act on. :param boot_iso: a bootable ISO image to attach to. Should be either of below: * A Swift object - It should be of format 'swift:<object-name>'. It is assumed that the image object is present in CONF.ilo.swift_ilo_container; * A Glance image - It should be format 'glance://<glance-image-uuid>' or just <glance-image-uuid>; * An HTTP(S) URL. :param parameters: the parameters to pass in the virtual floppy image in a dictionary. This is optional. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: SwiftOperationError, if any operation with Swift fails. :raises: IloOperationError, if attaching virtual media failed. """ LOG.info(_LI("Setting up node %s to boot from virtual media"), task.node.uuid) if parameters: floppy_image_temp_url = _prepare_floppy_image(task, parameters) attach_vmedia(task.node, 'FLOPPY', floppy_image_temp_url) boot_iso_url = None parsed_ref = urlparse.urlparse(boot_iso) if parsed_ref.scheme == 'swift': swift_api = swift.SwiftAPI() container = CONF.ilo.swift_ilo_container object_name = parsed_ref.path timeout = CONF.ilo.swift_object_expiry_timeout boot_iso_url = swift_api.get_temp_url( container, object_name, timeout) elif service_utils.is_glance_image(boot_iso): boot_iso_url = ( images.get_temp_url_for_glance_image(task.context, boot_iso)) attach_vmedia(task.node, 'CDROM', boot_iso_url or boot_iso)
def _create_rootfs_link(task): """Create Swift temp url for deployment root FS.""" rootfs = task.node.driver_info['deploy_squashfs'] if service_utils.is_glance_image(rootfs): glance = image_service.GlanceImageService(version=2, context=task.context) image_info = glance.show(rootfs) temp_url = glance.swift_temp_url(image_info) temp_url += '&filename=/root.squashfs' return temp_url try: image_service.HttpImageService().validate_href(rootfs) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error(_LE("Agent deploy supports only HTTP URLs as " "driver_info['deploy_squashfs']. Either %s " "is not a valid HTTP URL or " "is not reachable."), rootfs) return rootfs
def _validate_instance_image_info(task): """Validate instance image information for the task's node. :param task: a TaskManager instance containing the node to act on. :raises: InvalidParameterValue, if some information is invalid. :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are missing in the Glance image or 'kernel' and 'ramdisk' not provided in instance_info for non-Glance image. """ node = task.node d_info = _parse_deploy_info(node) if node.driver_internal_info.get('is_whole_disk_image'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def prepare_ramdisk(self, task, ramdisk_params): """Prepares the boot of deploy ramdisk using virtual media. This method prepares the boot of the deploy 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. :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. :raises: IloOperationError, if some operation on iLO failed. """ node = task.node # Clear ilo_boot_iso if it's a glance image to force recreate # another one again (or use existing one in glance). # This is mainly for rebuild scenario. if service_utils.is_glance_image( node.instance_info.get('image_source')): instance_info = node.instance_info instance_info.pop('ilo_boot_iso', None) node.instance_info = instance_info node.save() # Eject all virtual media devices, as we are going to use them # during deploy. ilo_common.eject_vmedia_devices(task) deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) ramdisk_params['BOOTIF'] = deploy_nic_mac deploy_iso = node.driver_info['ilo_deploy_iso'] ilo_common.setup_vmedia(task, deploy_iso, ramdisk_params)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue """ node = task.node params = {} if CONF.agent.manage_tftp: params['driver_info.deploy_kernel'] = node.driver_info.get( 'deploy_kernel') params['driver_info.deploy_ramdisk'] = node.driver_info.get( 'deploy_ramdisk') image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue(_( "image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) is_whole_disk_image = node.driver_internal_info.get( 'is_whole_disk_image') # TODO(sirushtim): Remove once IPA has support for partition images. if is_whole_disk_image is False: raise exception.InvalidParameterValue(_( "Node %(node)s is configured to use the %(driver)s driver " "which currently does not support deploying partition " "images.") % {'node': node.uuid, 'driver': node.driver}) # Validate the root device hints deploy_utils.parse_root_device_hints(node)