Beispiel #1
0
    def cache_tee_iter(self, image_id, image_iter, image_checksum):
        try:
            current_checksum = hashlib.md5()

            with self.driver.open_for_write(image_id) as cache_file:
                for chunk in image_iter:
                    try:
                        cache_file.write(chunk)
                    finally:
                        current_checksum.update(chunk)
                        yield chunk
                cache_file.flush()

                if (image_checksum and
                        image_checksum != current_checksum.hexdigest()):
                    msg = _("Checksum verification failed. Aborted "
                            "caching of image '%s'.") % image_id
                    raise exception.GlanceException(msg)

        except exception.GlanceException as e:
            with excutils.save_and_reraise_exception():
                # image_iter has given us bad, (size_checked_iter has found a
                # bad length), or corrupt data (checksum is wrong).
                LOG.exception(utils.exception_to_str(e))
        except Exception as e:
            LOG.exception(_LE("Exception encountered while tee'ing "
                              "image '%(image_id)s' into cache: %(error)s. "
                              "Continuing with response.") %
                          {'image_id': image_id,
                           'error': utils.exception_to_str(e)})

            # If no checksum provided continue responding even if
            # caching failed.
            for chunk in image_iter:
                yield chunk
Beispiel #2
0
    def create(self, req, image, extra_properties, tags):
        image_factory = self.gateway.get_image_factory(req.context)
        image_repo = self.gateway.get_repo(req.context)
        try:
            image = image_factory.new_image(extra_properties=extra_properties,
                                            tags=tags, **image)
            image_repo.add(image)
        except exception.DuplicateLocation as dup:
            raise webob.exc.HTTPBadRequest(explanation=dup.msg)
        except exception.Invalid as e:
            raise webob.exc.HTTPBadRequest(explanation=e.msg)
        except exception.Forbidden as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.InvalidParameterValue as e:
            raise webob.exc.HTTPBadRequest(explanation=e.msg)
        except exception.LimitExceeded as e:
            LOG.warn(utils.exception_to_str(e))
            raise webob.exc.HTTPRequestEntityTooLarge(
                explanation=e.msg, request=req, content_type='text/plain')
        except exception.Duplicate as dupex:
            raise webob.exc.HTTPConflict(explanation=dupex.msg)
        except exception.ReservedProperty as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.ReadonlyProperty as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except TypeError as e:
            LOG.debug(utils.exception_to_str(e))
            raise webob.exc.HTTPBadRequest(
                explanation=utils.exception_to_str(e))

        return image
Beispiel #3
0
    def index(self, req, marker=None, limit=None, sort_key='created_at',
              sort_dir='desc', filters=None):
        result = {}
        if filters is None:
            filters = {}
        filters['deleted'] = False

        if limit is None:
            limit = CONF.limit_param_default
        limit = min(CONF.api_limit_max, limit)

        task_repo = self.gateway.get_task_stub_repo(req.context)
        try:
            tasks = task_repo.list(marker, limit, sort_key,
                                   sort_dir, filters)
            if len(tasks) != 0 and len(tasks) == limit:
                result['next_marker'] = tasks[-1].task_id
        except (exception.NotFound, exception.InvalidSortKey,
                exception.InvalidFilterRangeValue) as e:
            LOG.warn(utils.exception_to_str(e))
            raise webob.exc.HTTPBadRequest(explanation=e.msg)
        except exception.Forbidden as e:
            LOG.warn(utils.exception_to_str(e))
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        result['tasks'] = tasks
        return result
Beispiel #4
0
    def update(self, req, image_id, changes):
        image_repo = self.gateway.get_repo(req.context)
        try:
            image = image_repo.get(image_id)

            for change in changes:
                change_method_name = '_do_%s' % change['op']
                assert hasattr(self, change_method_name)
                change_method = getattr(self, change_method_name)
                change_method(req, image, change)

            if changes:
                image_repo.save(image)
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except exception.Invalid as e:
            raise webob.exc.HTTPBadRequest(explanation=e.msg)
        except exception.Forbidden as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.InvalidParameterValue as e:
            raise webob.exc.HTTPBadRequest(explanation=e.msg)
        except exception.StorageQuotaFull as e:
            msg = (_("Denying attempt to upload image because it exceeds the"
                     " quota: %s") % utils.exception_to_str(e))
            LOG.warn(msg)
            raise webob.exc.HTTPRequestEntityTooLarge(
                explanation=msg, request=req, content_type='text/plain')
        except exception.LimitExceeded as e:
            LOG.exception(utils.exception_to_str(e))
            raise webob.exc.HTTPRequestEntityTooLarge(
                explanation=e.msg, request=req, content_type='text/plain')

        return image
Beispiel #5
0
    def set_data(self, data, size=None):
        self.send_notification('image.prepare', self.repo)

        notify_error = self.notifier.error
        try:
            self.repo.set_data(data, size)
        except glance_store.StorageFull as e:
            msg = (_("Image storage media is full: %s") %
                   utils.exception_to_str(e))
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
        except glance_store.StorageWriteDenied as e:
            msg = (_("Insufficient permissions on image storage media: %s")
                   % utils.exception_to_str(e))
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPServiceUnavailable(explanation=msg)
        except ValueError as e:
            msg = (_("Cannot save data for image %(image_id)s: %(error)s") %
                   {'image_id': self.repo.image_id,
                    'error': utils.exception_to_str(e)})
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPBadRequest(
                explanation=utils.exception_to_str(e))
        except exception.Duplicate as e:
            msg = (_("Unable to upload duplicate image data for image"
                     "%(image_id)s: %(error)s") %
                   {'image_id': self.repo.image_id,
                    'error': utils.exception_to_str(e)})
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPConflict(explanation=msg)
        except exception.Forbidden as e:
            msg = (_("Not allowed to upload image data for image %(image_id)s:"
                     " %(error)s") % {'image_id': self.repo.image_id,
                                      'error': utils.exception_to_str(e)})
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPForbidden(explanation=msg)
        except exception.NotFound as e:
            msg = (_("Image %(image_id)s could not be found after upload."
                     " The image may have been deleted during the upload:"
                     " %(error)s") % {'image_id': self.repo.image_id,
                                      'error': utils.exception_to_str(e)})
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPNotFound(explanation=utils.exception_to_str(e))
        except webob.exc.HTTPError as e:
            with excutils.save_and_reraise_exception():
                msg = (_("Failed to upload image data for image %(image_id)s"
                         " due to HTTP error: %(error)s") %
                       {'image_id': self.repo.image_id,
                        'error': utils.exception_to_str(e)})
                _send_notification(notify_error, 'image.upload', msg)
        except Exception as e:
            with excutils.save_and_reraise_exception():
                msg = (_("Failed to upload image data for image %(image_id)s "
                         "due to internal error: %(error)s") %
                       {'image_id': self.repo.image_id,
                        'error': utils.exception_to_str(e)})
                _send_notification(notify_error, 'image.upload', msg)
        else:
            self.send_notification('image.upload', self.repo)
            self.send_notification('image.activate', self.repo)
Beispiel #6
0
    def update_all(self, req, image_id, body):
        """
        Replaces the members of the image with those specified in the
        body.  The body is a dict with the following format::

            {"memberships": [
                {"member_id": <MEMBER_ID>,
                 ["can_share": [True|False]]}, ...
            ]}
        """
        self._check_can_access_image_members(req.context)
        self._enforce(req, 'modify_member')
        self._raise_404_if_image_deleted(req, image_id)

        memberships = body.get('memberships')
        if memberships:
            new_number_of_members = len(body['memberships'])
            self._enforce_image_member_quota(req, new_number_of_members)

        try:
            registry.replace_members(req.context, image_id, body)
            self._update_store_acls(req, image_id)
        except exception.Invalid as e:
            LOG.debug(utils.exception_to_str(e))
            raise webob.exc.HTTPBadRequest(explanation=e.msg)
        except exception.NotFound as e:
            LOG.debug(utils.exception_to_str(e))
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except exception.Forbidden as e:
            LOG.debug(utils.exception_to_str(e))
            raise webob.exc.HTTPNotFound(explanation=e.msg)

        return webob.exc.HTTPNoContent()
Beispiel #7
0
    def _get_metadata(self):
        if CONF.filesystem_store_metadata_file is None:
            return {}

        try:
            with open(CONF.filesystem_store_metadata_file, 'r') as fptr:
                metadata = jsonutils.load(fptr)
            glance.store.check_location_metadata(metadata)
            return metadata
        except glance.store.BackendException as bee:
            LOG.error(_('The JSON in the metadata file %(file)s could not be '
                        'used: %(error)s  An empty dictionary will be '
                        'returned to the client.') %
                      {'file': CONF.filesystem_store_metadata_file,
                       'error': utils.exception_to_str(bee)})
            return {}
        except IOError as ioe:
            LOG.error(_('The path for the metadata file %(file)s could not be '
                        'opened: %(error)s  An empty dictionary will be '
                        'returned to the client.') %
                      {'file': CONF.filesystem_store_metadata_file,
                       'error': utils.exception_to_str(ioe)})
            return {}
        except Exception as ex:
            LOG.exception(_('An error occurred processing the storage systems '
                            'meta data file: %s.  An empty dictionary will be '
                            'returned to the client.') %
                          utils.exception_to_str(ex))
            return {}
