def get_image_meta_from_headers(response): """ Processes HTTP headers from a supplied response that match the x-image-meta and x-image-meta-property and returns a mapping of image metadata and properties :param response: Response to process """ result = {} properties = {} if hasattr(response, 'getheaders'): # httplib.HTTPResponse headers = response.getheaders() else: # webob.Response headers = response.headers.items() for key, value in headers: key = str(key.lower()) if key.startswith('x-image-meta-property-'): field_name = key[len('x-image-meta-property-'):].replace('-', '_') properties[field_name] = value or None elif key.startswith('x-image-meta-'): field_name = key[len('x-image-meta-'):].replace('-', '_') result[field_name] = value or None result['properties'] = properties if 'size' in result: try: result['size'] = int(result['size']) except ValueError: raise exception.Invalid for key in ('is_public', 'deleted', 'protected'): if key in result: result[key] = strutils.bool_from_string(result[key]) return result
def get_image_meta_from_headers(response): """ Processes HTTP headers from a supplied response that match the x-image-meta and x-image-meta-property and returns a mapping of image metadata and properties :param response: Response to process """ result = {} properties = {} if hasattr(response, 'getheaders'): # httplib.HTTPResponse headers = response.getheaders() else: # webob.Response headers = response.headers.items() for key, value in headers: key = str(key.lower()) if key.startswith('x-image-meta-property-'): field_name = key[len('x-image-meta-property-'):].replace('-', '_') properties[field_name] = value or None elif key.startswith('x-image-meta-'): field_name = key[len('x-image-meta-'):].replace('-', '_') if 'x-image-meta-' + field_name not in IMAGE_META_HEADERS: msg = _("Bad header: %(header_name)s") % {'header_name': key} raise exc.HTTPBadRequest(msg, content_type="text/plain") result[field_name] = value or None result['properties'] = properties for key, nullable in [('size', False), ('min_disk', False), ('min_ram', False), ('virtual_size', True)]: if key in result: try: result[key] = int(result[key]) except ValueError: if nullable and result[key] == str(None): result[key] = None else: extra = (_("Cannot convert image %(key)s '%(value)s' " "to an integer.") % {'key': key, 'value': result[key]}) raise exception.InvalidParameterValue(value=result[key], param=key, extra_msg=extra) if result[key] < 0 and result[key] is not None: extra = (_("Image %(key)s must be >= 0 " "('%(value)s' specified).") % {'key': key, 'value': result[key]}) raise exception.InvalidParameterValue(value=result[key], param=key, extra_msg=extra) for key in ('is_public', 'deleted', 'protected'): if key in result: result[key] = strutils.bool_from_string(result[key]) return result
def get_image_meta_from_headers(response): """ Processes HTTP headers from a supplied response that match the x-image-meta and x-image-meta-property and returns a mapping of image metadata and properties :param response: Response to process """ result = {} properties = {} if hasattr(response, 'getheaders'): # httplib.HTTPResponse headers = response.getheaders() else: # webob.Response headers = response.headers.items() for key, value in headers: key = str(key.lower()) if key.startswith('x-image-meta-property-'): field_name = key[len('x-image-meta-property-'):].replace('-', '_') properties[field_name] = value or None elif key.startswith('x-image-meta-'): field_name = key[len('x-image-meta-'):].replace('-', '_') if 'x-image-meta-' + field_name not in IMAGE_META_HEADERS: msg = _("Bad header: %(header_name)s") % {'header_name': key} raise exc.HTTPBadRequest(msg, content_type="text/plain") result[field_name] = value or None result['properties'] = properties for key in ('size', 'min_disk', 'min_ram'): if key in result: try: result[key] = int(result[key]) except ValueError: extra = (_("Cannot convert image %(key)s '%(value)s' " "to an integer.") % { 'key': key, 'value': result[key] }) raise exception.InvalidParameterValue(value=result[key], param=key, extra_msg=extra) if result[key] < 0: extra = (_("Image %(key)s must be >= 0 " "('%(value)s' specified).") % { 'key': key, 'value': result[key] }) raise exception.InvalidParameterValue(value=result[key], param=key, extra_msg=extra) for key in ('is_public', 'deleted', 'protected'): if key in result: result[key] = strutils.bool_from_string(result[key]) return result
def get_image_meta_from_headers(response): """ Processes HTTP headers from a supplied response that match the x-image-meta and x-image-meta-property and returns a mapping of image metadata and properties :param response: Response to process """ result = {} properties = {} if hasattr(response, "getheaders"): # httplib.HTTPResponse headers = response.getheaders() else: # webob.Response headers = response.headers.items() for key, value in headers: key = str(key.lower()) if key.startswith("x-image-meta-property-"): field_name = key[len("x-image-meta-property-") :].replace("-", "_") properties[field_name] = value or None elif key.startswith("x-image-meta-"): field_name = key[len("x-image-meta-") :].replace("-", "_") if "x-image-meta-" + field_name not in IMAGE_META_HEADERS: msg = _("Bad header: %(header_name)s") % {"header_name": key} raise exc.HTTPBadRequest(msg, content_type="text/plain") result[field_name] = value or None result["properties"] = properties if "size" in result: try: result["size"] = int(result["size"]) except ValueError: raise exception.Invalid for key in ("is_public", "deleted", "protected"): if key in result: result[key] = strutils.bool_from_string(result[key]) return result
def get_image_meta_from_headers(response): """ Processes HTTP headers from a supplied response that match the x-image-meta and x-image-meta-property and returns a mapping of image metadata and properties :param response: Response to process """ result = {} properties = {} if hasattr(response, 'getheaders'): # httplib.HTTPResponse headers = response.getheaders() else: # webob.Response headers = response.headers.items() for key, value in headers: key = str(key.lower()) if key.startswith('x-image-meta-property-'): field_name = key[len('x-image-meta-property-'):].replace('-', '_') properties[field_name] = value or None elif key.startswith('x-image-meta-'): field_name = key[len('x-image-meta-'):].replace('-', '_') if 'x-image-meta-' + field_name not in IMAGE_META_HEADERS: msg = _("Bad header: %(header_name)s") % {'header_name': key} raise exc.HTTPBadRequest(msg, content_type="text/plain") result[field_name] = value or None result['properties'] = properties if 'size' in result: try: result['size'] = int(result['size']) except ValueError: raise exception.Invalid for key in ('is_public', 'deleted', 'protected'): if key in result: result[key] = strutils.bool_from_string(result[key]) return result
def update(self, req, id, image_meta, image_data): """ Updates an existing image with the registry. :param request: The WSGI/Webob Request object :param id: The opaque image identifier :retval Returns the updated image information as a mapping """ self._enforce(req, 'modify_image') is_public = image_meta.get('is_public') if is_public: self._enforce(req, 'publicize_image') orig_image_meta = self.get_image_meta_or_404(req, id) orig_status = orig_image_meta['status'] # Do not allow any updates on a deleted image. # Fix for LP Bug #1060930 if orig_status == 'deleted': msg = _("Forbidden to update deleted image.") raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") if req.context.is_admin is False: # Once an image is 'active' only an admin can # modify certain core metadata keys for key in ACTIVE_IMMUTABLE: if (orig_status == 'active' and image_meta.get(key) is not None and image_meta.get(key) != orig_image_meta.get(key)): msg = _("Forbidden to modify '%s' of active image.") % key raise HTTPForbidden(explanation=msg, request=req, content_type="text/plain") # The default behaviour for a PUT /images/<IMAGE_ID> is to # override any properties that were previously set. This, however, # leads to a number of issues for the common use case where a caller # registers an image with some properties and then almost immediately # uploads an image file along with some more properties. Here, we # check for a special header value to be false in order to force # properties NOT to be purged. However we also disable purging of # properties if an image file is being uploaded... purge_props = req.headers.get('x-glance-registry-purge-props', True) purge_props = (strutils.bool_from_string(purge_props) and image_data is None) if image_data is not None and orig_status != 'queued': raise HTTPConflict(_("Cannot upload to an unqueued image")) # Only allow the Location|Copy-From fields to be modified if the # image is in queued status, which indicates that the user called # POST /images but originally supply neither a Location|Copy-From # field NOR image data location = self._external_source(image_meta, req) reactivating = orig_status != 'queued' and location activating = orig_status == 'queued' and (location or image_data) # Make image public in the backend store (if implemented) orig_or_updated_loc = location or orig_image_meta.get('location', None) if orig_or_updated_loc: self.update_store_acls(req, id, orig_or_updated_loc, public=is_public) if reactivating: msg = _("Attempted to update Location field for an image " "not in queued status.") raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain") # ensure requester has permissions to create/update/delete properties # according to property-protections.conf orig_keys = set(orig_image_meta['properties']) new_keys = set(image_meta['properties']) self._enforce_update_protected_props( orig_keys.intersection(new_keys), image_meta, orig_image_meta, req) self._enforce_create_protected_props( new_keys.difference(orig_keys), req) if purge_props: self._enforce_delete_protected_props( orig_keys.difference(new_keys), image_meta, orig_image_meta, req) self._enforce_image_property_quota(image_meta, orig_image_meta=orig_image_meta, purge_props=purge_props, req=req) try: if location: image_meta['size'] = self._get_size(req.context, image_meta, location) image_meta = registry.update_image_metadata(req.context, id, image_meta, purge_props) if activating: image_meta = self._handle_source(req, id, image_meta, image_data) except exception.Invalid as e: msg = (_("Failed to update image metadata. Got error: %(e)s") % {'e': e}) LOG.debug(msg) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain") except exception.NotFound as e: msg = _("Failed to find image to update: %(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 update 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.update', redact_loc(image_meta)) # Prevent client from learning the location, as it # could contain security credentials image_meta = redact_loc(image_meta) self._enforce_read_protected_props(image_meta, req) return {'image_meta': image_meta}
def _parse_deleted_filter(self, req): """Parse deleted into something usable.""" deleted = req.params.get('deleted') if deleted is None: return None return strutils.bool_from_string(deleted)