def download(self, image_href, image_file): """Downloads image to specified location. :param image_href: Image reference. :param image_file: File object to write data to. :raises: exception.ImageRefValidationFailed if source image file doesn't exist. :raises: exception.ImageDownloadFailed if exceptions were raised while writing to file or creating hard link. """ source_image_path = self.validate_href(image_href) dest_image_path = image_file.name local_device = os.stat(dest_image_path).st_dev try: # We should have read and write access to source file to create # hard link to it. if (local_device == os.stat(source_image_path).st_dev and os.access(source_image_path, os.R_OK | os.W_OK)): image_file.close() os.remove(dest_image_path) os.link(source_image_path, dest_image_path) else: filesize = os.path.getsize(source_image_path) with open(source_image_path, 'rb') as input_img: sendfile.sendfile(image_file.fileno(), input_img.fileno(), 0, filesize) except Exception as e: raise exception.ImageDownloadFailed(image_href=image_href, reason=e)
def download(self, image_href, data=None): """Calls out to Glance for data and writes data. :param image_href: The opaque image identifier. :param data: (Optional) File object to write data to. """ image_id = service_utils.parse_image_id(image_href) if 'file' in CONF.glance.allowed_direct_url_schemes: location = self._get_location(image_id) url = urlparse.urlparse(location) if url.scheme == "file": with open(url.path, "r") as f: filesize = os.path.getsize(f.name) sendfile.sendfile(data.fileno(), f.fileno(), 0, filesize) return image_chunks = self.call('data', image_id) # NOTE(dtantsur): when using Glance V2, image_chunks is a wrapper # around real data, so we have to check the wrapped data for None. if image_chunks.wrapped is None: raise exception.ImageDownloadFailed( image_href=image_href, reason=_('image contains no data.')) if data is None: return image_chunks else: for chunk in image_chunks: data.write(chunk)
def _download(self, image_href, data=None, method='data'): """Calls out to Glance for data and writes data. :param image_href: The opaque image identifier. :param data: (Optional) File object to write data to. """ image_id = service_utils.parse_image_id(image_href) if (self.version == 2 and 'file' in CONF.glance.allowed_direct_url_schemes): location = self._get_location(image_id) url = urlparse.urlparse(location) if url.scheme == "file": with open(url.path, "r") as f: filesize = os.path.getsize(f.name) sendfile.sendfile(data.fileno(), f.fileno(), 0, filesize) return image_chunks = self.call(method, image_id) # NOTE(dtantsur): when using Glance V2, image_chunks is a wrapper # around real data, so we have to check the wrapped data for None. # Glance V1 returns HTTP 404 in this case, so no need to fix it. # TODO(dtantsur): remove the hasattr check when we no longer support # Glance V1. if hasattr(image_chunks, 'wrapped') and image_chunks.wrapped is None: raise exception.ImageDownloadFailed( image_href=image_href, reason=_('image contains no data.')) if data is None: return image_chunks else: for chunk in image_chunks: data.write(chunk)
def download(self, image_href, image_file): """Downloads image to specified location. :param image_href: Image reference. :param image_file: File object to write data to. :raises: exception.ImageRefValidationFailed if GET request returned response code not equal to 200. :raises: exception.ImageDownloadFailed if: * IOError happened during file write; * GET request failed. """ try: verify = strutils.bool_from_string(CONF.webserver_verify_ca, strict=True) except ValueError: verify = CONF.webserver_verify_ca try: response = requests.get(image_href, stream=True, verify=verify) if response.status_code != http_client.OK: raise exception.ImageRefValidationFailed( image_href=image_href, reason=_("Got HTTP code %s instead of 200 in response " "to GET request.") % response.status_code) with response.raw as input_img: shutil.copyfileobj(input_img, image_file, IMAGE_CHUNK_SIZE) except (OSError, requests.ConnectionError, requests.RequestException, IOError) as e: raise exception.ImageDownloadFailed(image_href=image_href, reason=str(e))
def _download_image(self, href, master_path, dest_path, ctx=None, force_raw=True): """Download image by href and store at a given path. This method should be called with uuid-specific lock taken. :param href: image UUID or href to fetch :param master_path: destination master path :param dest_path: destination file path :param ctx: context :param force_raw: boolean value, whether to convert the image to raw format :raise ImageDownloadFailed: when the image cache and the image HTTP or TFTP location are on different file system, causing hard link to fail. """ # TODO(ghe): timeout and retry for downloads # TODO(ghe): logging when image cannot be created tmp_dir = tempfile.mkdtemp(dir=self.master_dir) tmp_path = os.path.join(tmp_dir, href.split('/')[-1]) try: _fetch(ctx, href, tmp_path, force_raw) # NOTE(dtantsur): no need for global lock here - master_path # will have link count >1 at any moment, so won't be cleaned up os.link(tmp_path, master_path) os.link(master_path, dest_path) except OSError as exc: msg = (_("Could not link image %(img_href)s from %(src_path)s " "to %(dst_path)s, error: %(exc)s") % { 'img_href': href, 'src_path': master_path, 'dst_path': dest_path, 'exc': exc }) LOG.error(msg) raise exception.ImageDownloadFailed(msg) finally: utils.rmtree_without_raise(tmp_dir)
def download(self, image_href, image_file): """Downloads image to specified location. :param image_href: Image reference. :param image_file: File object to write data to. :raises: exception.ImageRefValidationFailed if GET request returned response code not equal to 200. :raises: exception.ImageDownloadFailed if: * IOError happened during file write; * GET request failed. """ try: response = requests.get(image_href, stream=True) if response.status_code != 200: raise exception.ImageRefValidationFailed(image_href=image_href, reason=_("Got HTTP code %s instead of 200 in response to " "GET request.") % response.status_code) with response.raw as input_img: shutil.copyfileobj(input_img, image_file, IMAGE_CHUNK_SIZE) except (requests.RequestException, IOError) as e: raise exception.ImageDownloadFailed(image_href=image_href, reason=e)
def download(self, image_href, image_file): """Downloads image to specified location. :param image_href: Image reference. :param image_file: File object to write data to. :raises: exception.ImageRefValidationFailed if source image file doesn't exist. :raises: exception.ImageDownloadFailed if exceptions were raised while writing to file or creating hard link. """ source_image_path = self.validate_href(image_href) dest_image_path = image_file.name local_device = os.stat(dest_image_path).st_dev try: # We should have read and write access to source file to create # hard link to it. if (local_device == os.stat(source_image_path).st_dev and os.access(source_image_path, os.R_OK | os.W_OK)): image_file.close() os.remove(dest_image_path) os.link(source_image_path, dest_image_path) else: filesize = os.path.getsize(source_image_path) offset = 0 with open(source_image_path, 'rb') as input_img: while offset < filesize: # TODO(kaifeng) Use os.sendfile() and remove sendfile # dependency when python2 support is dropped. count = min(SENDFILE_CHUNK_SIZE, filesize - offset) nbytes_out = sendfile.sendfile(image_file.fileno(), input_img.fileno(), offset, count) offset += nbytes_out except Exception as e: raise exception.ImageDownloadFailed(image_href=image_href, reason=six.text_type(e))