Beispiel #8
0
    def configure(self):
        """
        Configure the Store to use the stored configuration options
        Any store that needs special configuration should implement
        this method. If the store was not able to successfully configure
        itself, it should raise `exception.BadStoreConfiguration`
        """

        try:
            self.chunk_size = CONF.sheepdog_store_chunk_size * units.Mi
            self.addr = CONF.sheepdog_store_address.strip()
            self.port = CONF.sheepdog_store_port
        except cfg.ConfigFileValueError as e:
            reason = (_("Error in store configuration: %s") %
                      utils.exception_to_str(e))
            LOG.error(reason)
            raise exception.BadStoreConfiguration(store_name='sheepdog',
                                                  reason=reason)

        if ' ' in self.addr:
            reason = (_("Invalid address configuration of sheepdog store: %s")
                      % self.addr)
            LOG.error(reason)
            raise exception.BadStoreConfiguration(store_name='sheepdog',
                                                  reason=reason)

        try:
            cmd = ["collie", "vdi", "list", "-a", self.addr, "-p", self.port]
            processutils.execute(*cmd)
        except Exception as e:
            reason = (_("Error in store configuration: %s") %
                      utils.exception_to_str(e))
            LOG.error(reason)
            raise exception.BadStoreConfiguration(store_name='sheepdog',
                                                  reason=reason)
Beispiel #9
0
def main():
    """The main function."""

    try:
        config.parse_args()
    except RuntimeError as e:
        sys.exit("ERROR: %s" % utils.exception_to_str(e))

    # Setup logging
    logging.setup('glance')

    if CONF.token:
        CONF.slavetoken = CONF.token
        CONF.mastertoken = CONF.token

    command = lookup_command(CONF.command)

    try:
        command(CONF, CONF.args)
    except TypeError as e:
        LOG.error(_LE(command.__doc__) % {'prog': command.__name__})  # noqa
        sys.exit("ERROR: %s" % utils.exception_to_str(e))
    except ValueError as e:
        LOG.error(_LE(command.__doc__) % {'prog': command.__name__})  # noqa
        sys.exit("ERROR: %s" % utils.exception_to_str(e))
Beispiel #10
0
        def rollback(e):
            set_attr('error', utils.exception_to_str(e))

            invalid_path = self.get_image_filepath(image_id, 'invalid')
            LOG.debug("Fetch of cache file failed (%(e)s), rolling back by "
                      "moving '%(incomplete_path)s' to "
                      "'%(invalid_path)s'" %
                      {'e': utils.exception_to_str(e),
                       'incomplete_path': incomplete_path,
                       'invalid_path': invalid_path})
            os.rename(incomplete_path, invalid_path)
    def test_exception_to_str(self):
        class FakeException(Exception):
            def __str__(self):
                raise UnicodeError()

        ret = utils.exception_to_str(Exception('error message'))
        self.assertEqual('error message', ret)

        ret = utils.exception_to_str(Exception('\xa5 error message'))
        self.assertEqual(' error message', ret)

        ret = utils.exception_to_str(FakeException('\xa5 error message'))
        self.assertEqual("Caught '%(exception)s' exception." %
                         {'exception': 'FakeException'}, ret)
Beispiel #12
0
 def get(self, req, task_id):
     try:
         task_repo = self.gateway.get_task_repo(req.context)
         task = task_repo.get(task_id)
     except exception.NotFound as e:
         msg = (_("Failed to find task %(task_id)s. Reason: %(reason)s") %
                {'task_id': task_id, 'reason': utils.exception_to_str(e)})
         LOG.info(msg)
         raise webob.exc.HTTPNotFound(explanation=e.msg)
     except exception.Forbidden as e:
         msg = (_("Forbidden to get task %(task_id)s. Reason: %(reason)s") %
                {'task_id': task_id, 'reason': utils.exception_to_str(e)})
         LOG.info(msg)
         raise webob.exc.HTTPForbidden(explanation=e.msg)
     return task
Beispiel #13
0
def _execute(t_id, task_repo, image_repo, image_factory):
    task = script_utils.get_task(task_repo, t_id)

    if task is None:
        # NOTE: This happens if task is not found in the database. In
        # such cases, there is no way to update the task status so,
        # it's ignored here.
        return

    try:
        task_input = script_utils.unpack_task_input(task)

        uri = script_utils.validate_location_uri(task_input.get('import_from'))
        image_id = import_image(image_repo, image_factory, task_input, t_id,
                                uri)

        task.succeed({'image_id': image_id})
    except Exception as e:
        # Note: The message string contains Error in it to indicate
        # in the task.message that it's a error message for the user.

        # TODO(nikhil): need to bring back save_and_reraise_exception when
        # necessary
        err_msg = ("Error: " + six.text_type(type(e)) + ': ' +
                   common_utils.exception_to_str(e))
        log_msg = _LE(err_msg + ("Task ID %s" % task.task_id))  # noqa
        LOG.exception(log_msg)

        task.fail(_LE(err_msg))  # noqa
    finally:
        task_repo.save(task)
def migrate_location_credentials(migrate_engine, to_quoted):
    """
    Migrate location credentials for swift uri's between the quoted
    and unquoted forms.

    :param migrate_engine: The configured db engine
    :param to_quoted: If True, migrate location credentials from
                      unquoted to quoted form.  If False, do the
                      reverse.
    """
    meta = sqlalchemy.schema.MetaData()
    meta.bind = migrate_engine

    images_table = sqlalchemy.Table('images', meta, autoload=True)

    images = list(images_table.select(images_table.c.location.startswith(
                                      'swift')).execute())

    for image in images:
        try:
            fixed_uri = legacy_parse_uri(image['location'], to_quoted)
            images_table.update().where(
                images_table.c.id == image['id']).values(
                    location=fixed_uri).execute()
        except exception.BadStoreUri as e:
            reason = utils.exception_to_str(e)
            msg = _LE("Invalid store uri for image: %(image_id)s. "
                      "Details: %(reason)s") % {'image_id': image.id,
                                                'reason': reason}
            LOG.exception(msg)
            raise
Beispiel #15
0
def main():
    CONF.register_cli_opt(command_opt)
    try:
        cfg_files = cfg.find_config_files(
            project='glance', prog='glance-registry')
        cfg_files.extend(
            cfg.find_config_files(project='glance', prog='glance-api'))
        config.parse_args(
            default_config_files=cfg_files, usage="%(prog)s [options] <cmd>")
        log.setup('glance')
    except RuntimeError as e:
        sys.exit("ERROR: %s" % e)

    try:
        if CONF.command.action.startswith('db'):
            return CONF.command.action_fn()
        else:
            func_kwargs = {}
            for k in CONF.command.action_kwargs:
                v = getattr(CONF.command, 'action_kwarg_' + k)
                if v is None:
                    continue
                func_kwargs[k] = strutils.safe_decode(v)

            func_args = [
                strutils.safe_decode(arg) for arg in CONF.command.action_args
            ]
            return CONF.command.action_fn(*func_args, **func_kwargs)
    except exception.GlanceException as e:
        sys.exit("ERROR: %s" % utils.exception_to_str(e))
Beispiel #16
0
    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")
Beispiel #17
0
    def show(self, req, namespace, property_name, filters=None):
        try:
            if filters and filters['resource_type']:
                rs_repo = self.gateway.get_metadef_resource_type_repo(
                    req.context)
                db_resource_type = rs_repo.get(filters['resource_type'],
                                               namespace)
                prefix = db_resource_type.prefix
                if prefix and property_name.startswith(prefix):
                    property_name = property_name[len(prefix):]
                else:
                    msg = (_("Property %(property_name)s does not start "
                             "with the expected resource type association "
                             "prefix of '%(prefix)s'.")
                           % {'property_name': property_name,
                              'prefix': prefix})
                    raise exception.NotFound(msg)

            prop_repo = self.gateway.get_metadef_property_repo(req.context)
            db_property = prop_repo.get(namespace, property_name)
            property = self._to_model(db_property)
        except exception.Forbidden as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            raise webob.exc.HTTPInternalServerError()
        return property
Beispiel #18
0
    def update(self, req, image_id, member_id, status):
        """
        Adds a membership to the image.
        :param req: the Request object coming from the wsgi layer
        :param image_id: the image identifier
        :param member_id: the member identifier
        :retval The response body is a mapping of the following form::

            {'member_id': <MEMBER>,
             'image_id': <IMAGE>,
             'status': <MEMBER_STATUS>
             'created_at': ..,
             'updated_at': ..}

        """
        image_repo = self.gateway.get_repo(req.context)
        try:
            image = image_repo.get(image_id)
            member_repo = image.get_member_repo()
            member = member_repo.get(member_id)
            member.status = status
            member_repo.save(member)
            return member
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except exception.Forbidden as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except ValueError as e:
            raise webob.exc.HTTPBadRequest(explanation=
                                           utils.exception_to_str(e))
Beispiel #19
0
    def update(self, req, user_ns, namespace):
        namespace_repo = self.gateway.get_metadef_namespace_repo(req.context)
        try:
            ns_obj = namespace_repo.get(namespace)
            ns_obj.namespace = wsme_utils._get_value(user_ns.namespace)
            ns_obj.display_name = wsme_utils._get_value(user_ns.display_name)
            ns_obj.description = wsme_utils._get_value(user_ns.description)
            # Following optional fields will default to same values as in
            # create namespace if not specified
            ns_obj.visibility = (
                wsme_utils._get_value(user_ns.visibility) or 'private')
            ns_obj.protected = (
                wsme_utils._get_value(user_ns.protected) or False)
            ns_obj.owner = (
                wsme_utils._get_value(user_ns.owner) or req.context.owner)
            updated_namespace = namespace_repo.save(ns_obj)
        except exception.Forbidden as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except exception.Duplicate as e:
            raise webob.exc.HTTPConflict(explanation=e.msg)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            raise webob.exc.HTTPInternalServerError()

        return Namespace.to_wsme_model(updated_namespace,
                                       get_namespace_href(updated_namespace),
                                       self.ns_schema_link)
