def test_upload_data_to_store_not_found_after_upload(self): req = unit_test_utils.get_fake_request() location = "file://foo/bar" size = 10 checksum = "checksum" image_meta = {'id': unit_test_utils.UUID1, 'size': size} image_data = "blah" notifier = self.mox.CreateMockAnything() store = self.mox.CreateMockAnything() store.add(image_meta['id'], mox.IgnoreArg(), image_meta['size']).AndReturn((location, size, checksum, {})) self.mox.StubOutWithMock(registry, "update_image_metadata") update_data = {'checksum': checksum, 'size': size} registry.update_image_metadata(req.context, image_meta['id'], update_data).AndRaise( exception.NotFound) self.mox.StubOutWithMock(upload_utils, "initiate_deletion") upload_utils.initiate_deletion(req, location, image_meta['id'], mox.IsA(bool)) self.mox.StubOutWithMock(upload_utils, "safe_kill") upload_utils.safe_kill(req, image_meta['id']) notifier.error('image.upload', mox.IgnoreArg()) self.mox.ReplayAll() self.assertRaises(webob.exc.HTTPPreconditionFailed, upload_utils.upload_data_to_store, req, image_meta, image_data, store, notifier) self.mox.VerifyAll()
def delete(self, req, id): """ Deletes the image and all its chunks from the Glance :param req: The WSGI/Webob Request object :param id: The opaque image identifier :raises HttpBadRequest if image registry is invalid :raises HttpNotFound if image or any chunk is not available :raises HttpUnauthorized if image or any chunk is not deleteable by the requesting user """ self._enforce(req, "delete_image") image = self.get_image_meta_or_404(req, id) if image["protected"]: msg = _("Image is protected") LOG.debug(msg) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") if image["status"] == "pending_delete": msg = _("Forbidden to delete a %s image.") % image["status"] LOG.debug(msg) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") elif image["status"] == "deleted": msg = _("Image %s not found.") % id LOG.debug(msg) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") if image["location"] and CONF.delayed_delete: status = "pending_delete" else: status = "deleted" try: # Delete the image from the registry first, since we rely on it # for authorization checks. # See https://bugs.launchpad.net/glance/+bug/1065187 registry.update_image_metadata(req.context, id, {"status": status}) registry.delete_image_metadata(req.context, id) # The image's location field may be None in the case # of a saving or queued image, therefore don't ask a backend # to delete the image if the backend doesn't yet store it. # See https://bugs.launchpad.net/glance/+bug/747799 if image["location"]: upload_utils.initiate_deletion(req, image["location"], id, CONF.delayed_delete) except exception.NotFound as e: msg = _("Failed to find image to delete: %(e)s") % locals() for line in msg.split("\n"): LOG.info(line) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") except exception.Forbidden as e: msg = _("Forbidden to delete image: %(e)s") % locals() for line in msg.split("\n"): LOG.info(line) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") else: self.notifier.info("image.delete", redact_loc(image)) return Response(body="", status=200)
def _activate(self, req, image_id, location, location_metadata=None, from_state=None): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location: Location of where Glance stored this image :param location_metadata: a dictionary of storage specific information """ image_meta = {} image_meta["location"] = location image_meta["status"] = "active" if location_metadata: image_meta["location_data"] = [{"url": location, "metadata": location_metadata}] try: s = from_state image_meta_data = registry.update_image_metadata(req.context, image_id, image_meta, from_state=s) self.notifier.info("image.activate", redact_loc(image_meta_data)) self.notifier.info("image.update", redact_loc(image_meta_data)) return image_meta_data except exception.Duplicate: with excutils.save_and_reraise_exception(): # Delete image data since it has been supersceded by another # upload and re-raise. LOG.debug( "duplicate operation - deleting image data for " " %(id)s (location:%(location)s)" % {"id": image_id, "location": image_meta["location"]} ) upload_utils.initiate_deletion(req, image_meta["location"], image_id, CONF.delayed_delete) except exception.Invalid as e: msg = "Failed to activate image. Got error: %s" % utils.exception_to_str(e) LOG.debug(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain")
def test_initiate_delete(self): req = unit_test_utils.get_fake_request() location = "file://foo/bar" id = unit_test_utils.UUID1 self.mox.StubOutWithMock(glance.store, "safe_delete_from_backend") glance.store.safe_delete_from_backend(location, req.context, id) self.mox.ReplayAll() upload_utils.initiate_deletion(req, location, id) self.mox.VerifyAll()
def _activate(self, req, image_id, location, location_metadata=None, from_state=None): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location: Location of where Glance stored this image :param location_metadata: a dictionary of storage specific information """ image_meta = {} image_meta['location'] = location image_meta['status'] = 'active' if location_metadata: image_meta['location_data'] = [{ 'url': location, 'metadata': location_metadata }] try: s = from_state image_meta_data = registry.update_image_metadata(req.context, image_id, image_meta, from_state=s) self.notifier.info("image.activate", redact_loc(image_meta_data)) self.notifier.info("image.update", redact_loc(image_meta_data)) return image_meta_data except exception.Duplicate: # Delete image data since it has been supersceded by another # upload. LOG.debug( _("duplicate operation - deleting image data for %(id)s " "(location:%(location)s)") % { 'id': image_id, 'location': image_meta['location'] }) upload_utils.initiate_deletion(req, image_meta['location'], image_id, CONF.delayed_delete) # Then propagate the exception. raise except exception.Invalid as e: msg = _("Failed to activate image. Got error: %(e)s") % {'e': e} LOG.debug(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain")
def test_initiate_delete(self): req = unit_test_utils.get_fake_request() location = { "url": "file://foo/bar", "metadata": {}, "status": "active" } id = unit_test_utils.UUID1 with patch.object(store_utils, "safe_delete_from_backend") as mock_store_utils: upload_utils.initiate_deletion(req, location, id) mock_store_utils.assert_called_once_with(req.context, id, location)
def test_initiate_delete(self): req = unit_test_utils.get_fake_request() location = {"url": "file://foo/bar", "metadata": {}, "status": "active"} id = unit_test_utils.UUID1 with patch.object(store_utils, "safe_delete_from_backend") as mock_store_utils: upload_utils.initiate_deletion(req, location, id) mock_store_utils.assert_called_once_with(req.context, id, location)
def test_initiate_delete(self): req = unit_test_utils.get_fake_request() location = {"url": "file://foo/bar", "metadata": {}, "status": "active"} id = unit_test_utils.UUID1 self.mox.StubOutWithMock(store_utils, "safe_delete_from_backend") store_utils.safe_delete_from_backend(req.context, id, location) self.mox.ReplayAll() upload_utils.initiate_deletion(req, location, id) self.mox.VerifyAll()
def test_initiate_delete(self): req = unit_test_utils.get_fake_request() location = { "url": "file://foo/bar", "metadata": {}, "status": "active" } id = unit_test_utils.UUID1 self.mox.StubOutWithMock(store_utils, "safe_delete_from_backend") store_utils.safe_delete_from_backend(req.context, id, location) self.mox.ReplayAll() upload_utils.initiate_deletion(req, location, id) self.mox.VerifyAll()
def test_initiate_delete_with_delayed_delete(self): self.config(delayed_delete=True) req = unit_test_utils.get_fake_request() location = {"url": "file://foo/bar", "metadata": {}, "status": "active"} id = unit_test_utils.UUID1 self.mox.StubOutWithMock(store_utils, "schedule_delayed_delete_from_backend") ret = store_utils.schedule_delayed_delete_from_backend(req.context, id, location) ret.AndReturn(True) self.mox.ReplayAll() upload_utils.initiate_deletion(req, location, id) self.mox.VerifyAll()
def _activate(self, req, image_id, location, location_metadata=None, from_state=None): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location: Location of where Glance stored this image :param location_metadata: a dictionary of storage specific information """ image_meta = {} image_meta['location'] = location image_meta['status'] = 'active' if location_metadata: image_meta['location_data'] = [{'url': location, 'metadata': location_metadata}] try: s = from_state image_meta_data = registry.update_image_metadata(req.context, image_id, image_meta, from_state=s) self.notifier.info("image.activate", redact_loc(image_meta_data)) self.notifier.info("image.update", redact_loc(image_meta_data)) return image_meta_data except exception.Duplicate: # Delete image data since it has been supersceded by another # upload. LOG.debug("duplicate operation - deleting image data for %s " "(location:%s)" % (image_id, image_meta['location'])) upload_utils.initiate_deletion(req, image_meta['location'], image_id, CONF.delayed_delete) # Then propagate the exception. raise except exception.Invalid as e: msg = _("Failed to activate image. Got error: %(e)s") % {'e': e} LOG.debug(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain")
def test_initiate_delete_with_delayed_delete(self): self.config(delayed_delete=True) req = unit_test_utils.get_fake_request() location = { "url": "file://foo/bar", "metadata": {}, "status": "active" } id = unit_test_utils.UUID1 self.mox.StubOutWithMock(store_utils, "schedule_delayed_delete_from_backend") ret = store_utils.schedule_delayed_delete_from_backend( req.context, id, location) ret.AndReturn(True) self.mox.ReplayAll() upload_utils.initiate_deletion(req, location, id) self.mox.VerifyAll()
def test_upload_data_to_store_not_found_after_upload(self): req = unit_test_utils.get_fake_request() location = "file://foo/bar" size = 10 checksum = "checksum" image_meta = {'id': unit_test_utils.UUID1, 'size': size} image_data = "blah" notifier = self.mox.CreateMockAnything() store = self.mox.CreateMockAnything() store.add( image_meta['id'], mox.IgnoreArg(), image_meta['size']).AndReturn((location, size, checksum, {})) self.mox.StubOutWithMock(registry, "update_image_metadata") update_data = {'checksum': checksum, 'size': size} registry.update_image_metadata(req.context, image_meta['id'], update_data ).AndRaise(exception.NotFound) self.mox.StubOutWithMock(upload_utils, "initiate_deletion") upload_utils.initiate_deletion(req, {'url': location, 'status': 'active', 'metadata': {}}, image_meta['id']) self.mox.StubOutWithMock(upload_utils, "safe_kill") upload_utils.safe_kill(req, image_meta['id']) notifier.error('image.upload', mox.IgnoreArg()) self.mox.ReplayAll() self.assertRaises(webob.exc.HTTPPreconditionFailed, upload_utils.upload_data_to_store, req, image_meta, image_data, store, notifier) self.mox.VerifyAll()
def _activate(self, req, image_id, location_data, from_state=None): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location_data: Location of where Glance stored this image """ image_meta = {} image_meta['location'] = location_data['url'] image_meta['status'] = 'active' image_meta['location_data'] = [location_data] try: s = from_state image_meta_data = registry.update_image_metadata(req.context, image_id, image_meta, from_state=s) self.notifier.info("image.activate", redact_loc(image_meta_data)) self.notifier.info("image.update", redact_loc(image_meta_data)) return image_meta_data except exception.Duplicate: with excutils.save_and_reraise_exception(): # Delete image data since it has been supersceded by another # upload and re-raise. LOG.debug("duplicate operation - deleting image data for " " %(id)s (location:%(location)s)" % {'id': image_id, 'location': image_meta['location']}) upload_utils.initiate_deletion(req, image_meta['location'], image_id, CONF.delayed_delete) except exception.Invalid as e: msg = ("Failed to activate image. Got error: %s" % utils.exception_to_str(e)) LOG.debug(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain")
def delete(self, req, id): """ Deletes the image and all its chunks from the Glance :param req: The WSGI/Webob Request object :param id: The opaque image identifier :raises HttpBadRequest if image registry is invalid :raises HttpNotFound if image or any chunk is not available :raises HttpUnauthorized if image or any chunk is not deleteable by the requesting user """ self._enforce(req, 'delete_image') image = self.get_image_meta_or_404(req, id) if image['protected']: msg = _("Image is protected") LOG.debug(msg) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") if image['status'] == 'pending_delete': msg = (_("Forbidden to delete a %s image.") % image['status']) LOG.debug(msg) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") elif image['status'] == 'deleted': msg = _("Image %s not found.") % id LOG.debug(msg) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") if image['location'] and CONF.delayed_delete: status = 'pending_delete' else: status = 'deleted' try: # Delete the image from the registry first, since we rely on it # for authorization checks. # See https://bugs.launchpad.net/glance/+bug/1065187 image = registry.update_image_metadata(req.context, id, {'status': status}) registry.delete_image_metadata(req.context, id) # The image's location field may be None in the case # of a saving or queued image, therefore don't ask a backend # to delete the image if the backend doesn't yet store it. # See https://bugs.launchpad.net/glance/+bug/747799 if image['location']: upload_utils.initiate_deletion(req, image['location'], id, CONF.delayed_delete) except exception.NotFound as e: msg = _("Failed to find image to delete: %(e)s") % {'e': e} for line in msg.split('\n'): LOG.info(line) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") except exception.Forbidden as e: msg = _("Forbidden to delete image: %(e)s") % {'e': e} for line in msg.split('\n'): LOG.info(line) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") else: self.notifier.info('image.delete', redact_loc(image)) return Response(body='', status=200)
def delete(self, req, id): """ Deletes the image and all its chunks from the Glance :param req: The WSGI/Webob Request object :param id: The opaque image identifier :raises HttpBadRequest if image registry is invalid :raises HttpNotFound if image or any chunk is not available :raises HttpUnauthorized if image or any chunk is not deleteable by the requesting user """ self._enforce(req, 'delete_image') image = self.get_image_meta_or_404(req, id) if image['protected']: msg = "Image is protected" LOG.debug(msg) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") if image['status'] == 'pending_delete': msg = "Forbidden to delete a %s image." % image['status'] LOG.debug(msg) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") elif image['status'] == 'deleted': msg = "Image %s not found." % id LOG.debug(msg) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") if image['location'] and CONF.delayed_delete: status = 'pending_delete' else: status = 'deleted' ori_status = image['status'] try: # Update the image from the registry first, since we rely on it # for authorization checks. # See https://bugs.launchpad.net/glance/+bug/1065187 image = registry.update_image_metadata(req.context, id, {'status': status}) try: # The image's location field may be None in the case # of a saving or queued image, therefore don't ask a backend # to delete the image if the backend doesn't yet store it. # See https://bugs.launchpad.net/glance/+bug/747799 if image['location']: for loc_data in image['location_data']: if loc_data['status'] == 'active': upload_utils.initiate_deletion(req, loc_data, id) except Exception: with excutils.save_and_reraise_exception(): registry.update_image_metadata(req.context, id, {'status': ori_status}) registry.delete_image_metadata(req.context, id) except exception.NotFound as e: msg = (_("Failed to find image to delete: %s") % utils.exception_to_str(e)) for line in msg.split('\n'): LOG.info(line) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") except exception.Forbidden as e: msg = (_("Forbidden to delete image: %s") % utils.exception_to_str(e)) for line in msg.split('\n'): LOG.info(line) raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") else: self.notifier.info('image.delete', redact_loc(image)) return Response(body='', status=200)