Exemplo n.º 1
0
    def _get_floating_resources(self, query, metaquery, resource):
        """Return an iterable of models.Resource instances unconstrained
           by timestamp.

        :param query: project/user/source query
        :param metaquery: dict with metadata to match on.
        :param resource: resource filter.
        """
        if resource is not None:
            query['_id'] = resource

        query.update(dict((k, v)
                          for (k, v) in metaquery.iteritems()))

        keys = base._handle_sort_key('resource')
        sort_keys = ['last_sample_timestamp' if i == 'timestamp' else i
                     for i in keys]
        sort_instructions = self._build_sort_instructions(sort_keys)[0]

        for r in self.db.resource.find(query, sort=sort_instructions):
            yield models.Resource(
                resource_id=r['_id'],
                user_id=r['user_id'],
                project_id=r['project_id'],
                first_sample_timestamp=r.get('first_sample_timestamp',
                                             self._GENESIS),
                last_sample_timestamp=r.get('last_sample_timestamp',
                                            self._APOCALYPSE),
                source=r['source'],
                metadata=r['metadata'])
Exemplo n.º 2
0
    def get_resources(self, user=None, project=None, source=None,
                      start_timestamp=None, end_timestamp=None,
                      metaquery={}, resource=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param end_timestamp: Optional modified timestamp end range.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        """
        q = {}
        if user is not None:
            q['user_id'] = user
        if project is not None:
            q['project_id'] = project
        if source is not None:
            q['source'] = source
        if resource is not None:
            q['resource_id'] = resource
        # Add resource_ prefix so it matches the field in the db
        q.update(dict(('resource_' + k, v)
                      for (k, v) in metaquery.iteritems()))

        # FIXME(dhellmann): This may not perform very well,
        # but doing any better will require changing the database
        # schema and that will need more thought than I have time
        # to put into it today.
        if start_timestamp or end_timestamp:
            # Look for resources matching the above criteria and with
            # samples in the time range we care about, then change the
            # resource query to return just those resources by id.
            ts_range = make_timestamp_range(start_timestamp, end_timestamp)
            if ts_range:
                q['timestamp'] = ts_range

        # FIXME(jd): We should use self.db.meter.group() and not use the
        # resource collection, but that's not supported by MIM, so it's not
        # easily testable yet. Since it was bugged before anyway, it's still
        # better for now.
        resource_ids = self.db.meter.find(q).distinct('resource_id')
        q = {'_id': {'$in': resource_ids}}
        for resource in self.db.resource.find(q):
            yield models.Resource(
                resource_id=resource['_id'],
                project_id=resource['project_id'],
                user_id=resource['user_id'],
                metadata=resource['metadata'],
                meter=[
                    models.ResourceMeter(
                        counter_name=meter['counter_name'],
                        counter_type=meter['counter_type'],
                        counter_unit=meter['counter_unit'],
                    )
                    for meter in resource['meter']
                ],
            )
    def get_resources(user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery={},
                      resource=None):
        """Return an iterable of api_models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optonal start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        """
        session = sqlalchemy_session.get_session()
        query = session.query(Meter, ).group_by(Meter.resource_id)
        if user is not None:
            query = query.filter(Meter.user_id == user)
        if source is not None:
            query = query.filter(Meter.sources.any(id=source))
        if start_timestamp:
            if start_timestamp_op == 'gt':
                query = query.filter(Meter.timestamp > start_timestamp)
            else:
                query = query.filter(Meter.timestamp >= start_timestamp)
        if end_timestamp:
            if end_timestamp_op == 'le':
                query = query.filter(Meter.timestamp <= end_timestamp)
            else:
                query = query.filter(Meter.timestamp < end_timestamp)
        if project is not None:
            query = query.filter(Meter.project_id == project)
        if resource is not None:
            query = query.filter(Meter.resource_id == resource)
        if metaquery:
            raise NotImplementedError('metaquery not implemented')

        for meter in query.all():
            yield api_models.Resource(
                resource_id=meter.resource_id,
                project_id=meter.project_id,
                source=meter.sources[0].id,
                user_id=meter.user_id,
                metadata=meter.resource_metadata,
                meter=[
                    api_models.ResourceMeter(
                        counter_name=m.counter_name,
                        counter_type=m.counter_type,
                        counter_unit=m.counter_unit,
                    ) for m in meter.resource.meters
                ],
            )
Exemplo n.º 4
0
    def get_resources(self, user=None, project=None, source=None,
                      start_timestamp=None, start_timestamp_op=None,
                      end_timestamp=None, end_timestamp_op=None,
                      metaquery={}, resource=None, pagination=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like ge, gt.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """
        if pagination:
            raise NotImplementedError('Pagination not implemented')

        sample_filter = storage.SampleFilter(
            user=user, project=project,
            start=start_timestamp, start_timestamp_op=start_timestamp_op,
            end=end_timestamp, end_timestamp_op=end_timestamp_op,
            resource=resource, source=source, metaquery=metaquery)
        q, start_row, stop_row = make_sample_query_from_filter(
            sample_filter, require_meter=False)
        with self.conn_pool.connection() as conn:
            meter_table = conn.table(self.METER_TABLE)
            LOG.debug(_("Query Meter table: %s") % q)
            meters = meter_table.scan(filter=q, row_start=start_row,
                                      row_stop=stop_row)
            d_meters = []
            for i, m in meters:
                d_meters.append(deserialize_entry(m))

            # We have to sort on resource_id before we can group by it.
            # According to the itertools documentation a new group is
            # generated when the value of the key function changes
            # (it breaks there).
            meters = sorted(d_meters, key=_resource_id_from_record_tuple)
            for resource_id, r_meters in itertools.groupby(
                    meters, key=_resource_id_from_record_tuple):
                # We need deserialized entry(data[0]), sources (data[1]) and
                # metadata(data[3])
                meter_rows = [(data[0], data[1], data[3]) for data in sorted(
                    r_meters, key=_timestamp_from_record_tuple)]
                latest_data = meter_rows[-1]
                min_ts = meter_rows[0][0]['timestamp']
                max_ts = latest_data[0]['timestamp']
                yield models.Resource(
                    resource_id=resource_id,
                    first_sample_timestamp=min_ts,
                    last_sample_timestamp=max_ts,
                    project_id=latest_data[0]['project_id'],
                    source=latest_data[1][0],
                    user_id=latest_data[0]['user_id'],
                    metadata=latest_data[2],
                )
Exemplo n.º 5
0
    def get_resources(self, user=None, project=None, source=None,
                      start_timestamp=None, start_timestamp_op=None,
                      end_timestamp=None, end_timestamp_op=None,
                      metaquery=None, resource=None, limit=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like ge, gt.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param limit: Maximum number of results to return.
        """
        if limit == 0:
            return
        q = hbase_utils.make_query(metaquery=metaquery, user_id=user,
                                   project_id=project,
                                   resource_id=resource, source=source)
        q = hbase_utils.make_meter_query_for_resource(start_timestamp,
                                                      start_timestamp_op,
                                                      end_timestamp,
                                                      end_timestamp_op,
                                                      source, q)
        with self.conn_pool.connection() as conn:
            resource_table = conn.table(self.RESOURCE_TABLE)
            LOG.debug("Query Resource table: %s", q)
            for resource_id, data in resource_table.scan(filter=q,
                                                         limit=limit):
                f_res, sources, meters, md = hbase_utils.deserialize_entry(
                    data)
                resource_id = hbase_utils.encode_unicode(resource_id)
                # Unfortunately happybase doesn't keep ordered result from
                # HBase. So that's why it's needed to find min and max
                # manually
                first_ts = min(meters, key=operator.itemgetter(1))[1]
                last_ts = max(meters, key=operator.itemgetter(1))[1]
                source = meters[0][0][1]
                # If we use QualifierFilter then HBase returnes only
                # qualifiers filtered by. It will not return the whole entry.
                # That's why if we need to ask additional qualifiers manually.
                if 'project_id' not in f_res and 'user_id' not in f_res:
                    row = resource_table.row(
                        resource_id, columns=['f:project_id', 'f:user_id',
                                              'f:resource_metadata'])
                    f_res, _s, _m, md = hbase_utils.deserialize_entry(row)
                yield models.Resource(
                    resource_id=resource_id,
                    first_sample_timestamp=first_ts,
                    last_sample_timestamp=last_ts,
                    project_id=f_res['project_id'],
                    source=source,
                    user_id=f_res['user_id'],
                    metadata=md)
Exemplo n.º 6
0
def search_results_to_resources(results):
    """Transforms results of the search to the Resource instances."""

    for record in results['hits']['hits']:
        yield models.Resource(
            resource_id=record['_id'],
            first_sample_timestamp=utils.sanitize_timestamp(
                record['_source'].get('first_sample_timestamp')),
            last_sample_timestamp=utils.sanitize_timestamp(
                record['_source']['last_sample_timestamp']),
            source=record['_source'].get('source'),
            project_id=record['_source'].get('project_id'),
            user_id=record['_source'].get('user_id'),
            metadata=record['_source'].get('metadata', {}))
Exemplo n.º 7
0
        def make_resource(data, first_ts, last_ts):
            """Transform HBase fields to Resource model."""
            # convert HBase metadata e.g. f:r_display_name to display_name
            data['f:metadata'] = _metadata_from_document(data)

            return models.Resource(
                resource_id=data['f:resource_id'],
                first_sample_timestamp=first_ts,
                last_sample_timestamp=last_ts,
                project_id=data['f:project_id'],
                source=data['f:source'],
                user_id=data['f:user_id'],
                metadata=data['f:metadata'],
            )
Exemplo n.º 8
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      end_timestamp=None,
                      metaquery={}):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param end_timestamp: Optional modified timestamp end range.
        """
        q, start_row, end_row = make_query(user=user,
                                           project=project,
                                           source=source,
                                           start=start_timestamp,
                                           end=end_timestamp,
                                           require_meter=False)
        LOG.debug("q: %s" % q)
        # TODO implement metaquery support
        if len(metaquery) > 0:
            raise NotImplementedError('metaquery not implemented')

        resource_ids = {}
        g = self.meter.scan(filter=q, row_start=start_row, row_stop=end_row)
        for ignored, data in g:
            resource_ids[data['f:resource_id']] = data['f:resource_id']

        q = make_query(user=user,
                       project=project,
                       source=source,
                       query_only=True,
                       require_meter=False)
        LOG.debug("q: %s" % q)
        for resource_id, data in self.resource.rows(resource_ids):
            yield models.Resource(
                resource_id=resource_id,
                project_id=data['f:project_id'],
                user_id=data['f:user_id'],
                metadata=json.loads(data['f:metadata']),
                meter=[
                    models.ResourceMeter(*(m[4:].split("!"))) for m in data
                    if m.startswith('f:m_')
                ],
            )
Exemplo n.º 9
0
        def make_resource(data):
            """Transform HBase fields to Resource model."""
            # convert HBase metadata e.g. f:r_display_name to display_name
            data['f:metadata'] = dict((k[4:], v) for k, v in data.iteritems()
                                      if k.startswith('f:r_'))

            return models.Resource(
                resource_id=data['f:resource_id'],
                project_id=data['f:project_id'],
                source=data['f:source'],
                user_id=data['f:user_id'],
                metadata=data['f:metadata'],
                meter=[
                    models.ResourceMeter(*(m[4:].split("!"))) for m in data
                    if m.startswith('f:m_')
                ],
            )
Exemplo n.º 10
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery=None,
                      resource=None,
                      pagination=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """
        if pagination:
            raise ceilometer.NotImplementedError('Pagination not implemented')

        metaquery = metaquery or {}

        q = {}
        if user is not None:
            q['user_id'] = user
        if project is not None:
            q['project_id'] = project
        if source is not None:
            q['source'] = source
        if resource is not None:
            q['resource_id'] = resource
        # Add resource_ prefix so it matches the field in the db
        q.update(
            dict(('resource_' + k, v) for (k, v) in six.iteritems(metaquery)))

        if start_timestamp or end_timestamp:
            # Look for resources matching the above criteria and with
            # samples in the time range we care about, then change the
            # resource query to return just those resources by id.
            ts_range = pymongo_utils.make_timestamp_range(
                start_timestamp, end_timestamp, start_timestamp_op,
                end_timestamp_op)
            if ts_range:
                q['timestamp'] = ts_range

        sort_keys = base._handle_sort_key('resource', 'timestamp')
        sort_keys.insert(0, 'resource_id')
        sort_instructions = self._build_sort_instructions(sort_keys=sort_keys,
                                                          sort_dir='desc')
        resource = lambda x: x['resource_id']
        meters = self.db.meter.find(q, sort=sort_instructions)
        for resource_id, r_meters in itertools.groupby(meters, key=resource):
            # Because we have to know first/last timestamp, and we need a full
            # list of references to the resource's meters, we need a tuple
            # here.
            r_meters = tuple(r_meters)
            latest_meter = r_meters[0]
            last_ts = latest_meter['timestamp']
            first_ts = r_meters[-1]['timestamp']

            yield models.Resource(resource_id=latest_meter['resource_id'],
                                  project_id=latest_meter['project_id'],
                                  first_sample_timestamp=first_ts,
                                  last_sample_timestamp=last_ts,
                                  source=latest_meter['source'],
                                  user_id=latest_meter['user_id'],
                                  metadata=latest_meter['resource_metadata'])
Exemplo n.º 11
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery=None,
                      resource=None):
        """Return an iterable of api_models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        """
        s_filter = storage.SampleFilter(user=user,
                                        project=project,
                                        source=source,
                                        start_timestamp=start_timestamp,
                                        start_timestamp_op=start_timestamp_op,
                                        end_timestamp=end_timestamp,
                                        end_timestamp_op=end_timestamp_op,
                                        metaquery=metaquery,
                                        resource=resource)

        session = self._engine_facade.get_session()
        # get list of resource_ids
        res_q = session.query(distinct(models.Resource.resource_id)).join(
            models.Sample,
            models.Sample.resource_id == models.Resource.internal_id)
        res_q = make_query_from_filter(session,
                                       res_q,
                                       s_filter,
                                       require_meter=False)

        for res_id in res_q.all():

            # get max and min sample timestamp value
            min_max_q = (session.query(
                func.max(models.Sample.timestamp).label('max_timestamp'),
                func.min(models.Sample.timestamp).label('min_timestamp')).join(
                    models.Resource, models.Resource.internal_id ==
                    models.Sample.resource_id).filter(
                        models.Resource.resource_id == res_id[0]))

            min_max_q = make_query_from_filter(session,
                                               min_max_q,
                                               s_filter,
                                               require_meter=False)

            min_max = min_max_q.first()

            # get resource details for latest sample
            res_q = (session.query(
                models.Resource.resource_id, models.Resource.user_id,
                models.Resource.project_id, models.Resource.source_id,
                models.Resource.resource_metadata).join(
                    models.Sample,
                    models.Sample.resource_id == models.Resource.internal_id
                ).filter(
                    models.Sample.timestamp == min_max.max_timestamp).filter(
                        models.Resource.resource_id == res_id[0]).order_by(
                            models.Sample.id.desc()).limit(1))

            res = res_q.first()

            yield api_models.Resource(
                resource_id=res.resource_id,
                project_id=res.project_id,
                first_sample_timestamp=min_max.min_timestamp,
                last_sample_timestamp=min_max.max_timestamp,
                source=res.source_id,
                user_id=res.user_id,
                metadata=res.resource_metadata)
Exemplo n.º 12
0
    def _get_time_constrained_resources(self, query,
                                        start_timestamp, start_timestamp_op,
                                        end_timestamp, end_timestamp_op,
                                        metaquery, resource):
        """Return an iterable of models.Resource instances constrained
           by sample timestamp.

        :param query: project/user/source query
        :param start_timestamp: modified timestamp start range.
        :param start_timestamp_op: start time operator, like gt, ge.
        :param end_timestamp: modified timestamp end range.
        :param end_timestamp_op: end time operator, like lt, le.
        :param metaquery: dict with metadata to match on.
        :param resource: resource filter.
        """
        if resource is not None:
            query['resource_id'] = resource

        # Add resource_ prefix so it matches the field in the db
        query.update(dict(('resource_' + k, v)
                          for (k, v) in metaquery.iteritems()))

        # FIXME(dhellmann): This may not perform very well,
        # but doing any better will require changing the database
        # schema and that will need more thought than I have time
        # to put into it today.
        # Look for resources matching the above criteria and with
        # samples in the time range we care about, then change the
        # resource query to return just those resources by id.
        ts_range = pymongo_base.make_timestamp_range(start_timestamp,
                                                     end_timestamp,
                                                     start_timestamp_op,
                                                     end_timestamp_op)
        if ts_range:
            query['timestamp'] = ts_range

        sort_keys = base._handle_sort_key('resource')
        sort_instructions = self._build_sort_instructions(sort_keys)[0]

        # use a unique collection name for the results collection,
        # as result post-sorting (as oppposed to reduce pre-sorting)
        # is not possible on an inline M-R
        out = 'resource_list_%s' % uuid.uuid4()
        self.db.meter.map_reduce(self.MAP_RESOURCES,
                                 self.REDUCE_RESOURCES,
                                 out=out,
                                 sort={'resource_id': 1},
                                 query=query)

        try:
            for r in self.db[out].find(sort=sort_instructions):
                resource = r['value']
                yield models.Resource(
                    resource_id=r['_id'],
                    user_id=resource['user_id'],
                    project_id=resource['project_id'],
                    first_sample_timestamp=resource['first_timestamp'],
                    last_sample_timestamp=resource['last_timestamp'],
                    source=resource['source'],
                    metadata=resource['metadata'])
        finally:
            self.db[out].drop()
Exemplo n.º 13
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery={},
                      resource=None,
                      limit=None,
                      marker_pairs=None,
                      sort_key=None,
                      sort_dir=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param limit: Number of documents should be returned.
        :param marker_pairs: Attribute-value pairs to identify the last item of
                            the previous page.
        :param sort_key: Attribute by which results be sorted.
        :param sort_dir: Direction with which results be sorted(asc, desc).
        """

        if marker_pairs:
            raise NotImplementedError(
                "Cannot use marker pairs in resource listing, not implemented")

        q = {}
        if user is not None:
            q['user_id'] = user
        if project is not None:
            q['project_id'] = project
        if source is not None:
            q['source'] = source
        if resource is not None:
            q['resource_id'] = resource
        # Add resource_ prefix so it matches the field in the db
        q.update(dict(
            ('resource_' + k, v) for (k, v) in metaquery.iteritems()))

        # FIXME(dhellmann): This may not perform very well,
        # but doing any better will require changing the database
        # schema and that will need more thought than I have time
        # to put into it today.
        if start_timestamp or end_timestamp:
            # Look for resources matching the above criteria and with
            # samples in the time range we care about, then change the
            # resource query to return just those resources by id.
            ts_range = make_timestamp_range(start_timestamp, end_timestamp,
                                            start_timestamp_op,
                                            end_timestamp_op)
            if ts_range:
                q['timestamp'] = ts_range

        sort_keys = base._handle_sort_key('resource', sort_key)
        sort_instruction, query = self._build_paginate_query(
            None, sort_keys, sort_dir)
        q.update(query)

        aggregate = self.db.meter.aggregate([
            {
                "$match": q
            },
            {
                "$sort": dict(sort_instruction)
            },
            {
                "$group": {
                    "_id": "$resource_id",
                    "user_id": {
                        "$first": "$user_id"
                    },
                    "project_id": {
                        "$first": "$project_id"
                    },
                    "source": {
                        "$first": "$source"
                    },
                    "first_sample_timestamp": {
                        "$min": "$timestamp"
                    },
                    "last_sample_timestamp": {
                        "$max": "$timestamp"
                    },
                    "metadata": {
                        "$first": "$resource_metadata"
                    },
                    "meters_name": {
                        "$push": "$counter_name"
                    },
                    "meters_type": {
                        "$push": "$counter_type"
                    },
                    "meters_unit": {
                        "$push": "$counter_unit"
                    },
                }
            },
        ])

        for result in aggregate['result']:
            if limit is not None:
                if limit == 0:
                    break
                limit -= 1
            yield models.Resource(
                resource_id=result['_id'],
                user_id=result['user_id'],
                project_id=result['project_id'],
                first_sample_timestamp=result['first_sample_timestamp'],
                last_sample_timestamp=result['last_sample_timestamp'],
                source=result['source'],
                metadata=result['metadata'],
                meter=[
                    models.ResourceMeter(
                        counter_name=m_n,
                        counter_type=m_t,
                        counter_unit=m_u,
                    ) for m_n, m_u, m_t in
                    zip(result['meters_name'], result['meters_unit'],
                        result['meters_type'])
                ],
            )
Exemplo n.º 14
0
    def get_resources(user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery={},
                      resource=None,
                      pagination=None):
        """Return an iterable of api_models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optonal start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """

        # We probably want to raise these early, since we don't know from here
        # if they will be handled. We don't want extra wait or work for it to
        # just fail.
        if pagination:
            raise NotImplementedError(_('Pagination not implemented'))

        # (thomasm) We need to get the max timestamp first, since that's the
        # most accurate. We also need to filter down in the subquery to
        # constrain what we have to JOIN on later.
        session = sqlalchemy_session.get_session()

        ts_subquery = session.query(
            Meter.resource_id,
            func.max(Meter.timestamp).label("max_ts"),
            func.min(Meter.timestamp).label("min_ts")).group_by(
                Meter.resource_id)

        # Here are the basic 'eq' operation filters for the sample data.
        for column, value in [(Meter.resource_id, resource),
                              (Meter.user_id, user),
                              (Meter.project_id, project)]:
            if value:
                ts_subquery = ts_subquery.filter(column == value)

        if source:
            ts_subquery = ts_subquery.filter(Meter.sources.any(id=source))

        if metaquery:
            ts_subquery = apply_metaquery_filter(session, ts_subquery,
                                                 metaquery)

        # Here we limit the samples being used to a specific time period,
        # if requested.
        if start_timestamp:
            if start_timestamp_op == 'gt':
                ts_subquery = ts_subquery.filter(
                    Meter.timestamp > start_timestamp)
            else:
                ts_subquery = ts_subquery.filter(
                    Meter.timestamp >= start_timestamp)
        if end_timestamp:
            if end_timestamp_op == 'le':
                ts_subquery = ts_subquery.filter(
                    Meter.timestamp <= end_timestamp)
            else:
                ts_subquery = ts_subquery.filter(
                    Meter.timestamp < end_timestamp)
        ts_subquery = ts_subquery.subquery()

        # Now we need to get the max Meter.id out of the leftover results, to
        # break any ties.
        agg_subquery = session.query(
            func.max(Meter.id).label("max_id"), ts_subquery).filter(
                Meter.resource_id == ts_subquery.c.resource_id,
                Meter.timestamp == ts_subquery.c.max_ts).group_by(
                    ts_subquery.c.resource_id, ts_subquery.c.max_ts,
                    ts_subquery.c.min_ts).subquery()

        query = session.query(
            Meter, agg_subquery.c.min_ts,
            agg_subquery.c.max_ts).filter(Meter.id == agg_subquery.c.max_id)

        for meter, first_ts, last_ts in query.all():
            yield api_models.Resource(
                resource_id=meter.resource_id,
                project_id=meter.project_id,
                first_sample_timestamp=first_ts,
                last_sample_timestamp=last_ts,
                source=meter.sources[0].id,
                user_id=meter.user_id,
                metadata=meter.resource_metadata,
                meter=[
                    api_models.ResourceMeter(
                        counter_name=m.counter_name,
                        counter_type=m.counter_type,
                        counter_unit=m.counter_unit,
                    ) for m in meter.resource.meters
                ],
            )
Exemplo n.º 15
0
    def get_resources(self, user=None, project=None, source=None,
                      start_timestamp=None, start_timestamp_op=None,
                      end_timestamp=None, end_timestamp_op=None,
                      metaquery={}, resource=None, limit=None,
                      marker_pairs=None, sort_key=None, sort_dir=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param limit: Number of documents should be returned.
        :param marker_pairs: Attribute-value pairs to identify the last item of
                            the previous page.
        :param sort_key: Attribute by which results be sorted.
        :param sort_dir: Direction with which results be sorted(asc, desc).
        """
        q = {}
        if user is not None:
            q['user_id'] = user
        if project is not None:
            q['project_id'] = project
        if source is not None:
            q['source'] = source
        if resource is not None:
            q['resource_id'] = resource
        # Add resource_ prefix so it matches the field in the db
        q.update(dict(('resource_' + k, v)
                      for (k, v) in metaquery.iteritems()))

        # FIXME(dhellmann): This may not perform very well,
        # but doing any better will require changing the database
        # schema and that will need more thought than I have time
        # to put into it today.
        if start_timestamp or end_timestamp:
            # Look for resources matching the above criteria and with
            # samples in the time range we care about, then change the
            # resource query to return just those resources by id.
            ts_range = make_timestamp_range(start_timestamp, end_timestamp,
                                            start_timestamp_op,
                                            end_timestamp_op)
            if ts_range:
                q['timestamp'] = ts_range

        marker = self._get_marker(self.db.resource, marker_pairs=marker_pairs)
        sort_keys = base._handle_sort_key('resource', sort_key)

        # FIXME(jd): We should use self.db.meter.group() and not use the
        # resource collection, but that's not supported by MIM, so it's not
        # easily testable yet. Since it was bugged before anyway, it's still
        # better for now.
        resource_ids = self.db.meter.find(q).distinct('resource_id')
        q = {'_id': {'$in': resource_ids}}
        for resource in self.paginate_query(q, self.db.resource, limit=limit,
                                            marker=marker, sort_keys=sort_keys,
                                            sort_dir=sort_dir):
            yield models.Resource(
                resource_id=resource['_id'],
                project_id=resource['project_id'],
                source=resource['source'],
                user_id=resource['user_id'],
                metadata=resource['metadata'],
                meter=[
                    models.ResourceMeter(
                        counter_name=meter['counter_name'],
                        counter_type=meter['counter_type'],
                        counter_unit=meter.get('counter_unit', ''),
                    )
                    for meter in resource['meter']
                ],
            )
Exemplo n.º 16
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery=None,
                      resource=None,
                      pagination=None):
        """Return an iterable of api_models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """
        if pagination:
            raise ceilometer.NotImplementedError('Pagination not implemented')

        s_filter = storage.SampleFilter(user=user,
                                        project=project,
                                        source=source,
                                        start=start_timestamp,
                                        start_timestamp_op=start_timestamp_op,
                                        end=end_timestamp,
                                        end_timestamp_op=end_timestamp_op,
                                        metaquery=metaquery,
                                        resource=resource)

        session = self._engine_facade.get_session()
        # get list of resource_ids
        res_q = session.query(distinct(models.Resource.resource_id)).join(
            models.Sample,
            models.Sample.resource_id == models.Resource.internal_id)
        res_q = make_query_from_filter(session,
                                       res_q,
                                       s_filter,
                                       require_meter=False)

        for res_id in res_q.all():
            # get latest Sample
            max_q = (session.query(models.Sample).join(
                models.Resource, models.Resource.internal_id == models.Sample.
                resource_id).filter(models.Resource.resource_id == res_id[0]))
            max_q = make_query_from_filter(session,
                                           max_q,
                                           s_filter,
                                           require_meter=False)
            max_q = max_q.order_by(models.Sample.timestamp.desc(),
                                   models.Sample.id.desc()).limit(1)

            # get the min timestamp value.
            min_q = (session.query(models.Sample.timestamp).join(
                models.Resource, models.Resource.internal_id == models.Sample.
                resource_id).filter(models.Resource.resource_id == res_id[0]))
            min_q = make_query_from_filter(session,
                                           min_q,
                                           s_filter,
                                           require_meter=False)
            min_q = min_q.order_by(models.Sample.timestamp.asc()).limit(1)

            sample = max_q.first()
            if sample:
                yield api_models.Resource(
                    resource_id=sample.resource.resource_id,
                    project_id=sample.resource.project_id,
                    first_sample_timestamp=min_q.first().timestamp,
                    last_sample_timestamp=sample.timestamp,
                    source=sample.resource.source_id,
                    user_id=sample.resource.user_id,
                    metadata=sample.resource.resource_metadata)
Exemplo n.º 17
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery={},
                      resource=None,
                      pagination=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """
        if pagination:
            raise NotImplementedError(_('Pagination not implemented'))

        q = {}
        if user is not None:
            q['user_id'] = user
        if project is not None:
            q['project_id'] = project
        if source is not None:
            q['source'] = source
        if resource is not None:
            q['resource_id'] = resource
        # Add resource_ prefix so it matches the field in the db
        q.update(dict(
            ('resource_' + k, v) for (k, v) in metaquery.iteritems()))

        # FIXME(dhellmann): This may not perform very well,
        # but doing any better will require changing the database
        # schema and that will need more thought than I have time
        # to put into it today.
        if start_timestamp or end_timestamp:
            # Look for resources matching the above criteria and with
            # samples in the time range we care about, then change the
            # resource query to return just those resources by id.
            ts_range = make_timestamp_range(start_timestamp, end_timestamp,
                                            start_timestamp_op,
                                            end_timestamp_op)
            if ts_range:
                q['timestamp'] = ts_range

        sort_keys = base._handle_sort_key('resource')
        sort_instructions = self._build_sort_instructions(sort_keys)[0]

        # use a unique collection name for the results collection,
        # as result post-sorting (as oppposed to reduce pre-sorting)
        # is not possible on an inline M-R
        out = 'resource_list_%s' % uuid.uuid4()
        self.db.meter.map_reduce(self.MAP_RESOURCES,
                                 self.REDUCE_RESOURCES,
                                 out=out,
                                 sort={'resource_id': 1},
                                 query=q)

        try:
            for r in self.db[out].find(sort=sort_instructions):
                resource = r['value']
                yield models.Resource(
                    resource_id=r['_id'],
                    user_id=resource['user_id'],
                    project_id=resource['project_id'],
                    first_sample_timestamp=resource['first_timestamp'],
                    last_sample_timestamp=resource['last_timestamp'],
                    source=resource['source'],
                    metadata=resource['metadata'])
        finally:
            self.db[out].drop()
Exemplo n.º 18
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery={},
                      resource=None,
                      pagination=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """

        if pagination:
            raise NotImplementedError(_('Pagination not implemented'))

        q = {}
        if user is not None:
            q['user_id'] = user
        if project is not None:
            q['project_id'] = project
        if source is not None:
            q['source'] = source
        if resource is not None:
            q['resource_id'] = resource
        # Add resource_ prefix so it matches the field in the db
        q.update(dict(
            ('resource_' + k, v) for (k, v) in metaquery.iteritems()))

        if start_timestamp or end_timestamp:
            # Look for resources matching the above criteria and with
            # samples in the time range we care about, then change the
            # resource query to return just those resources by id.
            ts_range = make_timestamp_range(start_timestamp, end_timestamp,
                                            start_timestamp_op,
                                            end_timestamp_op)
            if ts_range:
                q['timestamp'] = ts_range

        # FIXME(jd): We should use self.db.meter.group() and not use the
        # resource collection, but that's not supported by MIM, so it's not
        # easily testable yet. Since it was bugged before anyway, it's still
        # better for now.
        resource_ids = self.db.meter.find(q).distinct('resource_id')
        q = {'_id': {'$in': resource_ids}}
        for resource in self.db.resource.find(q):
            yield models.Resource(
                resource_id=resource['_id'],
                project_id=resource['project_id'],
                first_sample_timestamp=None,
                last_sample_timestamp=None,
                source=resource['source'],
                user_id=resource['user_id'],
                metadata=resource['metadata'],
                meter=[
                    models.ResourceMeter(
                        counter_name=meter['counter_name'],
                        counter_type=meter['counter_type'],
                        counter_unit=meter.get('counter_unit', ''),
                    ) for meter in resource['meter']
                ],
            )
Exemplo n.º 19
0
    def get_resources(self, user=None, project=None, source=None,
                      start_timestamp=None, start_timestamp_op=None,
                      end_timestamp=None, end_timestamp_op=None,
                      metaquery=None, resource=None, limit=None):
        """Return an iterable of dictionaries containing resource information.

        { 'resource_id': UUID of the resource,
          'project_id': UUID of project owning the resource,
          'user_id': UUID of user owning the resource,
          'timestamp': UTC datetime of last update to the resource,
          'metadata': most current metadata for the resource,
          'meter': list of the meters reporting data for the resource,
          }

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param limit: Maximum number of results to return.
        """
        if limit == 0:
            return

        q = {}
        if metaquery:
            q = self._convert_metaquery(metaquery)

        if start_timestamp_op and start_timestamp_op != 'ge':
            raise ceilometer.NotImplementedError(('Start time op %s '
                                                  'not implemented') %
                                                 start_timestamp_op)

        if end_timestamp_op and end_timestamp_op != 'le':
            raise ceilometer.NotImplementedError(('End time op %s '
                                                  'not implemented') %
                                                 end_timestamp_op)

        if not start_timestamp:
            start_timestamp = timeutils.isotime(datetime.datetime(1970, 1, 1))
        else:
            start_timestamp = timeutils.isotime(start_timestamp)

        if end_timestamp:
            end_timestamp = timeutils.isotime(end_timestamp)

        dims_filter = dict(user_id=user,
                           project_id=project,
                           source=source,
                           resource_id=resource
                           )
        dims_filter = {k: v for k, v in dims_filter.items() if v is not None}

        _search_args = dict(
            start_time=start_timestamp,
            end_time=end_timestamp,
            limit=1)

        _search_args = {k: v for k, v in _search_args.items()
                        if v is not None}

        result_count = 0
        for metric in self.mc.metrics_list(
                **dict(dimensions=dims_filter)):
            _search_args['name'] = metric['name']
            _search_args['dimensions'] = metric['dimensions']
            try:
                for sample in self.mc.measurements_list(**_search_args):
                    d = sample['dimensions']
                    m = self._convert_to_dict(
                        sample['measurements'][0], sample['columns'])
                    vm = m['value_meta']
                    if not self._match_metaquery_to_value_meta(q, vm):
                        continue
                    if d.get('resource_id'):
                        result_count += 1

                        yield api_models.Resource(
                            resource_id=d.get('resource_id'),
                            first_sample_timestamp=(
                                timeutils.parse_isotime(m['timestamp'])),
                            last_sample_timestamp=timeutils.utcnow(),
                            project_id=d.get('project_id'),
                            source=d.get('source'),
                            user_id=d.get('user_id'),
                            metadata=m['value_meta']
                        )

                        if result_count == limit:
                            return

            except monasca_exc.HTTPConflict:
                pass
Exemplo n.º 20
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery=None,
                      resource=None,
                      pagination=None):
        """Return an iterable of api_models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """
        if pagination:
            raise NotImplementedError('Pagination not implemented')

        metaquery = metaquery or {}

        def _apply_filters(query):
            # TODO(gordc) this should be merged with make_query_from_filter
            for column, value in [(models.Sample.resource_id, resource),
                                  (models.Sample.user_id, user),
                                  (models.Sample.project_id, project),
                                  (models.Sample.source_id, source)]:
                if value:
                    query = query.filter(column == value)
            if metaquery:
                query = apply_metaquery_filter(session, query, metaquery)
            if start_timestamp:
                if start_timestamp_op == 'gt':
                    query = query.filter(
                        models.Sample.timestamp > start_timestamp)
                else:
                    query = query.filter(
                        models.Sample.timestamp >= start_timestamp)
            if end_timestamp:
                if end_timestamp_op == 'le':
                    query = query.filter(
                        models.Sample.timestamp <= end_timestamp)
                else:
                    query = query.filter(
                        models.Sample.timestamp < end_timestamp)
            return query

        session = self._engine_facade.get_session()
        # get list of resource_ids
        res_q = session.query(distinct(models.Sample.resource_id))
        res_q = _apply_filters(res_q)

        for res_id in res_q.all():
            # get latest Sample
            max_q = session.query(models.Sample)\
                .filter(models.Sample.resource_id == res_id[0])
            max_q = _apply_filters(max_q)
            max_q = max_q.order_by(models.Sample.timestamp.desc(),
                                   models.Sample.id.desc()).limit(1)

            # get the min timestamp value.
            min_q = session.query(models.Sample.timestamp)\
                .filter(models.Sample.resource_id == res_id[0])
            min_q = _apply_filters(min_q)
            min_q = min_q.order_by(models.Sample.timestamp.asc()).limit(1)

            sample = max_q.first()
            if sample:
                yield api_models.Resource(
                    resource_id=sample.resource_id,
                    project_id=sample.project_id,
                    first_sample_timestamp=min_q.first().timestamp,
                    last_sample_timestamp=sample.timestamp,
                    source=sample.source_id,
                    user_id=sample.user_id,
                    metadata=sample.resource_metadata)
Exemplo n.º 21
0
    def get_resources(self,
                      user=None,
                      project=None,
                      source=None,
                      start_timestamp=None,
                      start_timestamp_op=None,
                      end_timestamp=None,
                      end_timestamp_op=None,
                      metaquery=None,
                      resource=None,
                      limit=None):
        """Return an iterable of api_models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param limit: Maximum number of results to return.
        """
        if limit == 0:
            return
        s_filter = storage.SampleFilter(user=user,
                                        project=project,
                                        source=source,
                                        start_timestamp=start_timestamp,
                                        start_timestamp_op=start_timestamp_op,
                                        end_timestamp=end_timestamp,
                                        end_timestamp_op=end_timestamp_op,
                                        metaquery=metaquery,
                                        resource=resource)

        session = self._engine_facade.get_session()
        # get list of resource_ids
        has_timestamp = start_timestamp or end_timestamp
        # NOTE: When sql_expire_samples_only is enabled, there will be some
        #       resources without any sample, in such case we should use inner
        #       join on sample table to avoid wrong result.
        if cfg.CONF.sql_expire_samples_only or has_timestamp:
            res_q = session.query(distinct(models.Resource.resource_id)).join(
                models.Sample,
                models.Sample.resource_id == models.Resource.internal_id)
        else:
            res_q = session.query(distinct(models.Resource.resource_id))
        res_q = make_query_from_filter(session,
                                       res_q,
                                       s_filter,
                                       require_meter=False)
        res_q = res_q.limit(limit) if limit else res_q
        for res_id in res_q.all():

            # get max and min sample timestamp value
            min_max_q = (session.query(
                func.max(models.Sample.timestamp).label('max_timestamp'),
                func.min(models.Sample.timestamp).label('min_timestamp')).join(
                    models.Resource, models.Resource.internal_id ==
                    models.Sample.resource_id).filter(
                        models.Resource.resource_id == res_id[0]))

            min_max_q = make_query_from_filter(session,
                                               min_max_q,
                                               s_filter,
                                               require_meter=False)

            min_max = min_max_q.first()

            # get resource details for latest sample
            res_q = (session.query(
                models.Resource.resource_id, models.Resource.user_id,
                models.Resource.project_id, models.Resource.source_id,
                models.Resource.resource_metadata).join(
                    models.Sample,
                    models.Sample.resource_id == models.Resource.internal_id
                ).filter(
                    models.Sample.timestamp == min_max.max_timestamp).filter(
                        models.Resource.resource_id == res_id[0]).order_by(
                            models.Sample.id.desc()).limit(1))

            res = res_q.first()

            yield api_models.Resource(
                resource_id=res.resource_id,
                project_id=res.project_id,
                first_sample_timestamp=min_max.min_timestamp,
                last_sample_timestamp=min_max.max_timestamp,
                source=res.source_id,
                user_id=res.user_id,
                metadata=res.resource_metadata)
Exemplo n.º 22
0
    def get_resources(self, user=None, project=None, source=None,
                      start_timestamp=None, start_timestamp_op=None,
                      end_timestamp=None, end_timestamp_op=None,
                      metaquery={}, resource=None, pagination=None):
        """Return an iterable of models.Resource instances

        :param user: Optional ID for user that owns the resource.
        :param project: Optional ID for project that owns the resource.
        :param source: Optional source filter.
        :param start_timestamp: Optional modified timestamp start range.
        :param start_timestamp_op: Optional start time operator, like gt, ge.
        :param end_timestamp: Optional modified timestamp end range.
        :param end_timestamp_op: Optional end time operator, like lt, le.
        :param metaquery: Optional dict with metadata to match on.
        :param resource: Optional resource filter.
        :param pagination: Optional pagination query.
        """
        if pagination:
            raise NotImplementedError(_('Pagination not implemented'))

        q = {}
        if user is not None:
            q['user_id'] = user
        if project is not None:
            q['project_id'] = project
        if source is not None:
            q['source'] = source
        if resource is not None:
            q['resource_id'] = resource
        # Add resource_ prefix so it matches the field in the db
        q.update(dict(('resource_' + k, v)
                      for (k, v) in metaquery.iteritems()))

        # FIXME(dhellmann): This may not perform very well,
        # but doing any better will require changing the database
        # schema and that will need more thought than I have time
        # to put into it today.
        if start_timestamp or end_timestamp:
            # Look for resources matching the above criteria and with
            # samples in the time range we care about, then change the
            # resource query to return just those resources by id.
            ts_range = make_timestamp_range(start_timestamp, end_timestamp,
                                            start_timestamp_op,
                                            end_timestamp_op)
            if ts_range:
                q['timestamp'] = ts_range

        sort_keys = base._handle_sort_key('resource')
        sort_instructions = self._build_sort_instructions(sort_keys)[0]

        aggregate = self.db.meter.aggregate([
            {"$match": q},
            {"$sort": dict(sort_instructions)},
            {"$group": {
                "_id": "$resource_id",
                "user_id": {"$first": "$user_id"},
                "project_id": {"$first": "$project_id"},
                "source": {"$first": "$source"},
                "first_sample_timestamp": {"$min": "$timestamp"},
                "last_sample_timestamp": {"$max": "$timestamp"},
                "metadata": {"$first": "$resource_metadata"},
                "meters_name": {"$push": "$counter_name"},
                "meters_type": {"$push": "$counter_type"},
                "meters_unit": {"$push": "$counter_unit"},
            }},
        ])

        for result in aggregate['result']:
            yield models.Resource(
                resource_id=result['_id'],
                user_id=result['user_id'],
                project_id=result['project_id'],
                first_sample_timestamp=result['first_sample_timestamp'],
                last_sample_timestamp=result['last_sample_timestamp'],
                source=result['source'],
                metadata=result['metadata'],
                meter=[
                    models.ResourceMeter(
                        counter_name=m_n,
                        counter_type=m_t,
                        counter_unit=m_u,
                    )
                    for m_n, m_u, m_t in zip(result['meters_name'],
                                             result['meters_unit'],
                                             result['meters_type'])
                ],
            )