Beispiel #20
0
    def index(self, req, namespace, marker=None, limit=None,
              sort_key='created_at', sort_dir='desc', filters=None):
        try:
            filters = filters or dict()
            filters['namespace'] = namespace

            tag_repo = self.gateway.get_metadef_tag_repo(req.context)
            if marker:
                metadef_tag = tag_repo.get(namespace, marker)
                marker = metadef_tag.tag_id

            db_metatag_list = tag_repo.list(
                marker=marker, limit=limit, sort_key=sort_key,
                sort_dir=sort_dir, filters=filters)

            tag_list = [MetadefTag(**{'name': db_metatag.name})
                        for db_metatag in db_metatag_list]

            metadef_tags = MetadefTags()
            metadef_tags.tags = tag_list
        except exception.Forbidden as e:
            LOG.debug("User not permitted to retrieve metadata tags "
                      "within '%s' namespace" % namespace)
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            raise webob.exc.HTTPInternalServerError()

        return metadef_tags
Beispiel #21
0
    def create_tags(self, req, metadata_tags, namespace):
        tag_factory = self.gateway.get_metadef_tag_factory(req.context)
        tag_repo = self.gateway.get_metadef_tag_repo(req.context)
        try:
            tag_list = []
            for metadata_tag in metadata_tags.tags:
                tag_list.append(tag_factory.new_tag(
                    namespace=namespace, **metadata_tag.to_dict()))
            tag_repo.add_tags(tag_list)
            tag_list_out = [MetadefTag(**{'name': db_metatag.name})
                            for db_metatag in tag_list]
            metadef_tags = MetadefTags()
            metadef_tags.tags = tag_list_out
        except exception.Forbidden as e:
            LOG.debug("User not permitted to create metadata tags within "
                      "'%s' namespace" % namespace)
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except exception.Duplicate as e:
            raise webob.exc.HTTPConflict(explanation=e.msg)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            raise webob.exc.HTTPInternalServerError()

        return metadef_tags
Beispiel #22
0
 def test_remove_image_not_found(self):
     fake_uuid = str(uuid.uuid4())
     image = self.image_repo.get(UUID1)
     image.image_id = fake_uuid
     exc = self.assertRaises(
         exception.ImageNotFound, self.image_repo.remove, image)
     self.assertIn(fake_uuid, utils.exception_to_str(exc))
Beispiel #23
0
def store_add_to_backend(image_id, data, size, store):
    """
    A wrapper around a call to each stores add() method.  This gives glance
    a common place to check the output

    :param image_id:  The image add to which data is added
    :param data: The data to be stored
    :param size: The length of the data in bytes
    :param store: The store to which the data is being added
    :return: The url location of the file,
             the size amount of data,
             the checksum of the data
             the storage systems metadata dictionary for the location
    """
    (location, size, checksum, metadata) = store.add(image_id, data, size)
    if metadata is not None:
        if not isinstance(metadata, dict):
            msg = (_("The storage driver %(store)s returned invalid metadata "
                     "%(metadata)s. This must be a dictionary type") %
                   {'store': six.text_type(store),
                    'metadata': six.text_type(metadata)})
            LOG.error(msg)
            raise BackendException(msg)
        try:
            check_location_metadata(metadata)
        except BackendException as e:
            e_msg = (_("A bad metadata structure was returned from the "
                       "%(store)s storage driver: %(metadata)s.  %(error)s.") %
                     {'store': six.text_type(store),
                      'metadata': six.text_type(metadata),
                      'error': utils.exception_to_str(e)})
            LOG.error(e_msg)
            raise BackendException(e_msg)
    return (location, size, checksum, metadata)
Beispiel #24
0
    def show(self, req, namespace, filters=None):
        try:
            # Get namespace
            ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
            namespace_obj = ns_repo.get(namespace)
            namespace_detail = Namespace.to_wsme_model(
                namespace_obj,
                get_namespace_href(namespace_obj),
                self.ns_schema_link)
            ns_filters = dict()
            ns_filters['namespace'] = namespace

            # Get objects
            object_repo = self.gateway.get_metadef_object_repo(req.context)
            db_metaobject_list = object_repo.list(filters=ns_filters)
            object_list = [MetadefObject.to_wsme_model(
                db_metaobject,
                get_object_href(namespace, db_metaobject),
                self.obj_schema_link) for db_metaobject in db_metaobject_list]
            if object_list:
                namespace_detail.objects = object_list

            # Get resource type associations
            rs_repo = self.gateway.get_metadef_resource_type_repo(req.context)
            db_resource_type_list = rs_repo.list(filters=ns_filters)
            resource_type_list = [ResourceTypeAssociation.to_wsme_model(
                resource_type) for resource_type in db_resource_type_list]
            if resource_type_list:
                namespace_detail.resource_type_associations = (
                    resource_type_list)

            # Get properties
            prop_repo = self.gateway.get_metadef_property_repo(req.context)
            db_properties = prop_repo.list(filters=ns_filters)
            property_list = Namespace.to_model_properties(db_properties)
            if property_list:
                namespace_detail.properties = property_list

            if filters and filters['resource_type']:
                namespace_detail = self._prefix_property_name(
                    namespace_detail, filters['resource_type'])

            # Get tags
            tag_repo = self.gateway.get_metadef_tag_repo(req.context)
            db_metatag_list = tag_repo.list(filters=ns_filters)
            tag_list = [MetadefTag(**{'name': db_metatag.name})
                        for db_metatag in db_metatag_list]
            if tag_list:
                namespace_detail.tags = tag_list

        except exception.Forbidden as e:
            LOG.debug("User not permitted to show metadata namespace "
                      "'%s'", namespace)
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            raise webob.exc.HTTPInternalServerError()
        return namespace_detail
def safe_delete_from_backend(context, image_id, location):
    """
    Given a location, delete an image from the store and
    update location status to db.

    This function try to handle all known exceptions which might be raised
    by those calls on store and DB modules in its implementation.

    :param context: The request context
    :param image_id: The image identifier
    :param location: The image location entry
    """

    try:
        ret = store_api.delete_from_backend(location['url'], context=context)
        location['status'] = 'deleted'
        if 'id' in location:
            db_api.get_api().image_location_delete(context, image_id,
                                                   location['id'], 'deleted')
        return ret
    except store_api.NotFound:
        msg = _LW('Failed to delete image %s in store from URI') % image_id
        LOG.warn(msg)
    except store_api.StoreDeleteNotSupported as e:
        LOG.warn(utils.exception_to_str(e))
    except store_api.UnsupportedBackend:
        exc_type = sys.exc_info()[0].__name__
        msg = (_LE('Failed to delete image %(image_id)s from store: %(exc)s') %
               dict(image_id=image_id, exc=exc_type))
        LOG.error(msg)
Beispiel #26
0
    def execute(self, image_id):
        """Finishing the task flow

        :param image_id: Glance Image ID
        """
        task = script_utils.get_task(self.task_repo, self.task_id)
        if task is None:
            return
        try:
            task.succeed({'image_id': image_id})
        except Exception as e:
            # Note: The message string contains Error in it to indicate
            # in the task.message that it's a error message for the user.

            # TODO(nikhil): need to bring back save_and_reraise_exception when
            # necessary
            err_msg = ("Error: " + six.text_type(type(e)) + ': ' +
                       common_utils.exception_to_str(e))
            log_msg = err_msg + _LE("Task ID %s") % task.task_id
            LOG.exception(log_msg)

            task.fail(err_msg)
        finally:
            self.task_repo.save(task)

        LOG.info(_LI("%(task_id)s of %(task_type)s completed") %
                 {'task_id': self.task_id, 'task_type': self.task_type})
Beispiel #27
0
    def _deserialize(self, request):
        result = {}
        try:
            result["image_meta"] = utils.get_image_meta_from_headers(request)
        except exception.InvalidParameterValue as e:
            msg = utils.exception_to_str(e)
            LOG.warn(msg, exc_info=True)
            raise HTTPBadRequest(explanation=e.msg, request=request)

        image_meta = result["image_meta"]
        image_meta = validate_image_meta(request, image_meta)
        if request.content_length:
            image_size = request.content_length
        elif "size" in image_meta:
            image_size = image_meta["size"]
        else:
            image_size = None

        data = request.body_file if self.has_body(request) else None

        if image_size is None and data is not None:
            data = utils.LimitingReader(data, CONF.image_size_cap)

            # NOTE(bcwaldon): this is a hack to make sure the downstream code
            # gets the correct image data
            request.body_file = data

        elif image_size > CONF.image_size_cap:
            max_image_size = CONF.image_size_cap
            msg = _("Denying attempt to upload image larger than %d bytes.")
            LOG.warn(msg % max_image_size)
            raise HTTPBadRequest(explanation=msg % max_image_size, request=request)

        result["image_data"] = data
        return result
 def test_remove_namespace_not_found(self):
     fake_name = 'fake_name'
     namespace = self.namespace_repo.get(NAMESPACE1)
     namespace.namespace = fake_name
     exc = self.assertRaises(exception.NotFound, self.namespace_repo.remove,
                             namespace)
     self.assertIn(fake_name, utils.exception_to_str(exc))
Beispiel #29
0
    def test_add_no_container_no_create(self):
        """
        Tests that adding an image with a non-existing container
        raises an appropriate exception
        """
        self.config(swift_store_create_container_on_put=False,
                    swift_store_container='noexist')
        self.store = Store()

        image_swift = six.StringIO("nevergonnamakeit")

        global SWIFT_PUT_OBJECT_CALLS
        SWIFT_PUT_OBJECT_CALLS = 0

        # We check the exception text to ensure the container
        # missing text is found in it, otherwise, we would have
        # simply used self.assertRaises here
        exception_caught = False
        try:
            self.store.add(str(uuid.uuid4()), image_swift, 0)
        except BackendException as e:
            exception_caught = True
            self.assertIn("container noexist does not exist "
                          "in Swift", utils.exception_to_str(e))
        self.assertTrue(exception_caught)
        self.assertEqual(SWIFT_PUT_OBJECT_CALLS, 0)
