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'])
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 ], )
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], )
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)
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', {}))
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'], )
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_') ], )
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_') ], )
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'])
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)
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()
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']) ], )
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 ], )
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'] ], )
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)
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()
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'] ], )
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
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)
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)
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']) ], )