Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
    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))
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
    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))