Beispiel #30
0
 def update(self, req, id, type_name, type_version, changes, **kwargs):
     """Performs an update via json patch request"""
     artifact_repo = self.gateway.get_artifact_repo(req.context)
     try:
         artifact = self._get_artifact_with_dependencies(artifact_repo, id,
                                                         type_name,
                                                         type_version)
         updated = artifact
         for change in changes:
             updated = self._do_update_op(updated, change)
         artifact_repo.save(updated)
         return self._get_artifact_with_dependencies(artifact_repo, id)
     except (exception.InvalidArtifactPropertyValue,
             exception.InvalidJsonPatchPath,
             exception.InvalidParameterValue) as e:
         raise webob.exc.HTTPBadRequest(explanation=e.msg)
     except exception.NotFound as e:
         raise webob.exc.HTTPNotFound(explanation=e.msg)
     except exception.Forbidden as e:
         raise webob.exc.HTTPForbidden(explanation=e.msg)
     except exception.StorageQuotaFull as e:
         msg = (_("Denying attempt to upload artifact because it exceeds "
                  "the quota: %s") % utils.exception_to_str(e))
         raise webob.exc.HTTPRequestEntityTooLarge(
             explanation=msg, request=req, content_type='text/plain')
     except exception.Invalid as e:
         raise webob.exc.HTTPBadRequest(explanation=e.msg)
     except exception.LimitExceeded as e:
         raise webob.exc.HTTPRequestEntityTooLarge(
             explanation=e.msg, request=req, content_type='text/plain')
Beispiel #31
0
 def test_get_tag_not_found(self):
     exc = self.assertRaises(exception.NotFound, self.tag_repo.get,
                             NAMESPACE2, TAG1)
     self.assertIn(TAG1, utils.exception_to_str(exc))
Beispiel #32
0
 def test_get_property_not_found(self):
     exc = self.assertRaises(exception.NotFound, self.property_repo.get,
                             NAMESPACE2, PROPERTY1)
     self.assertIn(PROPERTY1, utils.exception_to_str(exc))
Beispiel #33
0
    def update_all(self, req, image_id, body):
        """
        Replaces the members of the image with those specified in the
        body.  The body is a dict with the following format::

            {"memberships": [
                {"member_id": <MEMBER_ID>,
                 ["can_share": [True|False]]}, ...
            ]}
        """
        self._check_can_access_image_members(req.context)

        # Make sure the image exists
        try:
            image = self.db_api.image_get(req.context, image_id)
        except exception.NotFound:
            msg = _("Image %(id)s not found") % {'id': image_id}
            LOG.info(msg)
            raise webob.exc.HTTPNotFound(msg)
        except exception.Forbidden:
            # If it's private and doesn't belong to them, don't let on
            # that it exists
            msg = _("Access denied to image %(id)s but returning 'not found'")
            LOG.info(msg % {'id': image_id})
            raise webob.exc.HTTPNotFound()

        # Can they manipulate the membership?
        if not self.is_image_sharable(req.context, image):
            msg = _("User lacks permission to share image %(id)s")
            LOG.info(msg % {'id': image_id})
            msg = _("No permission to share that image")
            raise webob.exc.HTTPForbidden(msg)

        # Get the membership list
        try:
            memb_list = body['memberships']
        except Exception as e:
            # Malformed entity...
            msg = _("Invalid membership association specified for "
                    "image %(id)s")
            LOG.info(msg % {'id': image_id})
            msg = (_("Invalid membership association: %s") %
                   utils.exception_to_str(e))
            raise webob.exc.HTTPBadRequest(explanation=msg)

        add = []
        existing = {}
        # Walk through the incoming memberships
        for memb in memb_list:
            try:
                datum = dict(image_id=image['id'],
                             member=memb['member_id'],
                             can_share=None)
            except Exception as e:
                # Malformed entity...
                msg = _("Invalid membership association specified for "
                        "image %(id)s")
                LOG.info(msg % {'id': image_id})
                msg = (_("Invalid membership association: %s") %
                       utils.exception_to_str(e))
                raise webob.exc.HTTPBadRequest(explanation=msg)

            # Figure out what can_share should be
            if 'can_share' in memb:
                datum['can_share'] = bool(memb['can_share'])

            # Try to find the corresponding membership
            members = self.db_api.image_member_find(req.context,
                                                    image_id=datum['image_id'],
                                                    member=datum['member'])
            try:
                member = members[0]
            except IndexError:
                # Default can_share
                datum['can_share'] = bool(datum['can_share'])
                add.append(datum)
            else:
                # Are we overriding can_share?
                if datum['can_share'] is None:
                    datum['can_share'] = members[0]['can_share']

                existing[member['id']] = {
                    'values': datum,
                    'membership': member,
                }

        # We now have a filtered list of memberships to add and
        # memberships to modify.  Let's start by walking through all
        # the existing image memberships...
        existing_members = self.db_api.image_member_find(req.context,
                                                         image_id=image['id'])
        for member in existing_members:
            if member['id'] in existing:
                # Just update the membership in place
                update = existing[member['id']]['values']
                self.db_api.image_member_update(req.context, member['id'],
                                                update)
            else:
                # Outdated one; needs to be deleted
                self.db_api.image_member_delete(req.context, member['id'])

        # Now add the non-existent ones
        for memb in add:
            self.db_api.image_member_create(req.context, memb)

        # Make an appropriate result
        msg = _("Successfully updated memberships for image %(id)s")
        LOG.info(msg % {'id': image_id})
        return webob.exc.HTTPNoContent()
Beispiel #34
0
    def update(self, req, id, body):
        """Updates an existing image with the registry.

        :param req: wsgi Request object
        :param body: Dictionary of information about the image
        :param id:  The opaque internal identifier for the image

        :retval Returns the updated image information as a mapping,
        """
        image_data = body['image']
        from_state = body.get('from_state', None)

        # Prohibit modification of 'owner'
        if not req.context.is_admin and 'owner' in image_data:
            del image_data['owner']

        if 'location' in image_data:
            image_data['locations'] = [image_data.pop('location')]

        purge_props = req.headers.get("X-Glance-Registry-Purge-Props", "false")
        try:
            LOG.debug(
                "Updating image %(id)s with metadata: %(image_data)r", {
                    'id': id,
                    'image_data':
                    {k: v
                     for k, v in image_data.items() if k != 'locations'}
                })
            image_data = _normalize_image_location_for_db(image_data)
            if purge_props == "true":
                purge_props = True
            else:
                purge_props = False

            updated_image = self.db_api.image_update(req.context,
                                                     id,
                                                     image_data,
                                                     purge_props=purge_props,
                                                     from_state=from_state)

            msg = _LI("Updating metadata for image %(id)s") % {'id': id}
            LOG.info(msg)
            return dict(image=make_image_dict(updated_image))
        except exception.Invalid as e:
            msg = (_("Failed to update image metadata. "
                     "Got error: %s") % utils.exception_to_str(e))
            LOG.error(msg)
            return exc.HTTPBadRequest(msg)
        except exception.ImageNotFound:
            msg = _LI("Image %(id)s not found") % {'id': id}
            LOG.info(msg)
            raise exc.HTTPNotFound(body='Image not found',
                                   request=req,
                                   content_type='text/plain')
        except exception.ForbiddenPublicImage:
            msg = _LI("Update denied for public image %(id)s") % {'id': id}
            LOG.info(msg)
            raise exc.HTTPForbidden()
        except exception.Forbidden:
            # If it's private and doesn't belong to them, don't let on
            # that it exists
            msg = _LI("Access denied to image %(id)s but returning"
                      " 'not found'") % {
                          'id': id
                      }
            LOG.info(msg)
            raise exc.HTTPNotFound(body='Image not found',
                                   request=req,
                                   content_type='text/plain')
        except exception.Conflict as e:
            LOG.info(utils.exception_to_str(e))
            raise exc.HTTPConflict(body='Image operation conflicts',
                                   request=req,
                                   content_type='text/plain')
        except Exception:
            LOG.exception(_LE("Unable to update image %s") % id)
            raise
Beispiel #35
0
    def create(self, req, namespace):
        try:
            namespace_created = False
            # Create Namespace
            ns_factory = self.gateway.get_metadef_namespace_factory(
                req.context)
            ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
            new_namespace = ns_factory.new_namespace(**namespace.to_dict())
            ns_repo.add(new_namespace)
            namespace_created = True

            # Create Resource Types
            if namespace.resource_type_associations:
                rs_factory = (self.gateway.get_metadef_resource_type_factory(
                    req.context))
                rs_repo = self.gateway.get_metadef_resource_type_repo(
                    req.context)
                for resource_type in namespace.resource_type_associations:
                    new_resource = rs_factory.new_resource_type(
                        namespace=namespace.namespace,
                        **resource_type.to_dict())
                    rs_repo.add(new_resource)

            # Create Objects
            if namespace.objects:
                object_factory = self.gateway.get_metadef_object_factory(
                    req.context)
                object_repo = self.gateway.get_metadef_object_repo(req.context)
                for metadata_object in namespace.objects:
                    new_meta_object = object_factory.new_object(
                        namespace=namespace.namespace,
                        **metadata_object.to_dict())
                    object_repo.add(new_meta_object)

            # Create Tags
            if namespace.tags:
                tag_factory = self.gateway.get_metadef_tag_factory(req.context)
                tag_repo = self.gateway.get_metadef_tag_repo(req.context)
                for metadata_tag in namespace.tags:
                    new_meta_tag = tag_factory.new_tag(
                        namespace=namespace.namespace,
                        **metadata_tag.to_dict())
                    tag_repo.add(new_meta_tag)

            # Create Namespace Properties
            if namespace.properties:
                prop_factory = (self.gateway.get_metadef_property_factory(
                    req.context))
                prop_repo = self.gateway.get_metadef_property_repo(req.context)
                for (name, value) in namespace.properties.items():
                    new_property_type = (prop_factory.new_namespace_property(
                        namespace=namespace.namespace,
                        **self._to_property_dict(name, value)))
                    prop_repo.add(new_property_type)

        except exception.Forbidden as e:
            self._cleanup_namespace(ns_repo, namespace, namespace_created)
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.NotFound as e:
            self._cleanup_namespace(ns_repo, namespace, namespace_created)
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except exception.Duplicate as e:
            self._cleanup_namespace(ns_repo, namespace, namespace_created)
            raise webob.exc.HTTPConflict(explanation=e.msg)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            raise webob.exc.HTTPInternalServerError()

        # Return the user namespace as we don't expose the id to user
        new_namespace.properties = namespace.properties
        new_namespace.objects = namespace.objects
        new_namespace.resource_type_associations = (
            namespace.resource_type_associations)
        new_namespace.tags = namespace.tags
        return Namespace.to_wsme_model(new_namespace,
                                       get_namespace_href(new_namespace),
                                       self.ns_schema_link)
