"Fossies" - the Fresh Open Source Software Archive

Member "glance-24.1.0/glance/api/v2/metadef_objects.py" (8 Jun 2022, 18627 Bytes) of package /linux/misc/openstack/glance-24.1.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "metadef_objects.py" see the Fossies "Dox" file reference documentation.

    1 # Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License");
    4 # you may not use this file except in compliance with the License.
    5 # You may obtain a copy of the License at
    6 #
    7 #    http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS,
   11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   12 # implied.
   13 # See the License for the specific language governing permissions and
   14 # limitations under the License.
   15 
   16 import http.client as http
   17 
   18 from oslo_log import log as logging
   19 from oslo_serialization import jsonutils
   20 from oslo_utils import encodeutils
   21 import webob.exc
   22 from wsme.rest import json
   23 
   24 from glance.api import policy
   25 from glance.api.v2 import metadef_namespaces as namespaces
   26 import glance.api.v2.metadef_properties as properties
   27 from glance.api.v2.model.metadef_object import MetadefObject
   28 from glance.api.v2.model.metadef_object import MetadefObjects
   29 from glance.api.v2 import policy as api_policy
   30 from glance.common import exception
   31 from glance.common import wsgi
   32 from glance.common import wsme_utils
   33 import glance.db
   34 from glance.i18n import _
   35 import glance.notifier
   36 import glance.schema
   37 
   38 LOG = logging.getLogger(__name__)
   39 
   40 
   41 class MetadefObjectsController(object):
   42     def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
   43         self.db_api = db_api or glance.db.get_api()
   44         self.policy = policy_enforcer or policy.Enforcer()
   45         self.notifier = notifier or glance.notifier.Notifier()
   46         self.gateway = glance.gateway.Gateway(db_api=self.db_api,
   47                                               notifier=self.notifier,
   48                                               policy_enforcer=self.policy)
   49         self.obj_schema_link = '/v2/schemas/metadefs/object'
   50 
   51     def create(self, req, metadata_object, namespace):
   52         object_factory = self.gateway.get_metadef_object_factory(
   53             req.context, authorization_layer=False)
   54         object_repo = self.gateway.get_metadef_object_repo(
   55             req.context, authorization_layer=False)
   56         try:
   57             ns_repo = self.gateway.get_metadef_namespace_repo(
   58                 req.context, authorization_layer=False)
   59             try:
   60                 # NOTE(abhishekk): Verifying that namespace is visible
   61                 # to user
   62                 namespace_obj = ns_repo.get(namespace)
   63             except exception.Forbidden:
   64                 # NOTE (abhishekk): Returning 404 Not Found as the
   65                 # namespace is outside of this user's project
   66                 msg = _("Namespace %s not found") % namespace
   67                 raise exception.NotFound(msg)
   68 
   69             # NOTE(abhishekk): Metadef object is created for Metadef namespaces
   70             # Here we are just checking if user is authorized to create metadef
   71             # object or not.
   72             api_policy.MetadefAPIPolicy(
   73                 req.context,
   74                 md_resource=namespace_obj,
   75                 enforcer=self.policy).add_metadef_object()
   76 
   77             new_meta_object = object_factory.new_object(
   78                 namespace=namespace,
   79                 **metadata_object.to_dict())
   80             object_repo.add(new_meta_object)
   81 
   82         except exception.Forbidden as e:
   83             LOG.debug("User not permitted to create metadata object within "
   84                       "'%s' namespace", namespace)
   85             raise webob.exc.HTTPForbidden(explanation=e.msg)
   86         except exception.Invalid as e:
   87             msg = (_("Couldn't create metadata object: %s")
   88                    % encodeutils.exception_to_unicode(e))
   89             raise webob.exc.HTTPBadRequest(explanation=msg)
   90         except exception.NotFound as e:
   91             raise webob.exc.HTTPNotFound(explanation=e.msg)
   92         except exception.Duplicate as e:
   93             raise webob.exc.HTTPConflict(explanation=e.msg)
   94         return MetadefObject.to_wsme_model(
   95             new_meta_object,
   96             get_object_href(namespace, new_meta_object),
   97             self.obj_schema_link)
   98 
   99     def index(self, req, namespace, marker=None, limit=None,
  100               sort_key='created_at', sort_dir='desc', filters=None):
  101         try:
  102             ns_repo = self.gateway.get_metadef_namespace_repo(
  103                 req.context, authorization_layer=False)
  104             try:
  105                 namespace_obj = ns_repo.get(namespace)
  106             except exception.Forbidden:
  107                 # NOTE (abhishekk): Returning 404 Not Found as the
  108                 # namespace is outside of this user's project
  109                 msg = _("Namespace %s not found") % namespace
  110                 raise exception.NotFound(msg)
  111 
  112             # NOTE(abhishekk): This is just a "do you have permission to
  113             # list objects" check. Each object is checked against
  114             # get_metadef_object below.
  115             api_policy.MetadefAPIPolicy(
  116                 req.context,
  117                 md_resource=namespace_obj,
  118                 enforcer=self.policy).get_metadef_objects()
  119 
  120             filters = filters or dict()
  121             filters['namespace'] = namespace
  122             object_repo = self.gateway.get_metadef_object_repo(
  123                 req.context, authorization_layer=False)
  124 
  125             db_metaobject_list = object_repo.list(
  126                 marker=marker, limit=limit, sort_key=sort_key,
  127                 sort_dir=sort_dir, filters=filters)
  128 
  129             object_list = [
  130                 MetadefObject.to_wsme_model(
  131                     obj, get_object_href(namespace, obj),
  132                     self.obj_schema_link
  133                 ) for obj in db_metaobject_list if api_policy.MetadefAPIPolicy(
  134                     req.context, md_resource=obj.namespace,
  135                     enforcer=self.policy
  136                 ).check('get_metadef_object')]
  137 
  138             metadef_objects = MetadefObjects()
  139             metadef_objects.objects = object_list
  140         except exception.Forbidden as e:
  141             LOG.debug("User not permitted to retrieve metadata objects within "
  142                       "'%s' namespace", namespace)
  143             raise webob.exc.HTTPForbidden(explanation=e.msg)
  144         except exception.NotFound as e:
  145             raise webob.exc.HTTPNotFound(explanation=e.msg)
  146         return metadef_objects
  147 
  148     def show(self, req, namespace, object_name):
  149         meta_object_repo = self.gateway.get_metadef_object_repo(
  150             req.context, authorization_layer=False)
  151         try:
  152             ns_repo = self.gateway.get_metadef_namespace_repo(
  153                 req.context, authorization_layer=False)
  154             try:
  155                 namespace_obj = ns_repo.get(namespace)
  156             except exception.Forbidden:
  157                 # NOTE (abhishekk): Returning 404 Not Found as the
  158                 # namespace is outside of this user's project
  159                 msg = _("Namespace %s not found") % namespace
  160                 raise exception.NotFound(msg)
  161 
  162             # NOTE(abhishekk): Metadef objects are associated with
  163             # namespace, so made provision to pass namespace here
  164             # for visibility check
  165             api_policy.MetadefAPIPolicy(
  166                 req.context,
  167                 md_resource=namespace_obj,
  168                 enforcer=self.policy).get_metadef_object()
  169 
  170             metadef_object = meta_object_repo.get(namespace,
  171                                                   object_name)
  172             return MetadefObject.to_wsme_model(
  173                 metadef_object,
  174                 get_object_href(namespace, metadef_object),
  175                 self.obj_schema_link)
  176         except exception.Forbidden as e:
  177             LOG.debug("User not permitted to show metadata object '%s' "
  178                       "within '%s' namespace", namespace, object_name)
  179             raise webob.exc.HTTPForbidden(explanation=e.msg)
  180         except exception.NotFound as e:
  181             raise webob.exc.HTTPNotFound(explanation=e.msg)
  182 
  183     def update(self, req, metadata_object, namespace, object_name):
  184         meta_repo = self.gateway.get_metadef_object_repo(
  185             req.context, authorization_layer=False)
  186         try:
  187             ns_repo = self.gateway.get_metadef_namespace_repo(
  188                 req.context, authorization_layer=False)
  189             try:
  190                 # NOTE(abhishekk): Verifying that namespace is visible
  191                 # to user
  192                 namespace_obj = ns_repo.get(namespace)
  193             except exception.Forbidden:
  194                 # NOTE (abhishekk): Returning 404 Not Found as the
  195                 # namespace is outside of this user's project
  196                 msg = _("Namespace %s not found") % namespace
  197                 raise exception.NotFound(msg)
  198 
  199             # NOTE(abhishekk): Metadef object is created for Metadef namespaces
  200             # Here we are just checking if user is authorized to modify metadef
  201             # object or not.
  202             api_policy.MetadefAPIPolicy(
  203                 req.context,
  204                 md_resource=namespace_obj,
  205                 enforcer=self.policy).modify_metadef_object()
  206 
  207             metadef_object = meta_repo.get(namespace, object_name)
  208             metadef_object._old_name = metadef_object.name
  209             metadef_object.name = wsme_utils._get_value(
  210                 metadata_object.name)
  211             metadef_object.description = wsme_utils._get_value(
  212                 metadata_object.description)
  213             metadef_object.required = wsme_utils._get_value(
  214                 metadata_object.required)
  215             metadef_object.properties = wsme_utils._get_value(
  216                 metadata_object.properties)
  217             updated_metadata_obj = meta_repo.save(metadef_object)
  218         except exception.Invalid as e:
  219             msg = (_("Couldn't update metadata object: %s")
  220                    % encodeutils.exception_to_unicode(e))
  221             raise webob.exc.HTTPBadRequest(explanation=msg)
  222         except exception.Forbidden as e:
  223             LOG.debug("User not permitted to update metadata object '%s' "
  224                       "within '%s' namespace ", object_name, namespace)
  225             raise webob.exc.HTTPForbidden(explanation=e.msg)
  226         except exception.NotFound as e:
  227             raise webob.exc.HTTPNotFound(explanation=e.msg)
  228         except exception.Duplicate as e:
  229             raise webob.exc.HTTPConflict(explanation=e.msg)
  230         return MetadefObject.to_wsme_model(
  231             updated_metadata_obj,
  232             get_object_href(namespace, updated_metadata_obj),
  233             self.obj_schema_link)
  234 
  235     def delete(self, req, namespace, object_name):
  236         meta_repo = self.gateway.get_metadef_object_repo(
  237             req.context, authorization_layer=False)
  238         try:
  239             ns_repo = self.gateway.get_metadef_namespace_repo(
  240                 req.context, authorization_layer=False)
  241             try:
  242                 # NOTE(abhishekk): Verifying that namespace is visible
  243                 # to user
  244                 namespace_obj = ns_repo.get(namespace)
  245             except exception.Forbidden:
  246                 # NOTE (abhishekk): Returning 404 Not Found as the
  247                 # namespace is outside of this user's project
  248                 msg = _("Namespace %s not found") % namespace
  249                 raise exception.NotFound(msg)
  250 
  251             # NOTE(abhishekk): Metadef object is created for Metadef namespaces
  252             # Here we are just checking if user is authorized to delete metadef
  253             # object or not.
  254             api_policy.MetadefAPIPolicy(
  255                 req.context,
  256                 md_resource=namespace_obj,
  257                 enforcer=self.policy).delete_metadef_object()
  258 
  259             metadef_object = meta_repo.get(namespace, object_name)
  260             metadef_object.delete()
  261             meta_repo.remove(metadef_object)
  262         except exception.Forbidden as e:
  263             LOG.debug("User not permitted to delete metadata object '%s' "
  264                       "within '%s' namespace", object_name, namespace)
  265             raise webob.exc.HTTPForbidden(explanation=e.msg)
  266         except exception.NotFound as e:
  267             raise webob.exc.HTTPNotFound(explanation=e.msg)
  268 
  269 
  270 def _get_base_definitions():
  271     return namespaces.get_schema_definitions()
  272 
  273 
  274 def _get_base_properties():
  275     return {
  276         "name": {
  277             "type": "string",
  278             "maxLength": 80
  279         },
  280         "description": {
  281             "type": "string"
  282         },
  283         "required": {
  284             "$ref": "#/definitions/stringArray"
  285         },
  286         "properties": {
  287             "$ref": "#/definitions/property"
  288         },
  289         "schema": {
  290             'readOnly': True,
  291             "type": "string"
  292         },
  293         "self": {
  294             'readOnly': True,
  295             "type": "string"
  296         },
  297         "created_at": {
  298             "type": "string",
  299             "readOnly": True,
  300             "description": _("Date and time of object creation"),
  301             "format": "date-time"
  302         },
  303         "updated_at": {
  304             "type": "string",
  305             "readOnly": True,
  306             "description": _("Date and time of the last object modification"),
  307             "format": "date-time"
  308         }
  309     }
  310 
  311 
  312 def get_schema():
  313     definitions = _get_base_definitions()
  314     properties = _get_base_properties()
  315     mandatory_attrs = MetadefObject.get_mandatory_attrs()
  316     schema = glance.schema.Schema(
  317         'object',
  318         properties,
  319         required=mandatory_attrs,
  320         definitions=definitions,
  321     )
  322     return schema
  323 
  324 
  325 def get_collection_schema():
  326     object_schema = get_schema()
  327     return glance.schema.CollectionSchema('objects', object_schema)
  328 
  329 
  330 class RequestDeserializer(wsgi.JSONRequestDeserializer):
  331     _disallowed_properties = ['self', 'schema', 'created_at', 'updated_at']
  332 
  333     def __init__(self, schema=None):
  334         super(RequestDeserializer, self).__init__()
  335         self.schema = schema or get_schema()
  336 
  337     def _get_request_body(self, request):
  338         output = super(RequestDeserializer, self).default(request)
  339         if 'body' not in output:
  340             msg = _('Body expected in request.')
  341             raise webob.exc.HTTPBadRequest(explanation=msg)
  342         return output['body']
  343 
  344     def create(self, request):
  345         body = self._get_request_body(request)
  346         self._check_allowed(body)
  347         try:
  348             self.schema.validate(body)
  349             if 'properties' in body:
  350                 for propertyname in body['properties']:
  351                     schema = properties.get_schema(require_name=False)
  352                     schema.validate(body['properties'][propertyname])
  353         except exception.InvalidObject as e:
  354             raise webob.exc.HTTPBadRequest(explanation=e.msg)
  355         metadata_object = json.fromjson(MetadefObject, body)
  356         return dict(metadata_object=metadata_object)
  357 
  358     def update(self, request):
  359         body = self._get_request_body(request)
  360         self._check_allowed(body)
  361         try:
  362             self.schema.validate(body)
  363         except exception.InvalidObject as e:
  364             raise webob.exc.HTTPBadRequest(explanation=e.msg)
  365         metadata_object = json.fromjson(MetadefObject, body)
  366         return dict(metadata_object=metadata_object)
  367 
  368     def index(self, request):
  369         params = request.params.copy()
  370         limit = params.pop('limit', None)
  371         marker = params.pop('marker', None)
  372         sort_dir = params.pop('sort_dir', 'desc')
  373 
  374         query_params = {
  375             'sort_key': params.pop('sort_key', 'created_at'),
  376             'sort_dir': self._validate_sort_dir(sort_dir),
  377             'filters': self._get_filters(params)
  378         }
  379 
  380         if marker is not None:
  381             query_params['marker'] = marker
  382 
  383         if limit is not None:
  384             query_params['limit'] = self._validate_limit(limit)
  385 
  386         return query_params
  387 
  388     def _validate_sort_dir(self, sort_dir):
  389         if sort_dir not in ['asc', 'desc']:
  390             msg = _('Invalid sort direction: %s') % sort_dir
  391             raise webob.exc.HTTPBadRequest(explanation=msg)
  392 
  393         return sort_dir
  394 
  395     def _get_filters(self, filters):
  396         visibility = filters.get('visibility')
  397         if visibility:
  398             if visibility not in ['public', 'private', 'shared']:
  399                 msg = _('Invalid visibility value: %s') % visibility
  400                 raise webob.exc.HTTPBadRequest(explanation=msg)
  401 
  402         return filters
  403 
  404     def _validate_limit(self, limit):
  405         try:
  406             limit = int(limit)
  407         except ValueError:
  408             msg = _("limit param must be an integer")
  409             raise webob.exc.HTTPBadRequest(explanation=msg)
  410 
  411         if limit <= 0:
  412             msg = _("limit param must be positive")
  413             raise webob.exc.HTTPBadRequest(explanation=msg)
  414 
  415         return limit
  416 
  417     @classmethod
  418     def _check_allowed(cls, image):
  419         for key in cls._disallowed_properties:
  420             if key in image:
  421                 msg = _("Attribute '%s' is read-only.") % key
  422                 raise webob.exc.HTTPForbidden(explanation=msg)
  423 
  424 
  425 class ResponseSerializer(wsgi.JSONResponseSerializer):
  426     def __init__(self, schema=None):
  427         super(ResponseSerializer, self).__init__()
  428         self.schema = schema or get_schema()
  429 
  430     def create(self, response, metadata_object):
  431         response.status_int = http.CREATED
  432         self.show(response, metadata_object)
  433 
  434     def show(self, response, metadata_object):
  435         metadata_object_json = json.tojson(MetadefObject, metadata_object)
  436         body = jsonutils.dumps(metadata_object_json, ensure_ascii=False)
  437         response.unicode_body = body
  438         response.content_type = 'application/json'
  439 
  440     def update(self, response, metadata_object):
  441         response.status_int = http.OK
  442         self.show(response, metadata_object)
  443 
  444     def index(self, response, result):
  445         result.schema = "v2/schemas/metadefs/objects"
  446         metadata_objects_json = json.tojson(MetadefObjects, result)
  447         body = jsonutils.dumps(metadata_objects_json, ensure_ascii=False)
  448         response.unicode_body = body
  449         response.content_type = 'application/json'
  450 
  451     def delete(self, response, result):
  452         response.status_int = http.NO_CONTENT
  453 
  454 
  455 def get_object_href(namespace_name, metadef_object):
  456     base_href = ('/v2/metadefs/namespaces/%s/objects/%s' %
  457                  (namespace_name, metadef_object.name))
  458     return base_href
  459 
  460 
  461 def create_resource():
  462     """Metadef objects resource factory method"""
  463     schema = get_schema()
  464     deserializer = RequestDeserializer(schema)
  465     serializer = ResponseSerializer(schema)
  466     controller = MetadefObjectsController()
  467     return wsgi.Resource(controller, deserializer, serializer)