def delete(self, location, context=None): """Takes a `glance.store.location.Location` object that indicates where to find the image file to delete :location `glance.store.location.Location` object, supplied from glance.store.location.get_location_from_uri() :raises NotFound if image does not exist """ file_path = '[%s] %s' % ( self.datastore_name, location.store_location.path[len(DS_URL_PREFIX):]) search_index_moref = self._service_content.searchIndex dc_moref = self._session.invoke_api(self._session.vim, 'FindByInventoryPath', search_index_moref, inventoryPath=self.datacenter_path) delete_task = self._session.invoke_api( self._session.vim, 'DeleteDatastoreFile_Task', self._service_content.fileManager, name=file_path, datacenter=dc_moref) try: self._session.wait_for_task(delete_task) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_('Failed to delete image %(image)s content.') % {'image': location.image_id})
def add(self, image_id, image_file, image_size, context=None): """ Stores an image file with supplied identifier to the backend storage system and returns a tuple containing information about the stored image. :param image_id: The opaque image identifier :param image_file: The image data to write, as a file-like object :param image_size: The size of the image data to write, in bytes :retval tuple of URL in backing store, bytes written, checksum and a dictionary with storage system specific information :raises `glance.store.exceptions.Duplicate` if the image already existed :note By default, the backend writes the image data to a file `/<DATADIR>/<ID>`, where <DATADIR> is the value of the filesystem_store_datadir configuration option and <ID> is the supplied image ID. """ datadir = self._find_best_datadir(image_size) filepath = os.path.join(datadir, str(image_id)) if os.path.exists(filepath): raise exceptions.Duplicate(image=filepath) checksum = hashlib.md5() bytes_written = 0 try: with open(filepath, 'wb') as f: for buf in utils.chunkreadable(image_file, ChunkedFile.CHUNKSIZE): bytes_written += len(buf) checksum.update(buf) f.write(buf) except IOError as e: if e.errno != errno.EACCES: self._delete_partial(filepath, image_id) errors = {errno.EFBIG: exceptions.StorageFull(), errno.ENOSPC: exceptions.StorageFull(), errno.EACCES: exceptions.StorageWriteDenied()} raise errors.get(e.errno, e) except Exception: with excutils.save_and_reraise_exception(): self._delete_partial(filepath, image_id) checksum_hex = checksum.hexdigest() metadata = self._get_metadata() LOG.debug(_("Wrote %(bytes_written)d bytes to %(filepath)s with " "checksum %(checksum_hex)s"), {'bytes_written': bytes_written, 'filepath': filepath, 'checksum_hex': checksum_hex}) return ('file://%s' % filepath, bytes_written, checksum_hex, metadata)
def remove_path_on_error(path, remove=delete_if_exists): """Protect code that wants to operate on PATH atomically. Any exception will cause PATH to be removed. :param path: File to work with :param remove: Optional function to remove passed path """ try: yield except Exception: with excutils.save_and_reraise_exception(): remove(path)
def add(self, image_id, image_file, image_size, context=None): """Stores an image file with supplied identifier to the backend storage system and returns a tuple containing information about the stored image. :param image_id: The opaque image identifier :param image_file: The image data to write, as a file-like object :param image_size: The size of the image data to write, in bytes :retval tuple of URL in backing store, bytes written, checksum and a dictionary with storage system specific information :raises `glance.common.exceptions.Duplicate` if the image already existed `glance.common.exceptions.UnexpectedStatus` if the upload request returned an unexpected status. The expected responses are 201 Created and 200 OK. """ checksum = hashlib.md5() image_file = _Reader(image_file, checksum) loc = StoreLocation({'scheme': self.scheme, 'server_host': self.server_host, 'image_dir': self.store_image_dir, 'datacenter_path': self.datacenter_path, 'datastore_name': self.datastore_name, 'image_id': image_id}) cookie = self._build_vim_cookie_header( self._session.vim.client.options.transport.cookiejar) headers = {'Connection': 'Keep-Alive', 'Cookie': cookie, 'Transfer-Encoding': 'chunked'} try: conn = self._get_http_conn('PUT', loc, headers, content=image_file) res = conn.getresponse() except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_('Failed to upload content of image ' '%(image)s') % {'image': image_id}) if res.status == httplib.CONFLICT: raise exceptions.Duplicate(_("Image file %(image_id)s already " "exists!") % {'image_id': image_id}) if res.status not in (httplib.CREATED, httplib.OK): msg = (_('Failed to upload content of image %(image)s') % {'image': image_id}) LOG.error(msg) raise exceptions.UnexpectedStatus(status=res.status, body=res.read()) return (loc.get_uri(), image_file.size, checksum.hexdigest(), {})
def _query(self, location, method, headers, depth=0): if depth > MAX_REDIRECTS: msg = ("The HTTP URL exceeded %(max_redirects)s maximum " "redirects." % {'max_redirects': MAX_REDIRECTS}) LOG.debug(msg) raise exceptions.MaxRedirectsExceeded(redirects=MAX_REDIRECTS) loc = location.store_location try: conn = self._get_http_conn(method, loc, headers) resp = conn.getresponse() except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_('Failed to access image %(image)s content.') % {'image': location.image_id}) if resp.status >= 400: if resp.status == httplib.NOT_FOUND: msg = 'VMware datastore could not find image at URI.' LOG.debug(msg) raise exceptions.NotFound(message=msg) msg = ('HTTP request returned a %(status)s status code.' % {'status': resp.status}) LOG.debug(msg) raise exceptions.BadStoreUri(msg) location_header = resp.getheader('location') if location_header: if resp.status not in (301, 302): msg = ("The HTTP URL %(path)s attempted to redirect " "with an invalid %(status)s status code." % {'path': loc.path, 'status': resp.status}) LOG.debug(msg) raise exceptions.BadStoreUri(msg) location_class = glance.store.location.Location new_loc = location_class(location.store_name, location.store_location.__class__, uri=location_header, image_id=location.image_id, store_specs=location.store_specs) return self._query(new_loc, method, depth + 1) content_length = int(resp.getheader('content-length', 0)) return (conn, resp, content_length)
def add(self, image_id, image_file, image_size): """ Stores an image file with supplied identifier to the backend storage system and returns a tuple containing information about the stored image. :param image_id: The opaque image identifier :param image_file: The image data to write, as a file-like object :param image_size: The size of the image data to write, in bytes :retval tuple of URL in backing store, bytes written, and checksum :raises `glance.store.exceptions.Duplicate` if the image already existed """ image = SheepdogImage(self.addr, self.port, image_id, self.chunk_size) if image.exist(): raise exceptions.Duplicate(_("Sheepdog image %s already exists") % image_id) location = StoreLocation({'image': image_id}) checksum = hashlib.md5() image.create(image_size) try: total = left = image_size while left > 0: length = min(self.chunk_size, left) data = image_file.read(length) image.write(data, total - left, length) left -= length checksum.update(data) except Exception: # Note(zhiyan): clean up already received data when # error occurs such as ImageSizeLimitExceeded exceptions. with excutils.save_and_reraise_exception(): image.delete() return (location.get_uri(), image_size, checksum.hexdigest(), {})
def add(self, image_id, image_file, image_size, context=None): """ Stores an image file with supplied identifier to the backend storage system and returns a tuple containing information about the stored image. :param image_id: The opaque image identifier :param image_file: The image data to write, as a file-like object :param image_size: The size of the image data to write, in bytes :retval tuple of URL in backing store, bytes written, checksum and a dictionary with storage system specific information :raises `glance.store.exceptions.Duplicate` if the image already existed """ loc = StoreLocation({'image_id': image_id}) if self.fs.exists(image_id): raise exceptions.Duplicate(_("GridFS already has an image at " "location %s") % loc.get_uri()) LOG.debug(_("Adding a new image to GridFS with " "id %(iid)s and size %(size)s") % dict(iid=image_id, size=image_size)) try: self.fs.put(image_file, _id=image_id) image = self._get_file(loc) except Exception: # Note(zhiyan): clean up already received data when # error occurs such as ImageSizeLimitExceeded exception. with excutils.save_and_reraise_exception(): self.fs.delete(image_id) LOG.debug(_("Uploaded image %(iid)s, " "md5 %(md)s, length %(len)s to GridFS") % dict(iid=image._id, md=image.md5, len=image.length)) return (loc.get_uri(), image.length, image.md5, {})