Beispiel #36
0
def _populate_metadata(meta, metadata_path=None):
    if not metadata_path:
        metadata_path = CONF.metadata_source_path

    try:
        json_schema_files = [
            f for f in os.listdir(metadata_path)
            if isfile(join(metadata_path, f)) and f.endswith('.json')
        ]
    except OSError as e:
        LOG.error(utils.exception_to_str(e))
        return

    metadef_namespaces_table = get_metadef_namespaces_table(meta)
    metadef_namespace_resource_types_tables =\
        get_metadef_namespace_resource_types_table(meta)
    metadef_objects_table = get_metadef_objects_table(meta)
    metadef_properties_table = get_metadef_properties_table(meta)
    metadef_resource_types_table = get_metadef_resource_types_table(meta)

    if not json_schema_files:
        LOG.error(_LE("Json schema files not found in %s. Aborting."),
                  metadata_path)
        return

    for namespace_id, json_schema_file in enumerate(json_schema_files,
                                                    start=1):
        try:
            file = join(metadata_path, json_schema_file)
            json_metadata = open(file)
            metadata = json.load(json_metadata)
            json_metadata.close()
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            continue

        values = {
            'id': namespace_id,
            'namespace': metadata.get('namespace', None),
            'display_name': metadata.get('display_name', None),
            'description': metadata.get('description', None),
            'visibility': metadata.get('visibility', None),
            'protected': metadata.get('protected', None),
            'owner': metadata.get('owner', 'admin'),
            'created_at': timeutils.utcnow()
        }
        _insert_data_to_db(metadef_namespaces_table, values)

        for resource_type in metadata.get('resource_type_associations', []):
            try:
                resource_type_id = \
                    _get_resource_type_id(meta, resource_type['name'])
            except AttributeError:
                values = {
                    'name': resource_type['name'],
                    'protected': True,
                    'created_at': timeutils.utcnow()
                }
                _insert_data_to_db(metadef_resource_types_table, values)
                resource_type_id =\
                    _get_resource_type_id(meta, resource_type['name'])

            values = {
                'resource_type_id': resource_type_id,
                'namespace_id': namespace_id,
                'created_at': timeutils.utcnow(),
                'properties_target': resource_type.get('properties_target'),
                'prefix': resource_type.get('prefix', None)
            }
            _insert_data_to_db(metadef_namespace_resource_types_tables, values)

        for property, schema in metadata.get('properties', {}).iteritems():
            values = {
                'name': property,
                'namespace_id': namespace_id,
                'schema': json.dumps(schema),
                'created_at': timeutils.utcnow()
            }
            _insert_data_to_db(metadef_properties_table, values)

        for object in metadata.get('objects', []):
            values = {
                'name': object.get('name', None),
                'description': object.get('description', None),
                'namespace_id': namespace_id,
                'schema': json.dumps(object.get('properties', None)),
                'created_at': timeutils.utcnow()
            }
            _insert_data_to_db(metadef_objects_table, values)

        LOG.info(_LI("File %s loaded to database."), file)

    LOG.info(_LI("Metadata loading finished"))
Beispiel #37
0
def upload_data_to_store(req, image_meta, image_data, store, notifier):
    """
    Upload image data to specified store.

    Upload image data to the store and cleans up on error.
    """
    image_id = image_meta['id']

    db_api = glance.db.get_api()
    image_size = image_meta.get('size')

    try:
        remaining = glance.api.common.check_quota(
            req.context, image_size, db_api, image_id=image_id)
        if remaining is not None:
            image_data = utils.LimitingReader(image_data, remaining)

        (uri,
         size,
         checksum,
         location_metadata) = store_api.store_add_to_backend(
             image_meta['id'],
             utils.CooperativeReader(image_data),
             image_meta['size'],
             store)

        location_data = {'url': uri,
                         'metadata': location_metadata,
                         'status': 'active'}

        try:
            # recheck the quota in case there were simultaneous uploads that
            # did not provide the size
            glance.api.common.check_quota(
                req.context, size, db_api, image_id=image_id)
        except exception.StorageQuotaFull:
            with excutils.save_and_reraise_exception():
                LOG.info(_LI('Cleaning up %s after exceeding '
                             'the quota') % image_id)
                store_utils.safe_delete_from_backend(
                    req.context, image_meta['id'], location_data)

        def _kill_mismatched(image_meta, attr, actual):
            supplied = image_meta.get(attr)
            if supplied and supplied != actual:
                msg = (_("Supplied %(attr)s (%(supplied)s) and "
                         "%(attr)s generated from uploaded image "
                         "(%(actual)s) did not match. Setting image "
                         "status to 'killed'.") % {'attr': attr,
                                                   'supplied': supplied,
                                                   'actual': actual})
                LOG.error(msg)
                safe_kill(req, image_id, 'saving')
                initiate_deletion(req, location_data, image_id)
                raise webob.exc.HTTPBadRequest(explanation=msg,
                                               content_type="text/plain",
                                               request=req)

        # Verify any supplied size/checksum value matches size/checksum
        # returned from store when adding image
        _kill_mismatched(image_meta, 'size', size)
        _kill_mismatched(image_meta, 'checksum', checksum)

        # Update the database with the checksum returned
        # from the backend store
        LOG.debug("Updating image %(image_id)s data. "
                  "Checksum set to %(checksum)s, size set "
                  "to %(size)d", {'image_id': image_id,
                                  'checksum': checksum,
                                  'size': size})
        update_data = {'checksum': checksum,
                       'size': size}
        try:
            image_meta = registry.update_image_metadata(req.context,
                                                        image_id,
                                                        update_data,
                                                        from_state='saving')

        except exception.NotFound as e:
            msg = _LI("Image %s could not be found after upload. The image may"
                      " have been deleted during the upload.") % image_id
            LOG.info(msg)

            # NOTE(jculp): we need to clean up the datastore if an image
            # resource is deleted while the image data is being uploaded
            #
            # We get "location_data" from above call to store.add(), any
            # exceptions that occur there handle this same issue internally,
            # Since this is store-agnostic, should apply to all stores.
            initiate_deletion(req, location_data, image_id)
            raise webob.exc.HTTPPreconditionFailed(explanation=msg,
                                                   request=req,
                                                   content_type='text/plain')

    except exception.Duplicate as e:
        msg = u"Attempt to upload duplicate image: %s" % e
        LOG.debug(msg)
        # NOTE(dosaboy): do not delete the image since it is likely that this
        # conflict is a result of another concurrent upload that will be
        # successful.
        notifier.error('image.upload', msg)
        raise webob.exc.HTTPConflict(explanation=msg,
                                     request=req,
                                     content_type="text/plain")

    except exception.Forbidden as e:
        msg = u"Forbidden upload attempt: %s" % e
        LOG.debug(msg)
        safe_kill(req, image_id, 'saving')
        notifier.error('image.upload', msg)
        raise webob.exc.HTTPForbidden(explanation=msg,
                                      request=req,
                                      content_type="text/plain")

    except store_api.StorageFull as e:
        msg = _("Image storage media is full: %s") % utils.exception_to_str(e)
        LOG.error(msg)
        safe_kill(req, image_id, 'saving')
        notifier.error('image.upload', msg)
        raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                  request=req,
                                                  content_type='text/plain')

    except store_api.StorageWriteDenied as e:
        msg = (_("Insufficient permissions on image storage media: %s") %
               utils.exception_to_str(e))
        LOG.error(msg)
        safe_kill(req, image_id, 'saving')
        notifier.error('image.upload', msg)
        raise webob.exc.HTTPServiceUnavailable(explanation=msg,
                                               request=req,
                                               content_type='text/plain')

    except exception.ImageSizeLimitExceeded as e:
        msg = (_("Denying attempt to upload image larger than %d bytes.")
               % CONF.image_size_cap)
        LOG.info(msg)
        safe_kill(req, image_id, 'saving')
        notifier.error('image.upload', msg)
        raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                  request=req,
                                                  content_type='text/plain')

    except exception.StorageQuotaFull as e:
        msg = (_("Denying attempt to upload image because it exceeds the ."
                 "quota: %s") % utils.exception_to_str(e))
        LOG.info(msg)
        safe_kill(req, image_id, 'saving')
        notifier.error('image.upload', msg)
        raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                  request=req,
                                                  content_type='text/plain')

    except webob.exc.HTTPError:
        #NOTE(bcwaldon): Ideally, we would just call 'raise' here,
        # but something in the above function calls is affecting the
        # exception context and we must explicitly re-raise the
        # caught exception.
        msg = _LE("Received HTTP error while uploading image %s") % image_id
        notifier.error('image.upload', msg)
        with excutils.save_and_reraise_exception():
            LOG.exception(msg)
            safe_kill(req, image_id, 'saving')

    except (ValueError, IOError) as e:
        msg = "Client disconnected before sending all data to backend"
        LOG.debug(msg)
        safe_kill(req, image_id, 'saving')
        raise webob.exc.HTTPBadRequest(explanation=msg,
                                       content_type="text/plain",
                                       request=req)

    except Exception as e:
        msg = _("Failed to upload image %s") % image_id
        LOG.exception(msg)
        safe_kill(req, image_id, 'saving')
        notifier.error('image.upload', msg)
        raise webob.exc.HTTPInternalServerError(explanation=msg,
                                                request=req,
                                                content_type='text/plain')

    return image_meta, location_data
