def _publish_image(image_file, object_name): """Make image file downloadable. Depending on ironic settings, pushes given file into Swift or copies it over to local HTTP server's document root and returns publicly accessible URL leading to the given file. :param image_file: path to file to publish :param object_name: name of the published file :return: a URL to download published file """ if CONF.redfish.use_swift: container = CONF.redfish.swift_container timeout = CONF.redfish.swift_object_expiry_timeout object_headers = {'X-Delete-After': str(timeout)} swift_api = swift.SwiftAPI() swift_api.create_object(container, object_name, image_file, object_headers=object_headers) image_url = swift_api.get_temp_url(container, object_name, timeout) else: public_dir = os.path.join(CONF.deploy.http_root, IMAGE_SUBDIR) if not os.path.exists(public_dir): os.mkdir(public_dir, 0o755) published_file = os.path.join(public_dir, object_name) try: os.link(image_file, published_file) os.chmod(image_file, CONF.redfish.file_permission) except OSError as exc: LOG.debug( "Could not hardlink image file %(image)s to public " "location %(public)s (will copy it over): " "%(error)s", { 'image': image_file, 'public': published_file, 'error': exc }) shutil.copyfile(image_file, published_file) os.chmod(published_file, CONF.redfish.file_permission) image_url = os.path.join(CONF.deploy.http_url, IMAGE_SUBDIR, object_name) image_url = _append_filename_param(image_url, os.path.basename(image_file)) return image_url
def _prepare_floppy_image(task, params): """Prepares the floppy image for passing the parameters. This method prepares a temporary vfat filesystem image. Then it adds two files into the image - one containing the authentication token and the other containing the parameters to be passed to the ramdisk. Then it uploads the file to Swift in 'swift_ilo_container', setting it to auto-expire after 'swift_object_expiry_timeout' seconds. Then it returns the temp url for the Swift object. :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. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: SwiftOperationError, if any operation with Swift fails. :returns: the Swift temp url for the floppy image. """ with tempfile.NamedTemporaryFile() as vfat_image_tmpfile_obj: files_info = {} token_tmpfile_obj = None vfat_image_tmpfile = vfat_image_tmpfile_obj.name # If auth_strategy is noauth, then no need to write token into # the image file. if task.context.auth_token: token_tmpfile_obj = tempfile.NamedTemporaryFile() token_tmpfile = token_tmpfile_obj.name utils.write_to_file(token_tmpfile, task.context.auth_token) files_info[token_tmpfile] = 'token' try: images.create_vfat_image(vfat_image_tmpfile, files_info=files_info, parameters=params) finally: if token_tmpfile_obj: token_tmpfile_obj.close() container = CONF.ilo.swift_ilo_container object_name = _get_floppy_image_name(task.node) timeout = CONF.ilo.swift_object_expiry_timeout object_headers = {'X-Delete-After': timeout} swift_api = swift.SwiftAPI() swift_api.create_object(container, object_name, vfat_image_tmpfile, object_headers=object_headers) temp_url = swift_api.get_temp_url(container, object_name, timeout) LOG.debug( "Uploaded floppy image %(object_name)s to %(container)s " "for deployment.", { 'object_name': object_name, 'container': container }) return temp_url
def test_head_object(self, connection_mock): swiftapi = swift.SwiftAPI() connection_obj_mock = connection_mock.return_value expected_head_result = {'a': 'b'} connection_obj_mock.head_object.return_value = expected_head_result actual_head_result = swiftapi.head_object('container', 'object') connection_obj_mock.head_object.assert_called_once_with( 'container', 'object') self.assertEqual(expected_head_result, actual_head_result)
def test_delete_object_exc(self, connection_mock): swiftapi = swift.SwiftAPI() exc = swift_exception.ClientException("Operation error") connection_obj_mock = connection_mock.return_value connection_obj_mock.delete_object.side_effect = exc self.assertRaises(exception.SwiftOperationError, swiftapi.delete_object, 'container', 'object') connection_obj_mock.delete_object.assert_called_once_with( 'container', 'object')
def test_delete_object_exc_resource_not_found(self, connection_mock): swiftapi = swift.SwiftAPI() exc = swift_exception.ClientException( "Resource not found", http_status=http_client.NOT_FOUND) connection_obj_mock = connection_mock.return_value connection_obj_mock.delete_object.side_effect = exc self.assertRaises(exception.SwiftObjectNotFoundError, swiftapi.delete_object, 'container', 'object') connection_obj_mock.delete_object.assert_called_once_with( 'container', 'object')
def test___init__(self, connection_mock): swift.SwiftAPI() params = {'retries': 2, 'insecure': 0, 'user': '******', 'tenant_name': 'tenant', 'key': 'password', 'authurl': 'http://authurl/v2.0', 'auth_version': '2'} connection_mock.assert_called_once_with(**params)
def test_create_object_create_container_fails(self, open_mock, connection_mock): swiftapi = swift.SwiftAPI() connection_obj_mock = connection_mock.return_value connection_obj_mock.put_container.side_effect = self.swift_exception self.assertRaises(exception.SwiftOperationError, swiftapi.create_object, 'container', 'object', 'some-file-location') connection_obj_mock.put_container.assert_called_once_with('container') self.assertFalse(connection_obj_mock.put_object.called)
def _prepare_floppy_image(cls, task, params=None): """Prepares the floppy image for passing the parameters. This method prepares a temporary VFAT filesystem image and adds a file into the image which contains parameters to be passed to the ramdisk. Then this method uploads built image to Swift '[redfish]swift_container', setting it to auto expire after '[redfish]swift_object_expiry_timeout' seconds. Finally, a temporary Swift URL is returned addressing Swift object just created. :param task: a TaskManager instance containing the node to act on. :param params: a dictionary containing 'parameter name'->'value' mapping to be passed to deploy or rescue image via floppy image. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: SwiftOperationError, if any operation with Swift fails. :returns: image URL for the floppy image. """ object_name = cls._get_floppy_image_name(task.node) container = CONF.redfish.swift_container timeout = CONF.redfish.swift_object_expiry_timeout object_headers = {'X-Delete-After': str(timeout)} swift_api = swift.SwiftAPI() LOG.debug("Trying to create floppy image for node " "%(node)s", {'node': task.node.uuid}) with tempfile.NamedTemporaryFile( dir=CONF.tempdir, suffix='.img') as vfat_image_tmpfile_obj: vfat_image_tmpfile = vfat_image_tmpfile_obj.name images.create_vfat_image(vfat_image_tmpfile, parameters=params) swift_api.create_object(container, object_name, vfat_image_tmpfile, object_headers=object_headers) image_url = swift_api.get_temp_url(container, object_name, timeout) image_url = cls._append_filename_param(image_url, 'bootme.img') LOG.debug( "Created floppy image %(name)s in Swift for node %(node)s, " "exposed as temporary URL " "%(url)s", { 'node': task.node.uuid, 'name': object_name, 'url': image_url }) return image_url
def test___init__(self, connection_mock, keystone_mock): """Check if client is properly initialized with swift""" self.config(group='swift', endpoint_override='http://example.com/objects') swift.SwiftAPI() connection_mock.assert_called_once_with( retries=2, session=keystone_mock.return_value, timeout=42, insecure=True, cert='spam', cert_key='ham', os_options={'object_storage_url': 'http://example.com/objects'})
def test_get_temp_url(self, gen_temp_url_mock, connection_mock, keystone_mock): swiftapi = swift.SwiftAPI() connection_obj_mock = connection_mock.return_value connection_obj_mock.url = 'http://host/v1/AUTH_tenant_id' head_ret_val = {'x-account-meta-temp-url-key': 'secretkey'} connection_obj_mock.head_account.return_value = head_ret_val gen_temp_url_mock.return_value = 'temp-url-path' temp_url_returned = swiftapi.get_temp_url('container', 'object', 10) connection_obj_mock.head_account.assert_called_once_with() object_path_expected = '/v1/AUTH_tenant_id/container/object' gen_temp_url_mock.assert_called_once_with(object_path_expected, 10, 'secretkey', 'GET') self.assertEqual('http://host/temp-url-path', temp_url_returned)
def _clean_up_boot_iso_for_instance(node): """Deletes the boot ISO created in Swift for the instance. :param node: an ironic node object. """ swift_api = swift.SwiftAPI() container = CONF.ilo.swift_ilo_container boot_iso_object_name = _get_boot_iso_object_name(node) try: swift_api.delete_object(container, boot_iso_object_name) except exception.SwiftOperationError as e: LOG.exception(_LE("Failed to clean up boot ISO for %(node)s." "Error: %(error)s."), {'node': node.uuid, 'error': e})
def test_create_object_put_object_fails(self, open_mock, connection_mock): swiftapi = swift.SwiftAPI() mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'file-object' open_mock.return_value = mock_file_handle connection_obj_mock = connection_mock.return_value connection_obj_mock.head_account.side_effect = None connection_obj_mock.put_object.side_effect = self.swift_exception self.assertRaises(exception.SwiftOperationError, swiftapi.create_object, 'container', 'object', 'some-file-location') connection_obj_mock.put_container.assert_called_once_with('container') connection_obj_mock.put_object.assert_called_once_with('container', 'object', 'file-object', headers=None)
def _store_configdrive(node, configdrive): """Handle the storage of the config drive. If configured, the config drive data are uploaded to a swift endpoint. The Node's instance_info is updated to include either the temporary Swift URL from the upload, or if no upload, the actual config drive data. :param node: an Ironic node object. :param configdrive: A gzipped and base64 encoded configdrive. :raises: SwiftOperationError if an error occur when uploading the config drive to the swift endpoint. :raises: ConfigInvalid if required keystone authorization credentials with swift are missing. """ if CONF.deploy.configdrive_use_object_store: # Don't store the JSON source in swift. if isinstance(configdrive, dict): configdrive = utils.build_configdrive(node, configdrive) # NOTE(lucasagomes): No reason to use a different timeout than # the one used for deploying the node timeout = ( CONF.conductor.configdrive_swift_temp_url_duration or CONF.conductor.deploy_callback_timeout # The documented default in ironic.conf.conductor or 1800) container = CONF.conductor.configdrive_swift_container object_name = _get_configdrive_obj_name(node) object_headers = {'X-Delete-After': str(timeout)} with tempfile.NamedTemporaryFile(dir=CONF.tempdir, mode="wt") as fileobj: fileobj.write(configdrive) fileobj.flush() swift_api = swift.SwiftAPI() swift_api.create_object(container, object_name, fileobj.name, object_headers=object_headers) configdrive = swift_api.get_temp_url(container, object_name, timeout) i_info = node.instance_info i_info['configdrive'] = configdrive node.instance_info = i_info node.save()
def test___init__(self, connection_mock, keystone_mock): sess = mock.Mock() sess.get_endpoint.return_value = 'http://swift:8080' sess.get_token.return_value = 'fake_token' sess.verify = '/path/to/ca/file' keystone_mock.return_value = sess swift.SwiftAPI() params = { 'retries': 2, 'preauthurl': 'http://swift:8080', 'preauthtoken': 'fake_token', 'insecure': False, 'cacert': '/path/to/ca/file' } connection_mock.assert_called_once_with(**params)
def test_create_object(self, open_mock, connection_mock, keystone_mock): swiftapi = swift.SwiftAPI() connection_obj_mock = connection_mock.return_value mock_file_handle = mock.MagicMock(spec=io.BytesIO) mock_file_handle.__enter__.return_value = 'file-object' open_mock.return_value = mock_file_handle connection_obj_mock.put_object.return_value = 'object-uuid' object_uuid = swiftapi.create_object('container', 'object', 'some-file-location') connection_obj_mock.put_container.assert_called_once_with('container') connection_obj_mock.put_object.assert_called_once_with( 'container', 'object', 'file-object', headers=None) self.assertEqual('object-uuid', object_uuid)
def _prepare_floppy_image(task, params): """Prepares the floppy image for passing the parameters. This method prepares a temporary vfat filesystem image. Then it adds a file into the image which contains the parameters to be passed to the ramdisk. After adding the parameters, it then uploads the file either to Swift in 'swift_ilo_container', setting it to auto-expire after 'swift_object_expiry_timeout' seconds or in web server. Then it returns the temp url for the Swift object or the http url for the uploaded floppy image depending upon value of CONF.ilo.use_web_server_for_images. :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. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: SwiftOperationError, if any operation with Swift fails. :returns: the Swift temp url for the floppy image. """ with tempfile.NamedTemporaryFile( dir=CONF.tempdir) as vfat_image_tmpfile_obj: vfat_image_tmpfile = vfat_image_tmpfile_obj.name images.create_vfat_image(vfat_image_tmpfile, parameters=params) object_name = _get_floppy_image_name(task.node) if CONF.ilo.use_web_server_for_images: image_url = copy_image_to_web_server(vfat_image_tmpfile, object_name) return image_url else: container = CONF.ilo.swift_ilo_container timeout = CONF.ilo.swift_object_expiry_timeout object_headers = {'X-Delete-After': timeout} swift_api = swift.SwiftAPI() swift_api.create_object(container, object_name, vfat_image_tmpfile, object_headers=object_headers) temp_url = swift_api.get_temp_url(container, object_name, timeout) LOG.debug( "Uploaded floppy image %(object_name)s to %(container)s " "for deployment.", { 'object_name': object_name, 'container': container }) return temp_url
def get_swift_url(parsed_url): """Gets swift temp url. It generates a temp url for the swift based firmware url to the target file. Expecting url as swift://containername/objectname. :param parsed_url: Parsed url object. :raises: SwiftOperationError, on failure to get url from swift. """ # Extract container name container = parsed_url.netloc # Extract the object name from the path of the form: # ``/objectname`` OR # ``/pseudo-folder/objectname`` # stripping the leading '/' character. objectname = parsed_url.path.lstrip('/') timeout = CONF.ilo.swift_object_expiry_timeout # Generate temp url using swift API return swift.SwiftAPI().get_temp_url(container, objectname, timeout)
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 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("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 _clean_up_boot_iso_for_instance(node): """Deletes the boot ISO if it was created in Swift for the instance. :param node: an ironic node object. """ ilo_boot_iso = node.instance_info.get('ilo_boot_iso') if not (ilo_boot_iso and ilo_boot_iso.startswith('swift')): return swift_api = swift.SwiftAPI() container = CONF.ilo.swift_ilo_container boot_iso_object_name = _get_boot_iso_object_name(node) try: swift_api.delete_object(container, boot_iso_object_name) except exception.SwiftOperationError as e: LOG.exception( _LE("Failed to clean up boot ISO for %(node)s." "Error: %(error)s."), { 'node': node.uuid, 'error': e })
def _download_swift_based_fw_to(self, target_file): """Swift based firmware file downloader It generates a temp url for the swift based firmware url and then downloads the firmware file via http based downloader to the target file. Expecting url as swift://containername/objectname :param target_file: destination file for downloading the original firmware file. :raises: SwiftOperationError, on failure to download from swift. :raises: ImageDownloadFailed, on failure to download the original file. """ # Extract container name and object name container = self.parsed_url.netloc objectname = os.path.basename(self.parsed_url.path) timeout = CONF.ilo.swift_object_expiry_timeout # Generate temp url using swift API tempurl = swift.SwiftAPI().get_temp_url(container, objectname, timeout) # set the parsed_url attribute to the newly created tempurl from swift and # delegate the dowloading job to the http_based downloader self.parsed_url = urlparse.urlparse(tempurl) _download_http_based_fw_to(self, target_file)
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. The boot iso should be present in either Glance or in Swift. If present in Glance, it should be of format 'glance:<glance-image-uuid>'. If present in Swift, it should be of format 'swift:<object-name>'. It is assumed that object is present in CONF.ilo.swift_ilo_container. :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_temp_url = None scheme, boot_iso_ref = boot_iso.split(':') if scheme == 'swift': swift_api = swift.SwiftAPI() container = CONF.ilo.swift_ilo_container object_name = boot_iso_ref timeout = CONF.ilo.swift_object_expiry_timeout boot_iso_temp_url = swift_api.get_temp_url(container, object_name, timeout) elif scheme == 'glance': glance_uuid = boot_iso_ref boot_iso_temp_url = images.get_temp_url_for_glance_image(task.context, glance_uuid) attach_vmedia(task.node, 'CDROM', boot_iso_temp_url)
def store_ramdisk_logs(node, logs, label=None): """Store the ramdisk logs. This method stores the ramdisk logs according to the configured storage backend. :param node: A node object. :param logs: A gzipped and base64 encoded string containing the logs archive. :param label: A string to label the log file such as a clean step name. :raises: OSError if the directory to save the logs cannot be created. :raises: IOError when the logs can't be saved to the local file system. :raises: SwiftOperationError, if any operation with Swift fails. """ logs_file_name = get_ramdisk_logs_file_name(node, label=label) data = base64.decode_as_bytes(logs) if CONF.agent.deploy_logs_storage_backend == 'local': if not os.path.exists(CONF.agent.deploy_logs_local_path): os.makedirs(CONF.agent.deploy_logs_local_path) log_path = os.path.join(CONF.agent.deploy_logs_local_path, logs_file_name) with open(log_path, 'wb') as f: f.write(data) elif CONF.agent.deploy_logs_storage_backend == 'swift': with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as f: f.write(data) f.flush() # convert days to seconds timeout = CONF.agent.deploy_logs_swift_days_to_expire * 86400 object_headers = {'X-Delete-After': str(timeout)} swift_api = swift.SwiftAPI() swift_api.create_object(CONF.agent.deploy_logs_swift_container, logs_file_name, f.name, object_headers=object_headers)
def remove_image_from_swift(object_name, associated_with=None): """Removes the given image from swift. This method removes the given image name from swift. It deletes the image if it exists in CONF.ilo.swift_ilo_container :param object_name: The name of the object which needs to be removed from swift. :param associated_with: string to depict the component/operation this object is associated to. """ container = CONF.ilo.swift_ilo_container try: swift_api = swift.SwiftAPI() swift_api.delete_object(container, object_name) except exception.SwiftObjectNotFoundError as e: LOG.warning( _LW("Temporary object %(associated_with_msg)s" "was already deleted from Swift. Error: %(err)s"), { 'associated_with_msg': ("associated with %s " % associated_with if associated_with else ""), 'err': e }) except exception.SwiftOperationError as e: LOG.exception( _LE("Error while deleting temporary swift object %(object_name)s " "%(associated_with_msg)s from %(container)s. Error: %(err)s"), { 'object_name': object_name, 'container': container, 'associated_with_msg': ("associated with %s" % associated_with if associated_with else ""), 'err': e })
def test___init___radosgw(self, connection_mock, swift_session_mock): """Check if client is properly initialized with radosgw""" auth_url = 'http://1.2.3.4' username = '******' password = '******' CONF.set_override('object_store_endpoint_type', 'radosgw', group='deploy') opts = [cfg.StrOpt('auth_url'), cfg.StrOpt('username'), cfg.StrOpt('password')] CONF.register_opts(opts, group='swift') CONF.set_override('auth_url', auth_url, group='swift') CONF.set_override('username', username, group='swift') CONF.set_override('password', password, group='swift') swift.SwiftAPI() params = {'authurl': auth_url, 'user': username, 'key': password} connection_mock.assert_called_once_with(**params) self.assertFalse(swift_session_mock.called)
def _delete_from_swift(task, container, object_name): LOG.debug( "Cleaning up image %(name)s from Swift container " "%(container)s for node " "%(node)s", { 'node': task.node.uuid, 'name': object_name, 'container': container }) swift_api = swift.SwiftAPI() try: swift_api.delete_object(container, object_name) except exception.SwiftOperationError as e: LOG.warning( "Failed to clean up image %(image)s for node " "%(node)s. Error: %(error)s.", { 'node': task.node.uuid, 'image': object_name, 'error': e })
def _clean_up_boot_iso_for_instance(node): """Deletes the boot ISO if it was created for the instance. :param node: an ironic node object. """ boot_iso_object_name = virtual_media_base.get_iso_image_name(node) if CONF.ilo.use_web_server_for_images: boot_iso_path = os.path.join(CONF.deploy.http_root, boot_iso_object_name) ironic_utils.unlink_without_raise(boot_iso_path) else: swift_api = swift.SwiftAPI() container = CONF.ilo.swift_ilo_container try: swift_api.delete_object(container, boot_iso_object_name) except exception.SwiftOperationError as e: LOG.exception( "Failed to clean up boot ISO for node " "%(node)s. Error: %(error)s.", { 'node': node.uuid, 'error': e })
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 the floppy image if it exists in CONF.ilo.swift_ilo_container. It also ejects both virtual media cdrom and virtual media floppy. :param task: a TaskManager instance containing the node to act on. """ LOG.debug("Cleaning up node %s after virtual media boot", task.node.uuid) container = CONF.ilo.swift_ilo_container object_name = _get_floppy_image_name(task.node) try: swift_api = swift.SwiftAPI() swift_api.delete_object(container, object_name) except exception.SwiftOperationError as e: LOG.exception( _LE("Error while deleting %(object_name)s from " "%(container)s. Error: %(error)s"), { 'object_name': object_name, 'container': container, 'error': e }) ilo_object = get_ilo_object(task.node) for device in ('FLOPPY', 'CDROM'): try: ilo_object.eject_virtual_media(device) except ilo_error.IloError as ilo_exception: LOG.exception( _LE("Error while ejecting virtual media %(device)s " "from node %(uuid)s. Error: %(error)s"), { 'device': device, 'uuid': task.node.uuid, 'error': ilo_exception })
def unpublish_image(self, object_name): """Withdraw the image previously made downloadable. Depending on ironic settings, removes previously published file from where it has been published - Swift or local HTTP server's document root. :param object_name: name of the published file (optional) """ if self._is_swift_enabled(): container = self._container swift_api = swift.SwiftAPI() LOG.debug( "Cleaning up image %(name)s from Swift container " "%(container)s", { 'name': object_name, 'container': container }) try: swift_api.delete_object(container, object_name) except exception.SwiftOperationError as exc: LOG.warning( "Failed to clean up image %(image)s. Error: " "%(error)s.", { 'image': object_name, 'error': exc }) else: published_file = os.path.join(CONF.deploy.http_root, self._image_subdir, object_name) ironic_utils.unlink_without_raise(published_file)
def _clean_up_boot_iso_for_instance(node): """Deletes the boot ISO if it was created for the instance. :param node: an ironic node object. """ ilo_boot_iso = node.instance_info.get('ilo_boot_iso') if not ilo_boot_iso: return if ilo_boot_iso.startswith('swift'): swift_api = swift.SwiftAPI() container = CONF.ilo.swift_ilo_container boot_iso_object_name = _get_boot_iso_object_name(node) try: swift_api.delete_object(container, boot_iso_object_name) except exception.SwiftOperationError as e: LOG.exception("Failed to clean up boot ISO for node " "%(node)s. Error: %(error)s.", {'node': node.uuid, 'error': e}) elif CONF.ilo.use_web_server_for_images: result = urlparse.urlparse(ilo_boot_iso) ilo_boot_iso_name = os.path.basename(result.path) boot_iso_path = os.path.join( CONF.deploy.http_root, ilo_boot_iso_name) ironic_utils.unlink_without_raise(boot_iso_path)
def _get_boot_iso(task, root_uuid): """This method returns a boot ISO to boot the node. It chooses one of the three options in the order as below: 1. Does nothing if 'ilo_boot_iso' is present in node's instance_info and 'boot_iso_created_in_web_server' is not set in 'driver_internal_info'. 2. Image deployed has a meta-property 'boot_iso' in Glance. This should refer to the UUID of the boot_iso which exists in Glance. 3. Generates a boot ISO on the fly using kernel and ramdisk mentioned in the image deployed. It uploads the generated boot ISO to Swift. :param task: a TaskManager instance containing the node to act on. :param root_uuid: the uuid of the root partition. :returns: boot ISO URL. 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 URL. On error finding the boot iso, it returns None. :raises: MissingParameterValue, if any of the required parameters are missing in the node's driver_info or instance_info. :raises: InvalidParameterValue, if any of the parameters have invalid value in the node's driver_info or instance_info. :raises: SwiftOperationError, if operation with Swift fails. :raises: ImageCreationFailed, if creation of boot ISO failed. :raises: exception.ImageRefValidationFailed if ilo_boot_iso is not HTTP(S) URL. """ LOG.debug("Trying to get a boot ISO to boot the baremetal node") # Option 1 - Check if user has provided ilo_boot_iso in node's # instance_info driver_internal_info = task.node.driver_internal_info boot_iso_created_in_web_server = ( driver_internal_info.get('boot_iso_created_in_web_server')) if (task.node.instance_info.get('ilo_boot_iso') and not boot_iso_created_in_web_server): LOG.debug("Using ilo_boot_iso provided in node's instance_info") boot_iso = task.node.instance_info['ilo_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 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 task.node.instance_info['ilo_boot_iso'] # Option 2 - Check if user has provided a boot_iso in Glance. If boot_iso # is a supported non-glance href execution will proceed to option 3. deploy_info = _parse_deploy_info(task.node) image_href = deploy_info['image_source'] image_properties = ( images.get_image_properties( task.context, image_href, ['boot_iso', 'kernel_id', 'ramdisk_id'])) boot_iso_uuid = image_properties.get('boot_iso') kernel_href = (task.node.instance_info.get('kernel') or image_properties.get('kernel_id')) ramdisk_href = (task.node.instance_info.get('ramdisk') or image_properties.get('ramdisk_id')) if boot_iso_uuid: LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid) return boot_iso_uuid if not kernel_href or not ramdisk_href: LOG.error("Unable to find kernel or ramdisk for " "image %(image)s to generate boot ISO for %(node)s", {'image': image_href, 'node': task.node.uuid}) return # NOTE(rameshg87): Functionality to share the boot ISOs created for # similar instances (instances with same deployed image) is # not implemented as of now. Creation/Deletion of such a shared boot ISO # will require synchronisation across conductor nodes for the shared boot # ISO. Such a synchronisation mechanism doesn't exist in ironic as of now. # Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift # or web server and provide its name. deploy_iso_uuid = deploy_info['ilo_deploy_iso'] boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node) boot_iso_object_name = _get_boot_iso_object_name(task.node) kernel_params = "" if deploy_utils.get_boot_option(task.node) == "ramdisk": i_info = task.node.instance_info kernel_params = "root=/dev/ram0 text " kernel_params += i_info.get("ramdisk_kernel_arguments", "") else: kernel_params = CONF.pxe.pxe_append_params with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj: boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, deploy_iso_uuid, root_uuid, kernel_params, boot_mode) if CONF.ilo.use_web_server_for_images: boot_iso_url = ( ilo_common.copy_image_to_web_server(boot_iso_tmp_file, boot_iso_object_name)) driver_internal_info = task.node.driver_internal_info driver_internal_info['boot_iso_created_in_web_server'] = True task.node.driver_internal_info = driver_internal_info task.node.save() LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s", {'boot_iso': boot_iso_url, 'node': task.node.uuid}) return boot_iso_url else: container = CONF.ilo.swift_ilo_container swift_api = swift.SwiftAPI() swift_api.create_object(container, boot_iso_object_name, boot_iso_tmp_file) LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name) return 'swift:%s' % boot_iso_object_name