# glance.api.v2.metadef_namespaces

# Source code for glance.api.v2.metadef_namespaces

    # Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #    http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    # implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    import http.client as http
    import urllib.parse as urlparse

    from oslo_config import cfg
    from oslo_log import log as logging
    from oslo_serialization import jsonutils
    import webob.exc
    from wsme.rest import json

    from glance.api import policy
    from glance.api.v2.model.metadef_namespace import Namespace
    from glance.api.v2.model.metadef_namespace import Namespaces
    from glance.api.v2.model.metadef_object import MetadefObject
    from glance.api.v2.model.metadef_property_type import PropertyType
    from glance.api.v2.model.metadef_resource_type import ResourceTypeAssociation
    from glance.api.v2.model.metadef_tag import MetadefTag
    from glance.api.v2 import policy as api_policy
    from glance.common import exception
    from glance.common import utils
    from glance.common import wsgi
    from glance.common import wsme_utils
    import glance.db
    import glance.gateway
    from glance.i18n import _, _LE
    import glance.notifier
    import glance.schema

    LOG = logging.getLogger(__name__)

    CONF = cfg.CONF

    class NamespaceController(object):
        def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
            self.db_api = db_api or glance.db.get_api()
            self.policy = policy_enforcer or policy.Enforcer()
            self.notifier = notifier or glance.notifier.Notifier()
            self.gateway = glance.gateway.Gateway(db_api=self.db_api,
                                                  notifier=self.notifier,
                                                  policy_enforcer=self.policy)
            self.ns_schema_link = '/v2/schemas/metadefs/namespace'
            self.obj_schema_link = '/v2/schemas/metadefs/object'
            self.tag_schema_link = '/v2/schemas/metadefs/tag'

        def index(self, req, marker=None, limit=None, sort_key='created_at',
                  sort_dir='desc', filters=None):
            try:
                ns_repo = self.gateway.get_metadef_namespace_repo(req.context)

                policy_check = api_policy.MetadefAPIPolicy(
                    req.context,
                    enforcer=self.policy)
                # NOTE(abhishekk): This is just a "do you have permission to
                # list namespace" check. Each namespace is checked against
                # get_metadef_namespace below.
                policy_check.get_metadef_namespaces()

                # NOTE(abhishekk): We also need to fetch resource_types associated
                # with namespaces, so better to check we have permission for the
                # same in advance.
                policy_check.list_metadef_resource_types()

                # Get namespace id
                if marker:
                    namespace_obj = ns_repo.get(marker)
                    marker = namespace_obj.namespace_id

                database_ns_list = ns_repo.list(
                    marker=marker, limit=limit, sort_key=sort_key,
                    sort_dir=sort_dir, filters=filters)

                ns_list = [
                    ns for ns in database_ns_list if api_policy.MetadefAPIPolicy(
                        req.context, md_resource=ns, enforcer=self.policy).check(
                        'get_metadef_namespace')]

                rs_repo = (
                    self.gateway.get_metadef_resource_type_repo(req.context))
                for db_namespace in ns_list:
                    # Get resource type associations
                    filters = dict()
                    filters['namespace'] = db_namespace.namespace
                    try:
                        repo_rs_type_list = rs_repo.list(filters=filters)
                    except exception.NotFound:
                        # NOTE(danms): If we fail to list resource_types
                        # for this namespace, do not fail the entire
                        # namespace list operation with NotFound.
                        repo_rs_type_list = []
                    resource_type_list = [
                        ResourceTypeAssociation.to_wsme_model(
                            resource_type
                        ) for resource_type in repo_rs_type_list]

                    if resource_type_list:
                        db_namespace.resource_type_associations = (
                            resource_type_list)

                namespace_list = [Namespace.to_wsme_model(
                    db_namespace,
                    get_namespace_href(db_namespace),
                    self.ns_schema_link) for db_namespace in ns_list]
                namespaces = Namespaces()
                namespaces.namespaces = namespace_list
                if len(namespace_list) != 0 and len(namespace_list) == limit:
                    namespaces.next = ns_list[-1].namespace

            except exception.Forbidden as e:
                LOG.debug("User not permitted to retrieve metadata namespaces "
                          "index")
                raise webob.exc.HTTPForbidden(explanation=e.msg)
            except exception.NotFound as e:
                raise webob.exc.HTTPNotFound(explanation=e.msg)
            return namespaces

        @utils.mutating
        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)

                # NOTE(abhishekk): Here we are going to check if user is authorized
                # to create namespace, resource_types, objects, properties etc.
                policy_check = api_policy.MetadefAPIPolicy(
                    req.context, enforcer=self.policy)
                policy_check.add_metadef_namespace()

                if namespace.resource_type_associations:
                    policy_check.add_metadef_resource_type_association()
                if namespace.objects:
                    policy_check.add_metadef_object()
                if namespace.properties:
                    policy_check.add_metadef_property()
                if namespace.tags:
                    policy_check.add_metadef_tag()

                # NOTE(abhishekk): As we are getting rid of auth layer, this
                # is the place where we should add owner if it is not specified
                # in request.
                kwargs = namespace.to_dict()
                if 'owner' not in kwargs:
                    kwargs.update({'owner': req.context.owner})

                new_namespace = ns_factory.new_namespace(**kwargs)
                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.Invalid as e:
                msg = (_("Couldn't create metadata namespace: %s") % e)
                raise webob.exc.HTTPBadRequest(explanation=msg)
            except exception.Forbidden as e:
                self._cleanup_namespace(ns_repo, namespace, namespace_created)
                LOG.debug("User not permitted to create metadata namespace")
                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)

            # 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)

        def _to_property_dict(self, name, value):
            # Convert the model PropertyTypes dict to a JSON string
            db_property_type_dict = dict()
            db_property_type_dict['schema'] = json.tojson(PropertyType, value)
            db_property_type_dict['name'] = name
            return db_property_type_dict

        def _cleanup_namespace(self, namespace_repo, namespace, namespace_created):
            if namespace_created:
                try:
                    namespace_obj = namespace_repo.get(namespace.namespace)
                    namespace_obj.delete()
                    namespace_repo.remove(namespace_obj)
                    LOG.debug("Cleaned up namespace %(namespace)s ",
                              {'namespace': namespace.namespace})
                except Exception as e:
                    msg = (_LE("Failed to delete namespace %(namespace)s."
                               "Exception: %(exception)s"),
                           {'namespace': namespace.namespace,
                            'exception': str(e)})
                    LOG.error(msg)

        def show(self, req, namespace, filters=None):
            try:
                # Get namespace
                ns_repo = self.gateway.get_metadef_namespace_repo(
                    req.context)
                try:
                    namespace_obj = ns_repo.get(namespace)
                    policy_check = api_policy.MetadefAPIPolicy(
                        req.context,
                        md_resource=namespace_obj,
                        enforcer=self.policy)
                    policy_check.get_metadef_namespace()
                except (exception.Forbidden, webob.exc.HTTPForbidden):
                    LOG.debug("User not permitted to show namespace '%s'",
                              namespace)
                    # NOTE (abhishekk): Returning 404 Not Found as the
                    # namespace is outside of this user's project
                    raise webob.exc.HTTPNotFound()

                # NOTE(abhishekk): We also need to fetch resource_types, objects,
                # properties, tags associated with namespace, so better to check
                # whether user has permissions for the same.
                policy_check.list_metadef_resource_types()
                policy_check.get_metadef_objects()
                policy_check.get_metadef_properties()
                policy_check.get_metadef_tags()

                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)
            return namespace_detail

        def update(self, req, user_ns, namespace):
            namespace_repo = self.gateway.get_metadef_namespace_repo(req.context)
            try:
                ns_obj = namespace_repo.get(namespace)
            except (exception.Forbidden, exception.NotFound):
                # NOTE (abhishekk): Returning 404 Not Found as the
                # namespace is outside of this user's project
                msg = _("Namespace %s not found") % namespace
                raise webob.exc.HTTPNotFound(explanation=msg)

            try:
                # NOTE(abhishekk): Here we are just checking if use is authorized
                # to modify the namespace or not
                api_policy.MetadefAPIPolicy(
                    req.context,
                    md_resource=ns_obj,
                    enforcer=self.policy).modify_metadef_namespace()

                ns_obj._old_namespace = ns_obj.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.Invalid as e:
                msg = (_("Couldn't update metadata namespace: %s") % e)
                raise webob.exc.HTTPBadRequest(explanation=msg)
            except exception.Forbidden as e:
                LOG.debug("User not permitted to update 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.Duplicate as e:
                raise webob.exc.HTTPConflict(explanation=e.msg)

            return Namespace.to_wsme_model(updated_namespace,
                                           get_namespace_href(updated_namespace),
                                           self.ns_schema_link)

        def delete(self, req, namespace):
            namespace_repo = self.gateway.get_metadef_namespace_repo(req.context)
            try:
                namespace_obj = namespace_repo.get(namespace)
            except (exception.Forbidden, exception.NotFound):
                # NOTE (abhishekk): Returning 404 Not Found as the
                # namespace is outside of this user's project
                msg = _("Namespace %s not found") % namespace
                raise webob.exc.HTTPNotFound(explanation=msg)

            try:
                # NOTE(abhishekk): Here we are just checking user is authorized to
                # delete the namespace or not.
                api_policy.MetadefAPIPolicy(
                    req.context,
                    md_resource=namespace_obj,
                    enforcer=self.policy).delete_metadef_namespace()
                namespace_obj.delete()
                namespace_repo.remove(namespace_obj)
            except exception.Forbidden as e:
                LOG.debug("User not permitted to delete metadata namespace "
                          "'%s'", namespace)
                raise webob.exc.HTTPForbidden(explanation=e.msg)
            except exception.NotFound as e:
                raise webob.exc.HTTPNotFound(explanation=e.msg)

        def delete_objects(self, req, namespace):
            ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
            try:
                namespace_obj = ns_repo.get(namespace)
            except (exception.Forbidden, exception.NotFound):
                # NOTE (abhishekk): Returning 404 Not Found as the
                # namespace is outside of this user's project
                msg = _("Namespace %s not found") % namespace
                raise webob.exc.HTTPNotFound(explanation=msg)

            try:
                # NOTE(abhishekk): This call currently checks whether user
                # has permission to delete the namespace or not before deleting
                # the objects associated with it.
                api_policy.MetadefAPIPolicy(
                    req.context,
                    md_resource=namespace_obj,
                    enforcer=self.policy).delete_metadef_namespace()

                namespace_obj.delete()
                ns_repo.remove_objects(namespace_obj)
            except exception.Forbidden as e:
                LOG.debug("User not permitted to delete metadata objects "
                          "within '%s' namespace", namespace)
                raise webob.exc.HTTPForbidden(explanation=e.msg)
            except exception.NotFound as e:
                raise webob.exc.HTTPNotFound(explanation=e.msg)

        def delete_tags(self, req, namespace):
            ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
            try:
                namespace_obj = ns_repo.get(namespace)
            except (exception.Forbidden, exception.NotFound):
                # NOTE (abhishekk): Returning 404 Not Found as the
                # namespace is outside of this user's project
                msg = _("Namespace %s not found") % namespace
                raise webob.exc.HTTPNotFound(explanation=msg)

            try:
                # NOTE(abhishekk): This call currently checks whether user
                # has permission to delete the namespace or not before deleting
                # the objects associated with it.
                policy_check = api_policy.MetadefAPIPolicy(
                    req.context,
                    md_resource=namespace_obj,
                    enforcer=self.policy)
                policy_check.delete_metadef_namespace()
                # NOTE(abhishekk): This call checks whether user
                # has permission to delete the tags or not.
                policy_check.delete_metadef_tags()

                namespace_obj.delete()
                ns_repo.remove_tags(namespace_obj)
            except exception.Forbidden as e:
                LOG.debug("User not permitted to delete 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)

        def delete_properties(self, req, namespace):
            ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
            try:
                namespace_obj = ns_repo.get(namespace)
            except (exception.Forbidden, exception.NotFound):
                # NOTE (abhishekk): Returning 404 Not Found as the
                # namespace is outside of this user's project
                msg = _("Namespace %s not found") % namespace
                raise webob.exc.HTTPNotFound(explanation=msg)
            try:
                # NOTE(abhishekk): This call currently checks whether user
                # has permission to delete the namespace or not before deleting
                # the objects associated with it.
                api_policy.MetadefAPIPolicy(
                    req.context,
                    md_resource=namespace_obj,
                    enforcer=self.policy).delete_metadef_namespace()

                namespace_obj.delete()
                ns_repo.remove_properties(namespace_obj)
            except exception.Forbidden as e:
                LOG.debug("User not permitted to delete metadata properties "
                          "within '%s' namespace", namespace)
                raise webob.exc.HTTPForbidden(explanation=e.msg)
            except exception.NotFound as e:
                raise webob.exc.HTTPNotFound(explanation=e.msg)

        def _prefix_property_name(self, namespace_detail, user_resource_type):
            prefix = None
            if user_resource_type and namespace_detail.resource_type_associations:
                for resource_type in namespace_detail.resource_type_associations:
                    if resource_type.name == user_resource_type:
                        prefix = resource_type.prefix
                        break

            if prefix:
                if namespace_detail.properties:
                    new_property_dict = dict()
                    for (key, value) in namespace_detail.properties.items():
                        new_property_dict[prefix + key] = value
                    namespace_detail.properties = new_property_dict

                if namespace_detail.objects:
                    for object in namespace_detail.objects:
                        new_object_property_dict = dict()
                        for (key, value) in object.properties.items():
                            new_object_property_dict[prefix + key] = value
                        object.properties = new_object_property_dict

                        if object.required and len(object.required) > 0:
                            required = [prefix + name for name in object.required]
                            object.required = required

            return namespace_detail

    class RequestDeserializer(wsgi.JSONRequestDeserializer):
        _disallowed_properties = ['self', 'schema', 'created_at', 'updated_at']

        def __init__(self, schema=None):
            super(RequestDeserializer, self).__init__()
            self.schema = schema or get_schema()

        def _get_request_body(self, request):
            output = super(RequestDeserializer, self).default(request)
            if 'body' not in output:
                msg = _('Body expected in request.')
                raise webob.exc.HTTPBadRequest(explanation=msg)
            return output['body']

        @classmethod
        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=msg)

        def index(self, request):
            params = request.params.copy()
            limit = params.pop('limit', None)
            marker = params.pop('marker', None)
            sort_dir = params.pop('sort_dir', 'desc')

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

            query_params = {
                'sort_key': params.pop('sort_key', 'created_at'),
                'sort_dir': self._validate_sort_dir(sort_dir),
                'filters': self._get_filters(params)
            }

            if marker is not None:
                query_params['marker'] = marker

            if limit is not None:
                query_params['limit'] = self._validate_limit(limit)

            return query_params

        def _validate_sort_dir(self, sort_dir):
            if sort_dir not in ['asc', 'desc']:
                msg = _('Invalid sort direction: %s') % sort_dir
                raise webob.exc.HTTPBadRequest(explanation=msg)

            return sort_dir

        def _get_filters(self, filters):
            visibility = filters.get('visibility')
            if visibility:
                if visibility not in ['public', 'private']:
                    msg = _('Invalid visibility value: %s') % visibility
                    raise webob.exc.HTTPBadRequest(explanation=msg)

            return filters

        def _validate_limit(self, limit):
            try:
                limit = int(limit)
            except ValueError:
                msg = _("limit param must be an integer")
                raise webob.exc.HTTPBadRequest(explanation=msg)

            if limit < 0:
                msg = _("limit param must be positive")
                raise webob.exc.HTTPBadRequest(explanation=msg)

            return limit

        def show(self, request):
            params = request.params.copy()
            query_params = {
                'filters': self._get_filters(params)
            }
            return query_params

        def create(self, request):
            body = self._get_request_body(request)
            self._check_allowed(body)
            try:
                self.schema.validate(body)
            except exception.InvalidObject as e:
                raise webob.exc.HTTPBadRequest(explanation=e.msg)
            namespace = json.fromjson(Namespace, body)
            return dict(namespace=namespace)

        def update(self, request):
            body = self._get_request_body(request)
            self._check_allowed(body)
            try:
                self.schema.validate(body)
            except exception.InvalidObject as e:
                raise webob.exc.HTTPBadRequest(explanation=e.msg)
            namespace = json.fromjson(Namespace, body)
            return dict(user_ns=namespace)

    class ResponseSerializer(wsgi.JSONResponseSerializer):
        def __init__(self, schema=None):
            super(ResponseSerializer, self).__init__()
            self.schema = schema

        def create(self, response, namespace):
            ns_json = json.tojson(Namespace, namespace)
            response = self.__render(ns_json, response, http.CREATED)
            response.location = get_namespace_href(namespace)

        def show(self, response, namespace):
            ns_json = json.tojson(Namespace, namespace)
            response = self.__render(ns_json, response)

        def index(self, response, result):
            params = dict(response.request.params)
            params.pop('marker', None)
            query = urlparse.urlencode(params)
            result.first = "/v2/metadefs/namespaces"
            result.schema = "/v2/schemas/metadefs/namespaces"
            if query:
                result.first = '%s?%s' % (result.first, query)
            if result.next:
                params['marker'] = result.next
                next_query = urlparse.urlencode(params)
                result.next = '/v2/metadefs/namespaces?%s' % next_query

            ns_json = json.tojson(Namespaces, result)
            response = self.__render(ns_json, response)

        def update(self, response, namespace):
            ns_json = json.tojson(Namespace, namespace)
            response = self.__render(ns_json, response, http.OK)

        def delete(self, response, result):
            response.status_int = http.NO_CONTENT

        def delete_objects(self, response, result):
            response.status_int = http.NO_CONTENT

        def delete_properties(self, response, result):
            response.status_int = http.NO_CONTENT

        def delete_tags(self, response, result):
            response.status_int = http.NO_CONTENT

        def __render(self, json_data, response, response_status=None):
            body = jsonutils.dumps(json_data, ensure_ascii=False)
            response.unicode_body = body
            response.content_type = 'application/json'
            if response_status:
                response.status_int = response_status
            return response

    def _get_base_definitions():
        return get_schema_definitions()

    def get_schema_definitions():
        return {
            "positiveInteger": {
                "type": "integer",
                "minimum": 0
            },
            "positiveIntegerDefault0": {
                "allOf": [
                    {"$ref": "#/definitions/positiveInteger"},
                    {"default": 0}
                ]
            },
            "stringArray": {
                "type": "array",
                "items": {"type": "string"},
                # "minItems": 1,
                "uniqueItems": True
            },
            "property": {
                "type": "object",
                "additionalProperties": {
                    "type": "object",
                    "required": ["title", "type"],
                    "properties": {
                        "name": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "title": {
                            "type": "string"
                        },
                        "description": {
                            "type": "string"
                        },
                        "operators": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            }
                        },
                        "type": {
                            "type": "string",
                            "enum": [
                                "array",
                                "boolean",
                                "integer",
                                "number",
                                "object",
                                "string",
                                None
                            ]
                        },
                        "required": {
                            "$ref": "#/definitions/stringArray"
                        },
                        "minimum": {
                            "type": "number"
                        },
                        "maximum": {
                            "type": "number"
                        },
                        "maxLength": {
                            "$ref": "#/definitions/positiveInteger"
                        },
                        "minLength": {
                            "$ref": "#/definitions/positiveIntegerDefault0"
                        },
                        "pattern": {
                            "type": "string",
                            "format": "regex"
                        },
                        "enum": {
                            "type": "array"
                        },
                        "readonly": {
                            "type": "boolean"
                        },
                        "default": {},
                        "items": {
                            "type": "object",
                            "properties": {
                                "type": {
                                    "type": "string",
                                    "enum": [
                                        "array",
                                        "boolean",
                                        "integer",
                                        "number",
                                        "object",
                                        "string",
                                        None
                                    ]
                                },
                                "enum": {
                                    "type": "array"
                                }
                            }
                        },
                        "maxItems": {
                            "$ref": "#/definitions/positiveInteger"
                        },
                        "minItems": {
                            "$ref": "#/definitions/positiveIntegerDefault0"
                        },
                        "uniqueItems": {
                            "type": "boolean",
                            "default": False
                        },
                        "additionalItems": {
                            "type": "boolean"
                        },
                    }
                }
            }
        }

    def _get_base_properties():
        return {
            "namespace": {
                "type": "string",
                "description": _("The unique namespace text."),
                "maxLength": 80,
            },
            "display_name": {
                "type": "string",
                "description": _("The user friendly name for the namespace. Used "
                                 "by UI if available."),
                "maxLength": 80,
            },
            "description": {
                "type": "string",
                "description": _("Provides a user friendly description of the "
                                 "namespace."),
                "maxLength": 500,
            },
            "visibility": {
                "type": "string",
                "description": _("Scope of namespace accessibility."),
                "enum": ["public", "private"],
            },
            "protected": {
                "type": "boolean",
                "description": _("If true, namespace will not be deletable."),
            },
            "owner": {
                "type": "string",
                "description": _("Owner of the namespace."),
                "maxLength": 255,
            },
            "created_at": {
                "type": "string",
                "readOnly": True,
                "description": _("Date and time of namespace creation"),
                "format": "date-time"
            },
            "updated_at": {
                "type": "string",
                "readOnly": True,
                "description": _("Date and time of the last namespace"
                                 " modification"),
                "format": "date-time"
            },
            "schema": {
                'readOnly': True,
                "type": "string"
            },
            "self": {
                'readOnly': True,
                "type": "string"
            },
            "resource_type_associations": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "prefix": {
                            "type": "string"
                        },
                        "properties_target": {
                            "type": "string"
                        }
                    }
                }
            },
            "properties": {
                "$ref": "#/definitions/property"
            },
            "objects": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "description": {
                            "type": "string"
                        },
                        "required": {
                            "$ref": "#/definitions/stringArray"
                        },
                        "properties": {
                            "$ref": "#/definitions/property"
                        },
                    }
                }
            },
            "tags": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        }
                    }
                }
            },
        }

    def get_schema():
        properties = _get_base_properties()
        definitions = _get_base_definitions()
        mandatory_attrs = Namespace.get_mandatory_attrs()
        schema = glance.schema.Schema(
            'namespace',
            properties,
            required=mandatory_attrs,
            definitions=definitions
        )
        return schema

    def get_collection_schema():
        namespace_schema = get_schema()
        return glance.schema.CollectionSchema('namespaces', namespace_schema)

    def get_namespace_href(namespace):
        base_href = '/v2/metadefs/namespaces/%s' % namespace.namespace
        return base_href

    def get_object_href(namespace_name, metadef_object):
        base_href = ('/v2/metadefs/namespaces/%s/objects/%s' %
                     (namespace_name, metadef_object.name))
        return base_href

    def get_tag_href(namespace_name, metadef_tag):
        base_href = ('/v2/metadefs/namespaces/%s/tags/%s' %
                     (namespace_name, metadef_tag.name))
        return base_href

    def create_resource():
        """Namespaces resource factory method"""
        schema = get_schema()
        deserializer = RequestDeserializer(schema)
        serializer = ResponseSerializer(schema)
        controller = NamespaceController()
        return wsgi.Resource(controller, deserializer, serializer)

this page last updated: 2026-03-18 20:42:57

Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.

found an error? report a bug