Beispiel #38
0
    def upload(self, req, image_id, data, size):
        image_repo = self.gateway.get_repo(req.context)
        image = None
        try:
            image = image_repo.get(image_id)
            image.status = 'saving'
            try:
                image_repo.save(image)
                image.set_data(data, size)
                image_repo.save(image)
            except exception.NotFound as e:
                msg = (_("Image %(id)s could not be found after upload."
                         "The image may have been deleted during the upload: "
                         "%(error)s Cleaning up the chunks uploaded") %
                       {'id': image_id,
                        'error': utils.exception_to_str(e)})
                LOG.warn(msg)
                # NOTE(sridevi): Cleaning up the uploaded chunks.
                try:
                    image.delete()
                except exception.NotFound:
                    # NOTE(sridevi): Ignore this exception
                    pass
                raise webob.exc.HTTPGone(explanation=msg,
                                         request=req,
                                         content_type='text/plain')

        except ValueError as e:
            LOG.debug("Cannot save data for image %(id)s: %(e)s",
                      {'id': image_id, 'e': utils.exception_to_str(e)})
            self._restore(image_repo, image)
            raise webob.exc.HTTPBadRequest(
                explanation=utils.exception_to_str(e))

        except glance_store.StoreAddDisabled:
            msg = _("Error in store configuration. Adding images to store "
                    "is disabled.")
            LOG.exception(msg)
            self._restore(image_repo, image)
            raise webob.exc.HTTPGone(explanation=msg, request=req,
                                     content_type='text/plain')

        except exception.InvalidImageStatusTransition as e:
            msg = utils.exception_to_str(e)
            LOG.debug(msg)
            raise webob.exc.HTTPConflict(explanation=e.msg, request=req)

        except exception.Forbidden as e:
            msg = ("Not allowed to upload image data for image %s" %
                   image_id)
            LOG.debug(msg)
            raise webob.exc.HTTPForbidden(explanation=msg, request=req)

        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)

        except glance_store.StorageFull as e:
            msg = _("Image storage media "
                    "is full: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(image_repo, image)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                      request=req)

        except exception.StorageQuotaFull as e:
            msg = _("Image exceeds the storage "
                    "quota: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(image_repo, image)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                      request=req)

        except exception.ImageSizeLimitExceeded as e:
            msg = _("The incoming image is "
                    "too large: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(image_repo, image)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                      request=req)

        except glance_store.StorageWriteDenied as e:
            msg = _("Insufficient permissions on image "
                    "storage media: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(image_repo, image)
            raise webob.exc.HTTPServiceUnavailable(explanation=msg,
                                                   request=req)

        except webob.exc.HTTPError as e:
            with excutils.save_and_reraise_exception():
                LOG.error(_LE("Failed to upload image data due to HTTP error"))
                self._restore(image_repo, image)

        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.exception(_LE("Failed to upload image data due to "
                                  "internal error"))
                self._restore(image_repo, image)
Beispiel #39
0
    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')
        if Controller._copy_from(req):
            self._enforce(req, 'copy_from')
        if image_data or Controller._copy_from(req):
            self._enforce(req, 'upload_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')
        if orig_or_updated_loc:
            try:
                self.update_store_acls(req,
                                       id,
                                       orig_or_updated_loc,
                                       public=is_public)
            except exception.BadStoreUri:
                msg = "Invalid location %s" % location
                LOG.debug(msg)
                raise HTTPBadRequest(explanation=msg,
                                     request=req,
                                     content_type="text/plain")

        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")
        except (exception.Conflict, exception.Duplicate) as e:
            LOG.info(utils.exception_to_str(e))
            raise HTTPConflict(body='Image operation conflicts',
                               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}
Beispiel #40
0
    def add(self, image_id, image_file, image_size, connection=None):
        location = self.create_location(image_id)
        if not connection:
            connection = self.get_connection(location)

        self._create_container_if_missing(location.container, connection)

        LOG.debug("Adding image object '%(obj_name)s' "
                  "to Swift" % dict(obj_name=location.obj))
        try:
            if image_size > 0 and image_size < self.large_object_size:
                # Image size is known, and is less than large_object_size.
                # Send to Swift with regular PUT.
                obj_etag = connection.put_object(location.container,
                                                 location.obj,
                                                 image_file,
                                                 content_length=image_size)
            else:
                # Write the image into Swift in chunks.
                chunk_id = 1
                if image_size > 0:
                    total_chunks = str(
                        int(
                            math.ceil(
                                float(image_size) /
                                float(self.large_object_chunk_size))))
                else:
                    # image_size == 0 is when we don't know the size
                    # of the image. This can occur with older clients
                    # that don't inspect the payload size.
                    LOG.debug("Cannot determine image size. Adding as a "
                              "segmented object to Swift.")
                    total_chunks = '?'

                checksum = hashlib.md5()
                written_chunks = []
                combined_chunks_size = 0
                while True:
                    chunk_size = self.large_object_chunk_size
                    if image_size == 0:
                        content_length = None
                    else:
                        left = image_size - combined_chunks_size
                        if left == 0:
                            break
                        if chunk_size > left:
                            chunk_size = left
                        content_length = chunk_size

                    chunk_name = "%s-%05d" % (location.obj, chunk_id)
                    reader = ChunkReader(image_file, checksum, chunk_size)
                    try:
                        chunk_etag = connection.put_object(
                            location.container,
                            chunk_name,
                            reader,
                            content_length=content_length)
                        written_chunks.append(chunk_name)
                    except Exception:
                        # Delete orphaned segments from swift backend
                        with excutils.save_and_reraise_exception():
                            LOG.exception(
                                _("Error during chunked upload to "
                                  "backend, deleting stale chunks"))
                            self._delete_stale_chunks(connection,
                                                      location.container,
                                                      written_chunks)

                    bytes_read = reader.bytes_read
                    msg = ("Wrote chunk %(chunk_name)s (%(chunk_id)d/"
                           "%(total_chunks)s) of length %(bytes_read)d "
                           "to Swift returning MD5 of content: "
                           "%(chunk_etag)s" % {
                               'chunk_name': chunk_name,
                               'chunk_id': chunk_id,
                               'total_chunks': total_chunks,
                               'bytes_read': bytes_read,
                               'chunk_etag': chunk_etag
                           })
                    LOG.debug(msg)

                    if bytes_read == 0:
                        # Delete the last chunk, because it's of zero size.
                        # This will happen if size == 0.
                        LOG.debug("Deleting final zero-length chunk")
                        connection.delete_object(location.container,
                                                 chunk_name)
                        break

                    chunk_id += 1
                    combined_chunks_size += bytes_read

                # In the case we have been given an unknown image size,
                # set the size to the total size of the combined chunks.
                if image_size == 0:
                    image_size = combined_chunks_size

                # Now we write the object manifest and return the
                # manifest's etag...
                manifest = "%s/%s-" % (location.container, location.obj)
                headers = {
                    'ETag': hashlib.md5("").hexdigest(),
                    'X-Object-Manifest': manifest
                }

                # The ETag returned for the manifest is actually the
                # MD5 hash of the concatenated checksums of the strings
                # of each chunk...so we ignore this result in favour of
                # the MD5 of the entire image file contents, so that
                # users can verify the image file contents accordingly
                connection.put_object(location.container,
                                      location.obj,
                                      None,
                                      headers=headers)
                obj_etag = checksum.hexdigest()

            # NOTE: We return the user and key here! Have to because
            # location is used by the API server to return the actual
            # image data. We *really* should consider NOT returning
            # the location attribute from GET /images/<ID> and
            # GET /images/details
            if swift_store_utils.is_multiple_swift_store_accounts_enabled():
                include_creds = False
            else:
                include_creds = True

            return (location.get_uri(credentials_included=include_creds),
                    image_size, obj_etag, {})
        except swiftclient.ClientException as e:
            if e.http_status == httplib.CONFLICT:
                raise exception.Duplicate(
                    _("Swift already has an image at "
                      "this location"))
            msg = (_("Failed to add object to Swift.\n"
                     "Got error from Swift: %s") % utils.exception_to_str(e))
            LOG.error(msg)
            raise glance.store.BackendException(msg)
Beispiel #41
0
    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)
Beispiel #42
0
def _populate_metadata(meta, metadata_path=None):
    if not metadata_path:
        metadata_path = CONF.metadata_source_path

    try:
        json_schema_files = [
            f for f in os.listdir(metadata_path)
            if isfile(join(metadata_path, f)) and f.endswith('.json')
        ]
    except OSError as e:
        LOG.error(utils.exception_to_str(e))
        return

    metadef_namespaces_table = get_metadef_namespaces_table(meta)
    metadef_namespace_resource_types_tables =\
        get_metadef_namespace_resource_types_table(meta)
    metadef_objects_table = get_metadef_objects_table(meta)
    metadef_tags_table = get_metadef_tags_table(meta)
    metadef_properties_table = get_metadef_properties_table(meta)
    metadef_resource_types_table = get_metadef_resource_types_table(meta)

    if not json_schema_files:
        LOG.error(_LE("Json schema files not found in %s. Aborting."),
                  metadata_path)
        return

    for json_schema_file in json_schema_files:
        try:
            file = join(metadata_path, json_schema_file)
            with open(file) as json_file:
                metadata = json.load(json_file)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            continue

        values = {
            'namespace': metadata.get('namespace', None),
            'display_name': metadata.get('display_name', None),
            'description': metadata.get('description', None),
            'visibility': metadata.get('visibility', None),
            'protected': metadata.get('protected', None),
            'owner': metadata.get('owner', 'admin'),
            'created_at': timeutils.utcnow()
        }

        temp = metadef_namespaces_table.select(
            whereclause='namespace = \'%s\'' % values['namespace'])\
            .execute().fetchone()

        if temp is not None:
            LOG.info(
                _LI("Skipping  namespace %s. It already exists in the "
                    "database."), values['namespace'])
            continue

        _insert_data_to_db(metadef_namespaces_table, values)

        db_namespace = select([
            metadef_namespaces_table.c.id
        ]).where(metadef_namespaces_table.c.namespace == values['namespace']
                 ).select_from(metadef_namespaces_table).execute().fetchone()
        namespace_id = db_namespace['id']

        for resource_type in metadata.get('resource_type_associations', []):
            try:
                resource_type_id = \
                    _get_resource_type_id(meta, resource_type['name'])
            except AttributeError:
                values = {
                    'name': resource_type['name'],
                    'protected': True,
                    'created_at': timeutils.utcnow()
                }
                _insert_data_to_db(metadef_resource_types_table, values)
                resource_type_id =\
                    _get_resource_type_id(meta, resource_type['name'])

            values = {
                'resource_type_id': resource_type_id,
                'namespace_id': namespace_id,
                'created_at': timeutils.utcnow(),
                'properties_target': resource_type.get('properties_target'),
                'prefix': resource_type.get('prefix', None)
            }
            _insert_data_to_db(metadef_namespace_resource_types_tables, values)

        for property, schema in six.iteritems(metadata.get('properties', {})):
            values = {
                'name': property,
                'namespace_id': namespace_id,
                'json_schema': json.dumps(schema),
                'created_at': timeutils.utcnow()
            }
            _insert_data_to_db(metadef_properties_table, values)

        for object in metadata.get('objects', []):
            values = {
                'name': object.get('name'),
                'description': object.get('description', None),
                'namespace_id': namespace_id,
                'json_schema': json.dumps(object.get('properties', None)),
                'created_at': timeutils.utcnow()
            }
            _insert_data_to_db(metadef_objects_table, values)

        for tag in metadata.get('tags', []):
            timeutils_utcnow = timeutils.utcnow()
            values = {
                'name': tag.get('name'),
                'namespace_id': namespace_id,
                'created_at': timeutils_utcnow,
                'updated_at': timeutils_utcnow
            }
            _insert_data_to_db(metadef_tags_table, values)

        LOG.info(_LI("File %s loaded to database."), file)

    LOG.info(_LI("Metadata loading finished"))
Beispiel #43
0
 def test_get_not_found(self):
     fake_uuid = str(uuid.uuid4())
     exc = self.assertRaises(exception.NotFound, self.image_repo.get,
                             fake_uuid)
     self.assertIn(fake_uuid, utils.exception_to_str(exc))
Beispiel #44
0
def _export_data_to_file(meta, path):
    if not path:
        path = CONF.metadata_source_path

    namespace_table = get_metadef_namespaces_table(meta)
    namespaces = namespace_table.select().execute().fetchall()

    pattern = re.compile('[\W_]+', re.UNICODE)

    for id, namespace in enumerate(namespaces, start=1):
        namespace_id = namespace['id']
        namespace_file_name = pattern.sub('', namespace['display_name'])

        values = {
            'namespace': namespace['namespace'],
            'display_name': namespace['display_name'],
            'description': namespace['description'],
            'visibility': namespace['visibility'],
            'protected': namespace['protected'],
            'owner': namespace['owner'],
            'resource_type_associations': [],
            'properties': {},
            'objects': [],
            'tags': []
        }

        namespace_resource_types = _get_namespace_resource_types(
            meta, namespace_id)
        db_objects = _get_objects(meta, namespace_id)
        db_properties = _get_properties(meta, namespace_id)
        db_tags = _get_tags(meta, namespace_id)

        resource_types = []
        for namespace_resource_type in namespace_resource_types:
            resource_type =\
                _get_resource_type(meta,
                                   namespace_resource_type['resource_type_id'])
            resource_types.append({
                'name': resource_type['name'],
                'protected': resource_type['protected']
            })
        values.update({'resource_type_associations': resource_types})

        objects = []
        for object in db_objects:
            objects.append({
                "name": object['name'],
                "description": object['description'],
                "properties": json.loads(object['json_schema'])
            })
        values.update({'objects': objects})

        properties = {}
        for property in db_properties:
            properties.update(
                {property['name']: json.loads(property['json_schema'])})
        values.update({'properties': properties})

        tags = []
        for tag in db_tags:
            tags.append({"name": tag['name']})
        values.update({'tags': tags})

        try:
            file_name = ''.join([path, namespace_file_name, '.json'])
            with open(file_name, 'w') as json_file:
                json_file.write(json.dumps(values))
        except Exception as e:
            LOG.exception(utils.exception_to_str(e))
        LOG.info(
            _LI("Namespace %(namespace)s saved in %(file)s") % {
                'namespace': namespace_file_name,
                'file': file_name
            })
Beispiel #45
0
 def test_list_object_namespace_not_found(self):
     exc = self.assertRaises(exception.NotFound,
                             self.object_repo.list,
                             filters={'namespace': 'not-a-namespace'})
     self.assertIn('not-a-namespace', utils.exception_to_str(exc))
Beispiel #46
0
 def _check_allowed(cls, image):
     for key in cls._disallowed_properties:
         if key in image:
             msg = _("Attribute '%s' is read-only.") % key
             raise webob.exc.HTTPForbidden(
                 explanation=utils.exception_to_str(msg))
Beispiel #47
0
    def _reserve(self, req, image_meta):
        """
        Adds the image metadata to the registry and assigns
        an image identifier if one is not supplied in the request
        headers. Sets the image's status to `queued`.

        :param req: The WSGI/Webob Request object
        :param id: The opaque image identifier
        :param image_meta: The image metadata

        :raises HTTPConflict if image already exists
        :raises HTTPBadRequest if image metadata is not valid
        """
        location = self._external_source(image_meta, req)
        store = image_meta.get('store')
        if store and store not in get_known_stores():
            msg = "Required store %s is invalid" % store
            LOG.debug(msg)
            raise HTTPBadRequest(explanation=msg,
                                 content_type='text/plain')

        image_meta['status'] = ('active' if image_meta.get('size') == 0
                                else 'queued')

        if location:
            try:
                store = get_store_from_location(location)
            except exception.BadStoreUri:
                msg = "Invalid location %s" % location
                LOG.debug(msg)
                raise HTTPBadRequest(explanation=msg,
                                     request=req,
                                     content_type="text/plain")
            # check the store exists before we hit the registry, but we
            # don't actually care what it is at this point
            self.get_store_or_400(req, store)

            # retrieve the image size from remote store (if not provided)
            image_meta['size'] = self._get_size(req.context, image_meta,
                                                location)
        else:
            # Ensure that the size attribute is set to zero for directly
            # uploadable images (if not provided). The size will be set
            # to a non-zero value during upload
            image_meta['size'] = image_meta.get('size', 0)

        try:
            image_meta = registry.add_image_metadata(req.context, image_meta)
            self.notifier.info("image.create", redact_loc(image_meta))
            return image_meta
        except exception.Duplicate:
            msg = ("An image with identifier %s already exists" %
                   image_meta['id'])
            LOG.debug(msg)
            raise HTTPConflict(explanation=msg,
                               request=req,
                               content_type="text/plain")
        except exception.Invalid as e:
            msg = (_("Failed to reserve image. Got error: %s") %
                   utils.exception_to_str(e))
            for line in msg.split('\n'):
                LOG.debug(line)
            raise HTTPBadRequest(explanation=msg,
                                 request=req,
                                 content_type="text/plain")
        except exception.Forbidden:
            msg = "Forbidden to reserve image."
            LOG.debug(msg)
            raise HTTPForbidden(explanation=msg,
                                request=req,
                                content_type="text/plain")
Beispiel #48
0
 def test_get_object_not_found(self):
     exc = self.assertRaises(exception.NotFound, self.object_repo.get,
                             NAMESPACE2, OBJECT1)
     self.assertIn(OBJECT1, utils.exception_to_str(exc))
Beispiel #49
0
def fail(e):
    global KNOWN_EXCEPTIONS
    return_code = KNOWN_EXCEPTIONS.index(type(e)) + 1
    sys.stderr.write("ERROR: %s\n" % utils.exception_to_str(e))
    sys.exit(return_code)
Beispiel #50
0
    def set_data(self, data, size=None):
        self.send_notification('image.prepare', self.repo)

        notify_error = self.notifier.error
        try:
            self.repo.set_data(data, size)
        except glance_store.StorageFull as e:
            msg = (_("Image storage media is full: %s") %
                   utils.exception_to_str(e))
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
        except glance_store.StorageWriteDenied as e:
            msg = (_("Insufficient permissions on image storage media: %s") %
                   utils.exception_to_str(e))
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPServiceUnavailable(explanation=msg)
        except ValueError as e:
            msg = (_("Cannot save data for image %(image_id)s: %(error)s") % {
                'image_id': self.repo.image_id,
                'error': utils.exception_to_str(e)
            })
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPBadRequest(
                explanation=utils.exception_to_str(e))
        except exception.Duplicate as e:
            msg = (_("Unable to upload duplicate image data for image"
                     "%(image_id)s: %(error)s") % {
                         'image_id': self.repo.image_id,
                         'error': utils.exception_to_str(e)
                     })
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPConflict(explanation=msg)
        except exception.Forbidden as e:
            msg = (_("Not allowed to upload image data for image %(image_id)s:"
                     " %(error)s") % {
                         'image_id': self.repo.image_id,
                         'error': utils.exception_to_str(e)
                     })
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPForbidden(explanation=msg)
        except exception.NotFound as e:
            msg = (_("Image %(image_id)s could not be found after upload."
                     " The image may have been deleted during the upload:"
                     " %(error)s") % {
                         'image_id': self.repo.image_id,
                         'error': utils.exception_to_str(e)
                     })
            _send_notification(notify_error, 'image.upload', msg)
            raise webob.exc.HTTPNotFound(explanation=utils.exception_to_str(e))
        except webob.exc.HTTPError as e:
            with excutils.save_and_reraise_exception():
                msg = (_("Failed to upload image data for image %(image_id)s"
                         " due to HTTP error: %(error)s") % {
                             'image_id': self.repo.image_id,
                             'error': utils.exception_to_str(e)
                         })
                _send_notification(notify_error, 'image.upload', msg)
        except Exception as e:
            with excutils.save_and_reraise_exception():
                msg = (_("Failed to upload image data for image %(image_id)s "
                         "due to internal error: %(error)s") % {
                             'image_id': self.repo.image_id,
                             'error': utils.exception_to_str(e)
                         })
                _send_notification(notify_error, 'image.upload', msg)
        else:
            self.send_notification('image.upload', self.repo)
            self.send_notification('image.activate', self.repo)
Beispiel #51
0
    def update(self, req, image_id, id, body=None):
        """
        Adds a membership to the image, or updates an existing one.
        If a body is present, it is a dict with the following format::

            {"member": {
                "can_share": [True|False]
            }}

        If "can_share" is provided, the member's ability to share is
        set accordingly.  If it is not provided, existing memberships
        remain unchanged and new memberships default to False.
        """
        self._check_can_access_image_members(req.context)

        # Make sure the image exists
        try:
            image = self.db_api.image_get(req.context, image_id)
        except exception.NotFound:
            msg = _("Image %(id)s not found") % {'id': image_id}
            LOG.info(msg)
            raise webob.exc.HTTPNotFound(msg)
        except exception.Forbidden:
            # If it's private and doesn't belong to them, don't let on
            # that it exists
            msg = _("Access denied to image %(id)s but returning 'not found'")
            LOG.info(msg % {'id': image_id})
            raise webob.exc.HTTPNotFound()

        # Can they manipulate the membership?
        if not self.is_image_sharable(req.context, image):
            msg = _("User lacks permission to share image %(id)s")
            LOG.info(msg % {'id': image_id})
            msg = _("No permission to share that image")
            raise webob.exc.HTTPForbidden(msg)

        # Determine the applicable can_share value
        can_share = None
        if body:
            try:
                can_share = bool(body['member']['can_share'])
            except Exception as e:
                # Malformed entity...
                msg = _("Invalid membership association specified for "
                        "image %(id)s")
                LOG.info(msg % {'id': image_id})
                msg = (_("Invalid membership association: %s") %
                       utils.exception_to_str(e))
                raise webob.exc.HTTPBadRequest(explanation=msg)

        # Look up an existing membership...
        members = self.db_api.image_member_find(req.context,
                                                image_id=image_id,
                                                member=id)
        if members:
            if can_share is not None:
                values = dict(can_share=can_share)
                self.db_api.image_member_update(req.context, members[0]['id'],
                                                values)
        else:
            values = dict(image_id=image['id'],
                          member=id,
                          can_share=bool(can_share))
            self.db_api.image_member_create(req.context, values)

        msg = _("Successfully updated a membership for image %(id)s")
        LOG.info(msg % {'id': image_id})
        return webob.exc.HTTPNoContent()
Beispiel #52
0
 def test_get_namespace_not_found(self):
     fake_namespace = "fake_namespace"
     exc = self.assertRaises(exception.NotFound, self.namespace_repo.get,
                             fake_namespace)
     self.assertIn(fake_namespace, utils.exception_to_str(exc))
Beispiel #53
0
def fail(returncode, e):
    sys.stderr.write("ERROR: %s\n" % utils.exception_to_str(e))
    sys.exit(returncode)
Beispiel #54
0
 def validate(self, obj):
     try:
         jsonschema.validate(obj, self.raw())
     except jsonschema.ValidationError as e:
         raise exception.InvalidObject(schema=self.name,
                                       reason=utils.exception_to_str(e))
Beispiel #55
0
    def upload(self, req, id, type_name, type_version, attr, size, data, index,
               **kwargs):
        artifact_repo = self.gateway.get_artifact_repo(req.context)
        artifact = None
        try:
            artifact = self._get_artifact_with_dependencies(
                artifact_repo, id, type_name, type_version)
            blob_prop = artifact.metadata.attributes.blobs.get(attr)
            if blob_prop is None:
                raise webob.exc.HTTPBadRequest(
                    explanation=_("Not a blob property '%s'") % attr)
            if isinstance(blob_prop, list):
                blob_list = getattr(artifact, attr)
                self._upload_list_property(req.method, blob_list, index, data,
                                           size)
            else:
                if index is not None:
                    raise webob.exc.HTTPBadRequest(
                        explanation=_("Not a list property '%s'") % attr)
                setattr(artifact, attr, (data, size))
            artifact_repo.save(artifact)
            return artifact

        except ValueError as e:
            LOG.debug("Cannot save data for artifact %(id)s: %(e)s", {
                'id': id,
                'e': utils.exception_to_str(e)
            })
            self._restore(artifact_repo, artifact)
            raise webob.exc.HTTPBadRequest(
                explanation=utils.exception_to_str(e))

        except glance_store.StoreAddDisabled:
            msg = _("Error in store configuration. Adding artifacts to store "
                    "is disabled.")
            LOG.exception(msg)
            self._restore(artifact_repo, artifact)
            raise webob.exc.HTTPGone(explanation=msg,
                                     request=req,
                                     content_type='text/plain')

        except (glance_store.Duplicate,
                exception.InvalidImageStatusTransition) as e:
            msg = utils.exception_to_str(e)
            LOG.exception(msg)
            raise webob.exc.HTTPConflict(explanation=e.msg, request=req)

        except exception.Forbidden as e:
            msg = ("Not allowed to upload data for artifact %s" % id)
            LOG.debug(msg)
            raise webob.exc.HTTPForbidden(explanation=msg, request=req)

        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)

        except glance_store.StorageFull as e:
            msg = _("Artifact storage media "
                    "is full: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(artifact_repo, artifact)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                      request=req)

        except exception.StorageQuotaFull as e:
            msg = _("Artifact exceeds the storage "
                    "quota: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(artifact_repo, artifact)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                      request=req)

        except exception.ImageSizeLimitExceeded as e:
            msg = _("The incoming artifact blob is "
                    "too large: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(artifact_repo, artifact)
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
                                                      request=req)

        except glance_store.StorageWriteDenied as e:
            msg = _("Insufficient permissions on artifact "
                    "storage media: %s") % utils.exception_to_str(e)
            LOG.error(msg)
            self._restore(artifact_repo, artifact)
            raise webob.exc.HTTPServiceUnavailable(explanation=msg,
                                                   request=req)

        except webob.exc.HTTPGone as e:
            with excutils.save_and_reraise_exception():
                LOG.error(
                    _LE("Failed to upload artifact blob data due to"
                        " HTTP error"))

        except webob.exc.HTTPError as e:
            with excutils.save_and_reraise_exception():
                LOG.error(
                    _LE("Failed to upload artifact blob data due to HTTP"
                        " error"))
                self._restore(artifact_repo, artifact)

        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.exception(
                    _LE("Failed to upload artifact blob data due to "
                        "internal error"))
                self._restore(artifact_repo, artifact)
Beispiel #56
0
    def show(self, req, namespace, filters=None):
        try:
            # Get namespace
            ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
            namespace_obj = ns_repo.get(namespace)
            namespace_detail = Namespace.to_wsme_model(
                namespace_obj, get_namespace_href(namespace_obj),
                self.ns_schema_link)
            ns_filters = dict()
            ns_filters['namespace'] = namespace

            # Get objects
            object_repo = self.gateway.get_metadef_object_repo(req.context)
            db_metaobject_list = object_repo.list(filters=ns_filters)
            object_list = [
                MetadefObject.to_wsme_model(
                    db_metaobject, get_object_href(namespace, db_metaobject),
                    self.obj_schema_link)
                for db_metaobject in db_metaobject_list
            ]
            if object_list:
                namespace_detail.objects = object_list

            # Get resource type associations
            rs_repo = self.gateway.get_metadef_resource_type_repo(req.context)
            db_resource_type_list = rs_repo.list(filters=ns_filters)
            resource_type_list = [
                ResourceTypeAssociation.to_wsme_model(resource_type)
                for resource_type in db_resource_type_list
            ]
            if resource_type_list:
                namespace_detail.resource_type_associations = (
                    resource_type_list)

            # Get properties
            prop_repo = self.gateway.get_metadef_property_repo(req.context)
            db_properties = prop_repo.list(filters=ns_filters)
            property_list = Namespace.to_model_properties(db_properties)
            if property_list:
                namespace_detail.properties = property_list

            if filters and filters['resource_type']:
                namespace_detail = self._prefix_property_name(
                    namespace_detail, filters['resource_type'])

            # Get tags
            tag_repo = self.gateway.get_metadef_tag_repo(req.context)
            db_metatag_list = tag_repo.list(filters=ns_filters)
            tag_list = [
                MetadefTag(**{'name': db_metatag.name})
                for db_metatag in db_metatag_list
            ]
            if tag_list:
                namespace_detail.tags = tag_list

        except exception.Forbidden as e:
            raise webob.exc.HTTPForbidden(explanation=e.msg)
        except exception.NotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.msg)
        except Exception as e:
            LOG.error(utils.exception_to_str(e))
            raise webob.exc.HTTPInternalServerError()
        return namespace_detail