# Copyright 2016 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Time series query for the `Google Monitoring API (V3)`_. .. _Google Monitoring API (V3): https://cloud.google.com/monitoring/api/ref_v3/rest/v3/\ projects.timeSeries/list """ import copy import datetime import itertools import six from gcloud.monitoring._dataframe import _build_dataframe from gcloud.monitoring.timeseries import TimeSeries _UTCNOW = datetime.datetime.utcnow # To be replaced by tests. class Aligner(object): """Allowed values for the `supported aligners`_.""" ALIGN_NONE = 'ALIGN_NONE' ALIGN_DELTA = 'ALIGN_DELTA' ALIGN_RATE = 'ALIGN_RATE' ALIGN_INTERPOLATE = 'ALIGN_INTERPOLATE' ALIGN_NEXT_OLDER = 'ALIGN_NEXT_OLDER' ALIGN_MIN = 'ALIGN_MIN' ALIGN_MAX = 'ALIGN_MAX' ALIGN_MEAN = 'ALIGN_MEAN' ALIGN_COUNT = 'ALIGN_COUNT' ALIGN_SUM = 'ALIGN_SUM' ALIGN_STDDEV = 'ALIGN_STDDEV' ALIGN_COUNT_TRUE = 'ALIGN_COUNT_TRUE' ALIGN_FRACTION_TRUE = 'ALIGN_FRACTION_TRUE' class Reducer(object): """Allowed values for the `supported reducers`_.""" REDUCE_NONE = 'REDUCE_NONE' REDUCE_MEAN = 'REDUCE_MEAN' REDUCE_MIN = 'REDUCE_MIN' REDUCE_MAX = 'REDUCE_MAX' REDUCE_SUM = 'REDUCE_SUM' REDUCE_STDDEV = 'REDUCE_STDDEV' REDUCE_COUNT = 'REDUCE_COUNT' REDUCE_COUNT_TRUE = 'REDUCE_COUNT_TRUE' REDUCE_FRACTION_TRUE = 'REDUCE_FRACTION_TRUE' REDUCE_PERCENTILE_99 = 'REDUCE_PERCENTILE_99' REDUCE_PERCENTILE_95 = 'REDUCE_PERCENTILE_95' REDUCE_PERCENTILE_50 = 'REDUCE_PERCENTILE_50' REDUCE_PERCENTILE_05 = 'REDUCE_PERCENTILE_05' class Query(object): """Query object for retrieving metric data. The preferred way to construct a query object is using the :meth:`~gcloud.monitoring.client.Client.query` method of the :class:`~gcloud.monitoring.client.Client` class. :type client: :class:`gcloud.monitoring.client.Client` :param client: The client to use. :type metric_type: string :param metric_type: The metric type name. The default value is :data:`Query.DEFAULT_METRIC_TYPE `, but please note that this default value is provided only for demonstration purposes and is subject to change. See the `supported metrics`_. :type end_time: :class:`datetime.datetime` or None :param end_time: The end time (inclusive) of the time interval for which results should be returned, as a datetime object. The default is the start of the current minute. The start time (exclusive) is determined by combining the values of ``days``, ``hours``, and ``minutes``, and subtracting the resulting duration from the end time. It is also allowed to omit the end time and duration here, in which case :meth:`~gcloud.monitoring.query.Query.select_interval` must be called before the query is executed. :type days: integer :param days: The number of days in the time interval. :type hours: integer :param hours: The number of hours in the time interval. :type minutes: integer :param minutes: The number of minutes in the time interval. :raises: :exc:`ValueError` if ``end_time`` is specified but ``days``, ``hours``, and ``minutes`` are all zero. If you really want to specify a point in time, use :meth:`~gcloud.monitoring.query.Query.select_interval`. .. _supported metrics: https://cloud.google.com/monitoring/api/metrics """ DEFAULT_METRIC_TYPE = 'compute.googleapis.com/instance/cpu/utilization' def __init__(self, client, metric_type=DEFAULT_METRIC_TYPE, end_time=None, days=0, hours=0, minutes=0): start_time = None if days or hours or minutes: if end_time is None: end_time = _UTCNOW().replace(second=0, microsecond=0) start_time = end_time - datetime.timedelta(days=days, hours=hours, minutes=minutes) elif end_time is not None: raise ValueError('Non-zero duration required for time interval.') self._client = client self._end_time = end_time self._start_time = start_time self._filter = _Filter(metric_type) self._per_series_aligner = None self._alignment_period_seconds = None self._cross_series_reducer = None self._group_by_fields = () def __iter__(self): return self.iter() @property def metric_type(self): """The metric type name.""" return self._filter.metric_type @property def filter(self): """The filter string. This is constructed from the metric type, the resource type, and selectors for the group ID, monitored projects, resource labels, and metric labels. """ return str(self._filter) def select_interval(self, end_time, start_time=None): """Copy the query and set the query time interval. Example:: import datetime now = datetime.datetime.utcnow() query = query.select_interval( end_time=now, start_time=now - datetime.timedelta(minutes=5)) As a convenience, you can alternatively specify the end time and an interval duration when you create the query initially. :type end_time: :class:`datetime.datetime` :param end_time: The end time (inclusive) of the time interval for which results should be returned, as a datetime object. :type start_time: :class:`datetime.datetime` or None :param start_time: The start time (exclusive) of the time interval for which results should be returned, as a datetime object. If not specified, the interval is a point in time. :rtype: :class:`Query` :returns: The new query object. """ new_query = self.copy() new_query._end_time = end_time new_query._start_time = start_time return new_query def select_group(self, group_id): """Copy the query and add filtering by group. Example:: query = query.select_group('1234567') :type group_id: string :param group_id: The ID of a group to filter by. :rtype: :class:`Query` :returns: The new query object. """ new_query = self.copy() new_query._filter.group_id = group_id return new_query def select_projects(self, *args): """Copy the query and add filtering by monitored projects. This is only useful if the target project represents a Stackdriver account containing the specified monitored projects. Examples:: query = query.select_projects('project-1') query = query.select_projects('project-1', 'project-2') :type args: tuple :param args: Project IDs limiting the resources to be included in the query. :rtype: :class:`Query` :returns: The new query object. """ new_query = self.copy() new_query._filter.projects = args return new_query def select_resources(self, *args, **kwargs): """Copy the query and add filtering by resource labels. Examples:: query = query.select_resources(zone='us-central1-a') query = query.select_resources(zone_prefix='europe-') query = query.select_resources(resource_type='gce_instance') A keyword argument ``