# Copyright 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. from six.moves.urllib import parse from oslo_config import cfg from designate.objects.adapters import base from designate.objects import base as ovoobj_base from designate import exceptions cfg.CONF.import_opt('api_base_uri', 'designate.api', group='service:api') cfg.CONF.import_opt('enable_host_header', 'designate.api', group='service:api') class APIv2Adapter(base.DesignateAdapter): BASE_URI = cfg.CONF['service:api'].api_base_uri.rstrip('/') ADAPTER_FORMAT = 'API_v2' ##################### # Rendering methods # ##################### @classmethod def _render_list(cls, list_object, *args, **kwargs): r_list = super(APIv2Adapter, cls)._render_list( list_object, *args, **kwargs) if cls.MODIFICATIONS['options'].get('links', True)\ and 'request' in kwargs: r_list['links'] = cls._get_collection_links( list_object, kwargs['request']) # Check if we should include metadata if isinstance(list_object, ovoobj_base.PagedListObjectMixin): metadata = {} if list_object.total_count is not None: metadata['total_count'] = list_object.total_count r_list['metadata'] = metadata return r_list @classmethod def _render_object(cls, object, *args, **kwargs): obj = super(APIv2Adapter, cls)._render_object(object, *args, **kwargs) if cls.MODIFICATIONS['options'].get('links', True)\ and 'request' in kwargs: obj['links'] = cls._get_resource_links(object, kwargs['request']) return obj ##################### # Parsing methods # ##################### @classmethod def parse(cls, values, output_object, *args, **kwargs): return super(APIv2Adapter, cls).parse( cls.ADAPTER_FORMAT, values, output_object, *args, **kwargs) ##################### # Link methods # ##################### @classmethod def _get_base_url(cls, request): if cfg.CONF['service:api'].enable_host_header: try: return request.host_url except Exception: return cls.BASE_URI else: return cls.BASE_URI @classmethod def _get_resource_links(cls, obj, request): base_uri = cls._get_base_url(request) path = cls._get_path(request, obj) return {'self': '%s%s/%s' % (base_uri, path, obj.id)} @classmethod def _get_path(cls, request, *args): path = request.path.lstrip('/').split('/') item_path = '' for part in path: if part == cls.MODIFICATIONS['options']['collection_name']: item_path += '/' + part return item_path else: item_path += '/' + part @classmethod def _get_collection_links(cls, item_list, request): links = { 'self': cls._get_collection_href(request) } params = request.GET # defined in etc/designate/designate.conf.sample limit = cfg.CONF['service:api'].default_limit_v2 if 'limit' in params: limit = params['limit'] if limit.lower() == 'max': limit = cfg.CONF['service:api'].max_limit_v2 else: try: limit = int(limit) except ValueError: raise exceptions.ValueError( "'limit' should be an integer or 'max'") # Bug: this creates a link to "next" even on the last page if # len(item_list) happens to be == limit if limit is not None and limit == len(item_list): links['next'] = cls._get_next_href(request, item_list) return links @classmethod def _get_collection_href(cls, request, extra_params=None): params = request.GET if extra_params is not None: params.update(extra_params) base_uri = cls._get_base_url(request) href = "%s%s?%s" % ( base_uri, cls._get_path(request), parse.urlencode(params)) return href.rstrip('?') @classmethod def _get_next_href(cls, request, items): # Prepare the extra params extra_params = { 'marker': items[-1]['id'] } return cls._get_collection_href(request, extra_params)