"Fossies" - the Fresh Open Source Software Archive

Member "monasca-api-4.0.0/monasca_api/v2/reference/helpers.py" (13 May 2020, 26772 Bytes) of package /linux/misc/openstack/monasca-api-4.0.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 "helpers.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.1.0_vs_4.0.0.

    1 # Copyright 2015 Cray Inc. All Rights Reserved.
    2 # (C) Copyright 2014,2016-2017 Hewlett Packard Enterprise Development LP
    3 # (C) Copyright 2017 SUSE LLC
    4 # Copyright 2018 OP5 AB
    5 #
    6 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    7 # not use this file except in compliance with the License. You may obtain
    8 # a copy of the License at
    9 #
   10 # http://www.apache.org/licenses/LICENSE-2.0
   11 #
   12 # Unless required by applicable law or agreed to in writing, software
   13 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   14 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   15 # License for the specific language governing permissions and limitations
   16 # under the License.
   17 
   18 import datetime
   19 
   20 import falcon
   21 from oslo_log import log
   22 from oslo_utils import encodeutils
   23 from oslo_utils import timeutils
   24 import six
   25 import six.moves.urllib.parse as urlparse
   26 
   27 from monasca_api.common.rest import utils as rest_utils
   28 from monasca_api import conf
   29 from monasca_api.v2.common.exceptions import HTTPUnprocessableEntityError
   30 from monasca_common.validation import metrics as metric_validation
   31 
   32 
   33 LOG = log.getLogger(__name__)
   34 CONF = conf.CONF
   35 
   36 
   37 def from_json(req):
   38     """Read the json_msg from the http request body and return them as JSON.
   39 
   40     :param req: HTTP request object.
   41     :return: Returns the metrics as a JSON object.
   42     :raises falcon.HTTPBadRequest:
   43     """
   44     try:
   45         return req.media
   46     except Exception as ex:
   47         LOG.exception(ex)
   48         raise falcon.HTTPBadRequest('Bad request',
   49                                     'Request body is not valid JSON')
   50 
   51 
   52 def to_json(data):
   53     """Converts data to JSON string.
   54 
   55     :param dict data: data to be transformed to JSON
   56     :return: JSON string
   57     :rtype: str
   58     :raises: Exception
   59     """
   60     try:
   61         # NOTE(trebskit) ensure_ascii => UTF-8
   62         return rest_utils.as_json(data, ensure_ascii=False)
   63     except Exception as ex:
   64         LOG.exception(ex)
   65         raise
   66 
   67 
   68 def validate_json_content_type(req):
   69     if req.content_type not in ['application/json']:
   70         raise falcon.HTTPBadRequest('Bad request', 'Bad content type. Must be '
   71                                                    'application/json')
   72 
   73 
   74 def validate_authorization(http_request, authorized_rules_list):
   75     """Validates whether is authorized according to provided policy rules list.
   76 
   77     If authorization fails, 401 is thrown with appropriate description.
   78     Additionally response specifies 'WWW-Authenticate' header with 'Token'
   79     value challenging the client to use different token (the one with
   80     different set of roles which can access the service).
   81     """
   82 
   83     challenge = 'Token'
   84     for rule in authorized_rules_list:
   85         try:
   86             http_request.can(rule)
   87             return
   88         except Exception as ex:
   89             LOG.debug(ex)
   90 
   91     raise falcon.HTTPUnauthorized('Forbidden',
   92                                   'The request does not have access to this service',
   93                                   challenge)
   94 
   95 
   96 def validate_payload_size(content_length):
   97     """Validates payload size.
   98 
   99     Method validates payload size, this method used req.content_length to determinate
  100     payload size
  101 
  102         [service]
  103         max_log_size = 1048576
  104 
  105     **max_log_size** refers to the maximum allowed content length.
  106     If it is exceeded :py:class:`falcon.HTTPRequestEntityTooLarge` is
  107     thrown.
  108 
  109     :param  content_length: size of payload
  110 
  111     :exception: :py:class:`falcon.HTTPLengthRequired`
  112     :exception: :py:class:`falcon.HTTPRequestEntityTooLarge`
  113 
  114     """
  115     max_size = CONF.log_publisher.max_log_size
  116 
  117     LOG.debug('Payload (content-length) is %s', str(content_length))
  118 
  119     if content_length >= max_size:
  120         raise falcon.HTTPPayloadTooLarge(
  121             title='Log payload size exceeded',
  122             description='Maximum allowed size is %d bytes' % max_size
  123         )
  124 
  125 
  126 def get_x_tenant_or_tenant_id(http_request, delegate_authorized_rules_list):
  127     params = falcon.uri.parse_query_string(http_request.query_string)
  128     if 'tenant_id' in params:
  129         tenant_id = params['tenant_id']
  130 
  131         for rule in delegate_authorized_rules_list:
  132             try:
  133                 http_request.can(rule)
  134                 return tenant_id
  135             except Exception as ex:
  136                 LOG.debug(ex)
  137 
  138     return http_request.project_id
  139 
  140 
  141 def get_query_param(req, param_name, required=False, default_val=None):
  142     try:
  143         params = falcon.uri.parse_query_string(req.query_string)
  144         if param_name in params:
  145             if isinstance(params[param_name], list):
  146                 param_val = encodeutils.safe_decode(params[param_name][0], 'utf8')
  147             else:
  148                 param_val = encodeutils.safe_decode(params[param_name], 'utf8')
  149 
  150             return param_val
  151         else:
  152             if required:
  153                 raise Exception("Missing " + param_name)
  154             else:
  155                 return default_val
  156     except Exception as ex:
  157         LOG.debug(ex)
  158         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  159 
  160 
  161 def get_query_name(req, name_required=False):
  162     """Returns the query param "name" if supplied.
  163 
  164     :param req: HTTP request object.
  165     """
  166     try:
  167         params = falcon.uri.parse_query_string(req.query_string)
  168         if 'name' in params:
  169             name = params['name']
  170             return name
  171         else:
  172             if name_required:
  173                 raise Exception("Missing name")
  174             else:
  175                 return ''
  176     except Exception as ex:
  177         LOG.debug(ex)
  178         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  179 
  180 
  181 def get_query_dimensions(req, param_key='dimensions'):
  182     """Gets and parses the query param dimensions.
  183 
  184     :param req: HTTP request object.
  185     :param dimensions_param: param name for dimensions, default='dimensions'
  186     :return: Returns the dimensions as a JSON object
  187     :raises falcon.HTTPBadRequest: If dimensions are malformed.
  188     """
  189     try:
  190         params = falcon.uri.parse_query_string(req.query_string)
  191         dimensions = {}
  192         if param_key not in params:
  193             return dimensions
  194 
  195         dimensions_param = params[param_key]
  196         if isinstance(dimensions_param, six.string_types):
  197             dimensions_str_array = dimensions_param.split(',')
  198         elif isinstance(dimensions_param, list):
  199             dimensions_str_array = []
  200             for sublist in dimensions_param:
  201                 dimensions_str_array.extend(sublist.split(","))
  202         else:
  203             raise Exception("Error parsing dimensions, unknown format")
  204 
  205         for dimension in dimensions_str_array:
  206             dimension_name_value = dimension.split(':', 1)
  207             if len(dimension_name_value) == 2:
  208                 dimensions[dimension_name_value[0]] = dimension_name_value[1]
  209             elif len(dimension_name_value) == 1:
  210                 dimensions[dimension_name_value[0]] = ""
  211         return dimensions
  212     except Exception as ex:
  213         LOG.debug(ex)
  214         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  215 
  216 
  217 def get_query_starttime_timestamp(req, required=True):
  218     try:
  219         params = falcon.uri.parse_query_string(req.query_string)
  220         if 'start_time' in params:
  221             return _convert_time_string(params['start_time'])
  222         else:
  223             if required:
  224                 raise Exception("Missing start time")
  225             else:
  226                 return None
  227     except Exception as ex:
  228         LOG.debug(ex)
  229         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  230 
  231 
  232 def get_query_endtime_timestamp(req, required=True):
  233     try:
  234         params = falcon.uri.parse_query_string(req.query_string)
  235         if 'end_time' in params:
  236             return _convert_time_string(params['end_time'])
  237         else:
  238             if required:
  239                 raise Exception("Missing end time")
  240             else:
  241                 return None
  242     except Exception as ex:
  243         LOG.debug(ex)
  244         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  245 
  246 
  247 def validate_start_end_timestamps(start_timestamp, end_timestamp=None):
  248     if end_timestamp:
  249         if not start_timestamp < end_timestamp:
  250             raise falcon.HTTPBadRequest('Bad request',
  251                                         'start_time must be before end_time')
  252 
  253 
  254 def _convert_time_string(date_time_string):
  255     dt = timeutils.parse_isotime(date_time_string)
  256     dt = timeutils.normalize_time(dt)
  257     timestamp = (dt - datetime.datetime(1970, 1, 1)).total_seconds()
  258     return timestamp
  259 
  260 
  261 def get_query_statistics(req):
  262     try:
  263         params = falcon.uri.parse_query_string(req.query_string)
  264         if 'statistics' in params:
  265             statistics = []
  266             # falcon may return this as a list or as a string
  267             if isinstance(params['statistics'], list):
  268                 statistics.extend(params['statistics'])
  269             else:
  270                 statistics.extend(params['statistics'].split(','))
  271             statistics = [statistic.lower() for statistic in statistics]
  272             if not all(statistic in ['avg', 'min', 'max', 'count', 'sum'] for
  273                        statistic in statistics):
  274                 raise Exception("Invalid statistic")
  275             return statistics
  276         else:
  277             raise Exception("Missing statistics")
  278     except Exception as ex:
  279         LOG.debug(ex)
  280         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  281 
  282 
  283 def get_query_period(req):
  284     try:
  285         params = falcon.uri.parse_query_string(req.query_string)
  286         if 'period' in params:
  287             period = params['period']
  288             try:
  289                 period = int(period)
  290             except Exception:
  291                 raise Exception("Period must be a valid integer")
  292             if period < 0:
  293                 raise Exception("Period must be a positive integer")
  294             return str(period)
  295         else:
  296             return None
  297     except Exception as ex:
  298         LOG.debug(ex)
  299         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  300 
  301 
  302 def get_query_group_by(req):
  303     try:
  304         params = falcon.uri.parse_query_string(req.query_string)
  305         if 'group_by' in params:
  306             group_by = params['group_by']
  307             if not isinstance(group_by, list):
  308                 group_by = [group_by]
  309             return group_by
  310         else:
  311             return None
  312     except Exception as ex:
  313         LOG.debug(ex)
  314         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  315 
  316 
  317 def validate_query_name(name):
  318     """Validates the query param name.
  319 
  320     :param name: Query param name.
  321     :raises falcon.HTTPBadRequest: If name is not valid.
  322     """
  323     if not name:
  324         return
  325     try:
  326         metric_validation.validate_name(name)
  327     except Exception as ex:
  328         LOG.debug(ex)
  329         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  330 
  331 
  332 def validate_query_dimensions(dimensions):
  333     """Validates the query param dimensions.
  334 
  335     :param dimensions: Query param dimensions.
  336     :raises falcon.HTTPBadRequest: If dimensions are not valid.
  337     """
  338     try:
  339 
  340         for key, value in dimensions.items():
  341             if key.startswith('_'):
  342                 raise Exception("Dimension key {} may not start with '_'".format(key))
  343             metric_validation.validate_dimension_key(key)
  344             if value:
  345                 if '|' in value:
  346                     values = value.split('|')
  347                     for v in values:
  348                         metric_validation.validate_dimension_value(key, v)
  349                 else:
  350                     metric_validation.validate_dimension_value(key, value)
  351     except Exception as ex:
  352         LOG.debug(ex)
  353         raise HTTPUnprocessableEntityError('Unprocessable Entity', str(ex))
  354 
  355 
  356 def paginate(resource, uri, limit):
  357     parsed_uri = urlparse.urlparse(uri)
  358 
  359     self_link = encodeutils.safe_decode(build_base_uri(parsed_uri), 'utf8')
  360 
  361     old_query_params = _get_old_query_params(parsed_uri)
  362 
  363     if old_query_params:
  364         self_link += '?' + '&'.join(old_query_params)
  365 
  366     if resource and len(resource) > limit:
  367 
  368         if 'id' in resource[limit - 1]:
  369             new_offset = resource[limit - 1]['id']
  370 
  371         next_link = encodeutils.safe_decode(build_base_uri(parsed_uri), 'utf8')
  372 
  373         new_query_params = [u'offset' + '=' + urlparse.quote(
  374             new_offset.encode('utf8'), safe='')]
  375 
  376         _get_old_query_params_except_offset(new_query_params, parsed_uri)
  377 
  378         if new_query_params:
  379             next_link += '?' + '&'.join(new_query_params)
  380 
  381         resource = {u'links': ([{u'rel': u'self',
  382                                  u'href': self_link},
  383                                 {u'rel': u'next',
  384                                  u'href': next_link}]),
  385                     u'elements': resource[:limit]}
  386 
  387     else:
  388 
  389         resource = {u'links': ([{u'rel': u'self',
  390                                  u'href': encodeutils.safe_decode(self_link, 'utf-8')}]),
  391                     u'elements': resource}
  392 
  393     return resource
  394 
  395 
  396 def paginate_with_no_id(dictionary_list, uri, offset, limit):
  397     """This method is to paginate a list of dictionaries with no id in it.
  398        For example, metric name list, directory name list and directory
  399        value list.
  400     """
  401     parsed_uri = urlparse.urlparse(uri)
  402     self_link = encodeutils.safe_decode(build_base_uri(parsed_uri), 'utf-8')
  403     old_query_params = _get_old_query_params(parsed_uri)
  404 
  405     if old_query_params:
  406         self_link += '?' + '&'.join(old_query_params)
  407 
  408     value_list = []
  409     for item in dictionary_list:
  410         value_list.extend(item.values())
  411 
  412     if value_list:
  413         # Truncate dictionary list with offset first
  414         truncated_list_offset = _truncate_with_offset(
  415             dictionary_list, value_list, offset)
  416 
  417         # Then truncate it with limit
  418         truncated_list_offset_limit = truncated_list_offset[:limit]
  419         links = [{u'rel': u'self', u'href': self_link}]
  420         if len(truncated_list_offset) > limit:
  421             new_offset = list(truncated_list_offset_limit[limit - 1].values())[0]
  422             next_link = encodeutils.safe_decode(build_base_uri(parsed_uri), 'utf-8')
  423             new_query_params = [u'offset' + '=' + new_offset]
  424 
  425             _get_old_query_params_except_offset(new_query_params, parsed_uri)
  426 
  427             if new_query_params:
  428                 next_link += '?' + '&'.join(new_query_params)
  429 
  430             links.append({u'rel': u'next', u'href': next_link})
  431 
  432         resource = {u'links': links,
  433                     u'elements': truncated_list_offset_limit}
  434     else:
  435         resource = {u'links': ([{u'rel': u'self',
  436                                  u'href': self_link}]),
  437                     u'elements': dictionary_list}
  438 
  439     return resource
  440 
  441 
  442 def _truncate_with_offset(resource, value_list, offset):
  443     """Truncate a list of dictionaries with a given offset.
  444     """
  445     if not offset:
  446         return resource
  447 
  448     offset = offset.lower()
  449     for i, j in enumerate(value_list):
  450         # if offset matches one of the values in value_list,
  451         # the truncated list should start with the one after current offset
  452         if j == offset:
  453             return resource[i + 1:]
  454         # if offset does not exist in value_list, find the nearest
  455         # location and truncate from that location.
  456         if j > offset:
  457             return resource[i:]
  458     return []
  459 
  460 
  461 def paginate_alarming(resource, uri, limit):
  462     parsed_uri = urlparse.urlparse(uri)
  463 
  464     self_link = build_base_uri(parsed_uri)
  465 
  466     old_query_params = _get_old_query_params(parsed_uri)
  467 
  468     if old_query_params:
  469         self_link += '?' + '&'.join(old_query_params)
  470 
  471     if resource and len(resource) > limit:
  472 
  473         old_offset = 0
  474         for param in old_query_params:
  475             if param.find('offset') >= 0:
  476                 old_offset = int(param.split('=')[-1])
  477         new_offset = str(limit + old_offset)
  478 
  479         next_link = build_base_uri(parsed_uri)
  480 
  481         new_query_params = [u'offset' + '=' + urlparse.quote(
  482             new_offset.encode('utf8'), safe='')]
  483 
  484         _get_old_query_params_except_offset(new_query_params, parsed_uri)
  485 
  486         if new_query_params:
  487             next_link += '?' + '&'.join(new_query_params)
  488 
  489         resource = {u'links': ([{u'rel': u'self',
  490                                  u'href': encodeutils.safe_decode(self_link, 'utf8')},
  491                                 {u'rel': u'next',
  492                                  u'href': encodeutils.safe_decode(next_link, 'utf8')}]),
  493                     u'elements': resource[:limit]}
  494 
  495     else:
  496 
  497         resource = {u'links': ([{u'rel': u'self',
  498                                  u'href': encodeutils.safe_decode(self_link, 'utf8')}]),
  499                     u'elements': resource}
  500 
  501     return resource
  502 
  503 
  504 def paginate_dimension_values(dimvals, uri, offset, limit):
  505 
  506     parsed_uri = urlparse.urlparse(uri)
  507     self_link = build_base_uri(parsed_uri)
  508     old_query_params = _get_old_query_params(parsed_uri)
  509 
  510     if old_query_params:
  511         self_link += '?' + '&'.join(old_query_params)
  512 
  513     if (dimvals and dimvals[u'values']):
  514         have_more, truncated_values = _truncate_dimension_values(dimvals[u'values'],
  515                                                                  limit,
  516                                                                  offset)
  517 
  518         links = [{u'rel': u'self', u'href': self_link.decode('utf8')}]
  519         if have_more:
  520             new_offset = truncated_values[limit - 1]
  521             next_link = build_base_uri(parsed_uri)
  522             new_query_params = [u'offset' + '=' + urlparse.quote(
  523                 new_offset.encode('utf8'), safe='')]
  524 
  525             _get_old_query_params_except_offset(new_query_params, parsed_uri)
  526 
  527             if new_query_params:
  528                 next_link += '?' + '&'.join(new_query_params)
  529 
  530             links.append({u'rel': u'next', u'href': next_link.decode('utf8')})
  531 
  532         truncated_dimvals = {u'id': dimvals[u'id'],
  533                              u'dimension_name': dimvals[u'dimension_name'],
  534                              u'values': truncated_values}
  535         #
  536         # Only return metric name if one was provided
  537         #
  538         if u'metric_name' in dimvals:
  539             truncated_dimvals[u'metric_name'] = dimvals[u'metric_name']
  540 
  541         resource = {u'links': links,
  542                     u'elements': [truncated_dimvals]}
  543     else:
  544         resource = {u'links': ([{u'rel': u'self',
  545                                  u'href': self_link.decode('utf8')}]),
  546                     u'elements': [dimvals]}
  547 
  548     return resource
  549 
  550 
  551 def _truncate_dimension_values(values, limit, offset):
  552     if offset and offset in values:
  553         next_value_pos = values.index(offset) + 1
  554         values = values[next_value_pos:]
  555     have_more = len(values) > limit
  556     return have_more, values[:limit]
  557 
  558 
  559 def paginate_measurements(measurements, uri, limit):
  560     parsed_uri = urlparse.urlparse(uri)
  561 
  562     self_link = build_base_uri(parsed_uri)
  563     self_link = encodeutils.safe_decode(self_link, 'utf-8')
  564 
  565     old_query_params = _get_old_query_params(parsed_uri)
  566 
  567     if old_query_params:
  568         self_link += '?' + '&'.join(old_query_params)
  569 
  570     if measurements:
  571         measurement_elements = []
  572         resource = {u'links': [{u'rel': u'self',
  573                                 u'href': self_link},
  574                                ]}
  575         for measurement in measurements:
  576             if len(measurement['measurements']) >= limit:
  577 
  578                 new_offset = ('_').join([measurement['id'],
  579                                          measurement['measurements'][limit - 1][0]])
  580 
  581                 next_link = build_base_uri(parsed_uri)
  582                 next_link = encodeutils.safe_decode(next_link, 'utf-8')
  583 
  584                 new_query_params = [u'offset' + '=' + urlparse.quote(
  585                     new_offset.encode('utf8'), safe='')]
  586 
  587                 _get_old_query_params_except_offset(new_query_params, parsed_uri)
  588 
  589                 if new_query_params:
  590                     next_link += '?' + '&'.join(new_query_params)
  591 
  592                 resource[u'links'].append({u'rel': u'next',
  593                                            u'href': next_link})
  594 
  595                 truncated_measurement = {u'dimensions': measurement['dimensions'],
  596                                          u'measurements': (measurement
  597                                                            ['measurements'][:limit]),
  598                                          u'name': measurement['name'],
  599                                          u'columns': measurement['columns'],
  600                                          u'id': measurement['id']}
  601                 measurement_elements.append(truncated_measurement)
  602                 break
  603             else:
  604                 limit -= len(measurement['measurements'])
  605                 measurement_elements.append(measurement)
  606 
  607         resource[u'elements'] = measurement_elements
  608 
  609     else:
  610         resource = {u'links': ([{u'rel': u'self',
  611                                  u'href': self_link}]),
  612                     u'elements': []}
  613 
  614     return resource
  615 
  616 
  617 def _get_old_query_params(parsed_uri):
  618     old_query_params = []
  619 
  620     if parsed_uri.query:
  621 
  622         for query_param in parsed_uri.query.split('&'):
  623             query_param_name, query_param_val = query_param.split('=', 1)
  624 
  625             old_query_params.append(urlparse.quote(
  626                 query_param_name.encode('utf8'), safe='') +
  627                 "=" +
  628                 urlparse.quote(query_param_val.encode('utf8'), safe=''))
  629 
  630     return old_query_params
  631 
  632 
  633 def _get_old_query_params_except_offset(new_query_params, parsed_uri):
  634     if parsed_uri.query:
  635 
  636         for query_param in parsed_uri.query.split('&'):
  637             query_param_name, query_param_val = query_param.split('=', 1)
  638             if query_param_name.lower() != 'offset':
  639                 new_query_params.append(urlparse.quote(
  640                     query_param_name.encode(
  641                         'utf8'), safe='') + "=" + urlparse.quote(
  642                     query_param_val.encode(
  643                         'utf8'), safe=''))
  644 
  645 
  646 def paginate_statistics(statistics, uri, limit):
  647     parsed_uri = urlparse.urlparse(uri)
  648 
  649     self_link = build_base_uri(parsed_uri)
  650 
  651     old_query_params = _get_old_query_params(parsed_uri)
  652 
  653     if old_query_params:
  654         self_link += '?' + '&'.join(old_query_params)
  655 
  656     self_link = encodeutils.safe_decode(self_link, 'utf-8')
  657 
  658     if statistics:
  659         statistic_elements = []
  660         resource = {u'links': [{u'rel': u'self',
  661                                 u'href': self_link}]}
  662 
  663         for statistic in statistics:
  664             stat_id = statistic['id']
  665             if len(statistic['statistics']) >= limit:
  666 
  667                 # cassadra impl use both id and timestamp to paginate in group by
  668                 if 'end_time' in statistic:
  669                     new_offset = '_'.join([stat_id, statistic['end_time']])
  670                     del statistic['end_time']
  671                 else:
  672                     new_offset = (
  673                         statistic['statistics'][limit - 1][0])
  674 
  675                 next_link = build_base_uri(parsed_uri)
  676 
  677                 new_query_params = [u'offset' + '=' + urlparse.quote(
  678                     new_offset.encode('utf8'), safe='')]
  679 
  680                 _get_old_query_params_except_offset(new_query_params, parsed_uri)
  681 
  682                 if new_query_params:
  683                     next_link += '?' + '&'.join(new_query_params)
  684 
  685                 next_link = encodeutils.safe_decode(next_link, 'utf-8')
  686                 resource[u'links'].append({u'rel': u'next',
  687                                           u'href': next_link})
  688 
  689                 truncated_statistic = {u'dimensions': statistic['dimensions'],
  690                                        u'statistics': (statistic['statistics'][:limit]),
  691                                        u'name': statistic['name'],
  692                                        u'columns': statistic['columns'],
  693                                        u'id': statistic['id']}
  694 
  695                 statistic_elements.append(truncated_statistic)
  696                 break
  697             else:
  698                 limit -= len(statistic['statistics'])
  699                 if 'end_time' in statistic:
  700                     del statistic['end_time']
  701                 statistic_elements.append(statistic)
  702 
  703         resource[u'elements'] = statistic_elements
  704 
  705     else:
  706 
  707         resource = {u'links': ([{u'rel': u'self',
  708                                  u'href': self_link}]),
  709                     u'elements': []}
  710 
  711     return resource
  712 
  713 
  714 def create_alarms_count_next_link(uri, offset, limit):
  715     if offset is None:
  716         offset = 0
  717     parsed_url = urlparse.urlparse(uri)
  718     base_url = build_base_uri(parsed_url)
  719     new_query_params = [u'offset=' + urlparse.quote(str(offset + limit))]
  720     _get_old_query_params_except_offset(new_query_params, parsed_url)
  721 
  722     next_link = base_url
  723     if new_query_params:
  724         next_link += '?' + '&'.join(new_query_params)
  725 
  726     return next_link
  727 
  728 
  729 def build_base_uri(parsed_uri):
  730     return parsed_uri.scheme + '://' + parsed_uri.netloc + parsed_uri.path
  731 
  732 
  733 def get_link(uri, resource_id, rel='self'):
  734     """Returns a link dictionary containing href, and rel.
  735 
  736     :param uri: the http request.uri.
  737     :param resource_id: the id of the resource
  738     """
  739     parsed_uri = urlparse.urlparse(uri)
  740     href = build_base_uri(parsed_uri)
  741     href += '/' + resource_id
  742 
  743     if rel:
  744         link_dict = dict(href=href, rel=rel)
  745     else:
  746         link_dict = dict(href=href)
  747 
  748     return link_dict
  749 
  750 
  751 def add_links_to_resource(resource, uri, rel='self'):
  752     """Adds links to the given resource dictionary.
  753 
  754     :param resource: the resource dictionary you wish to add links.
  755     :param uri: the http request.uri.
  756     """
  757     resource['links'] = [get_link(uri, resource['id'], rel)]
  758     return resource
  759 
  760 
  761 def add_links_to_resource_list(resourcelist, uri):
  762     """Adds links to the given resource dictionary list.
  763 
  764     :param resourcelist: the list of resources you wish to add links.
  765     :param uri: the http request.uri.
  766     """
  767     for resource in resourcelist:
  768         add_links_to_resource(resource, uri)
  769     return resourcelist
  770 
  771 
  772 def raise_not_found_exception(resource_name, resource_id, tenant_id):
  773     """Provides exception for not found requests (update, delete, list).
  774 
  775     :param resource_name: the name of the resource.
  776     :param resource_id: id of the resource.
  777     :param tenant_id: id of the tenant
  778     """
  779     msg = 'No %s method exists for tenant_id = %s id = %s' % (
  780         resource_name, tenant_id, resource_id)
  781     raise falcon.HTTPError(
  782         status='404 Not Found',
  783         title='Not Found',
  784         description=msg,
  785         code=404)
  786 
  787 
  788 def str_2_bool(s):
  789     return s.lower() in ("true")