Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions gcloud/monitoring/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,22 @@
https://cloud.google.com/monitoring/api/v3/
"""

import datetime

from gcloud.client import JSONClient
from gcloud.monitoring.connection import Connection
from gcloud.monitoring.group import Group
from gcloud.monitoring.metric import Metric
from gcloud.monitoring.metric import MetricDescriptor
from gcloud.monitoring.metric import MetricKind
from gcloud.monitoring.metric import ValueType
from gcloud.monitoring.query import Query
from gcloud.monitoring.resource import Resource
from gcloud.monitoring.resource import ResourceDescriptor
from gcloud.monitoring.timeseries import Point
from gcloud.monitoring.timeseries import TimeSeries

_UTCNOW = datetime.datetime.utcnow # To be replaced by tests.


class Client(JSONClient):
Expand Down Expand Up @@ -195,6 +203,126 @@ def metric_descriptor(self, type_,
display_name=display_name,
)

@staticmethod

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

def metric(type_, labels):
"""Factory for constructing metric objects.

:class:`~gcloud.monitoring.metric.Metric` objects are typically
created to write custom metric values. The type should match the
metric type specified in the
:class:`~gcloud.monitoring.metric.MetricDescriptor` used to
create the custom metric::

This comment was marked as spam.

This comment was marked as spam.


>>> metric = client.metric('custom.googleapis.com/my_metric',
... labels={
... 'status': 'successful',
... })

:type type_: string
:param type_: The metric type name.

:type labels: dict
:param labels: A mapping from label names to values for all labels
enumerated in the associated
:class:`~gcloud.monitoring.metric.MetricDescriptor`.

:rtype: :class:`~gcloud.monitoring.metric.Metric`
:returns: The metric object.
"""
return Metric(type=type_, labels=labels)

@staticmethod
def resource(type_, labels):
"""Factory for constructing monitored resource objects.

A monitored resource object (
:class:`~gcloud.monitoring.resource.Resource`) is
typically used to create a
:class:`~gcloud.monitoring.timeseries.TimeSeries` object.

For a list of possible monitored resource types and their associated
labels, see:

https://cloud.google.com/monitoring/api/resources

:type type_: string
:param type_: The monitored resource type name.

:type labels: dict
:param labels: A mapping from label names to values for all labels
enumerated in the associated
:class:`~gcloud.monitoring.resource.ResourceDescriptor`,
except that ``project_id`` can and should be omitted
when writing time series data.

:rtype: :class:`~gcloud.monitoring.resource.Resource`
:returns: A monitored resource object.
"""
return Resource(type_, labels)

@staticmethod
def time_series(metric, resource, value,
end_time=None, start_time=None):
"""Construct a time series object for a single data point.

.. note::

While :class:`~gcloud.monitoring.timeseries.TimeSeries` objects
returned by the API typically have multiple data points,
:class:`~gcloud.monitoring.timeseries.TimeSeries` objects
sent to the API must have at most one point.

For example::

>>> timeseries = client.time_series(metric, resource, 1.23,
... end_time=end)

For more information, see:

https://cloud.google.com/monitoring/api/ref_v3/rest/v3/TimeSeries

:type metric: :class:`~gcloud.monitoring.metric.Metric`
:param metric: A :class:`~gcloud.monitoring.metric.Metric` object.

:type resource: :class:`~gcloud.monitoring.resource.Resource`
:param resource: A :class:`~gcloud.monitoring.resource.Resource`
object.

:type value: bool, int, string, or float

This comment was marked as spam.

This comment was marked as spam.

:param value:
The value of the data point to create for the
:class:`~gcloud.monitoring.timeseries.TimeSeries`.

.. note::

The Python type of the value will determine the
`class`:ValueType: sent to the API, which must match the value
type specified in the metric descriptor. For example, a Python
float will be sent to the API as a :data:`ValueType.DOUBLE`.

:type end_time: :class:`~datetime.datetime`
:param end_time:
The end time for the point to be included in the time series.
Assumed to be UTC if no time zone information is present.
Defaults to the current time, as obtained by calling
:meth:`datetime.datetime.utcnow`.

:type start_time: :class:`~datetime.datetime`
:param start_time:
The start time for the point to be included in the time series.
Assumed to be UTC if no time zone information is present
Defaults to None. If the start time is unspecified,
the API interprets the start time to be the same as the end time.

:rtype: :class:`~gcloud.monitoring.timeseries.TimeSeries`
:returns: A time series object.
"""
if end_time is None:
end_time = _UTCNOW()
point = Point(value=value, start_time=start_time, end_time=end_time)
return TimeSeries(metric=metric, resource=resource, metric_kind=None,
value_type=None, points=[point])

def fetch_metric_descriptor(self, metric_type):
"""Look up a metric descriptor by type.

Expand Down
4 changes: 4 additions & 0 deletions gcloud/monitoring/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,10 @@ def __repr__(self):
class Metric(collections.namedtuple('Metric', 'type labels')):
"""A specific metric identified by specifying values for all labels.

The preferred way to construct a metric object is using the
:meth:`~gcloud.monitoring.client.Client.metric` factory method
of the :class:`~gcloud.monitoring.client.Client` class.

:type type: string
:param type: The metric type name.

Expand Down
4 changes: 4 additions & 0 deletions gcloud/monitoring/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ def __repr__(self):
class Resource(collections.namedtuple('Resource', 'type labels')):
"""A monitored resource identified by specifying values for all labels.

The preferred way to construct a resource object is using the
:meth:`~gcloud.monitoring.client.Client.resource` factory method
of the :class:`~gcloud.monitoring.client.Client` class.

:type type: string
:param type: The resource type name.

Expand Down
114 changes: 114 additions & 0 deletions gcloud/monitoring/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,120 @@ def test_metric_descriptor_factory(self):
self.assertEqual(descriptor.description, DESCRIPTION)
self.assertEqual(descriptor.display_name, '')

def test_metric_factory(self):
TYPE = 'custom.googleapis.com/my_metric'
LABELS = {
'instance_name': 'my-instance'
}

client = self._makeOne(project=PROJECT, credentials=_Credentials())
client.connection = _Connection() # For safety's sake.
metric = client.metric(TYPE, LABELS)
self.assertEqual(metric.type, TYPE)
self.assertEqual(metric.labels, LABELS)

def test_resource_factory(self):
TYPE = 'https://cloud.google.com/monitoring/api/resources'
LABELS = {
'instance_id': 'my-instance-id',
'zone': 'us-central1-f'
}

client = self._makeOne(project=PROJECT, credentials=_Credentials())
client.connection = _Connection() # For safety's sake.
resource = client.resource(TYPE, LABELS)
self.assertEqual(resource.type, TYPE)
self.assertEqual(resource.labels, LABELS)

def test_timeseries_factory_gauge(self):
import datetime
from gcloud._testing import _Monkey
import gcloud.monitoring.client
METRIC_TYPE = 'custom.googleapis.com/my_metric'
METRIC_LABELS = {
'status': 'successful'
}

RESOURCE_TYPE = 'gce_instance'
RESOURCE_LABELS = {
'instance_id': '1234567890123456789',
'zone': 'us-central1-f'
}

VALUE = 42
TIME1 = datetime.datetime.utcnow()

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

client = self._makeOne(project=PROJECT, credentials=_Credentials())
client.connection = _Connection() # For safety's sake.
metric = client.metric(METRIC_TYPE, METRIC_LABELS)
resource = client.resource(RESOURCE_TYPE, RESOURCE_LABELS)

# Construct a time series assuming a gauge metric.
timeseries = client.time_series(metric, resource, VALUE,
end_time=TIME1)
self.assertEqual(timeseries.metric, metric)
self.assertEqual(timeseries.resource, resource)
self.assertEqual(len(timeseries.points), 1)
self.assertEqual(timeseries.points[0].value, VALUE)
self.assertIsNone(timeseries.points[0].start_time)
self.assertEqual(timeseries.points[0].end_time, TIME1)

This comment was marked as spam.

This comment was marked as spam.

TIME2 = datetime.datetime.utcnow()
# Construct a time series assuming a gauge metric using the current
# time
with _Monkey(gcloud.monitoring.client, _UTCNOW=lambda: TIME2):
timeseries_no_end = client.time_series(metric, resource, VALUE)

self.assertEqual(timeseries_no_end.points[0].end_time, TIME2)
self.assertIsNone(timeseries_no_end.points[0].start_time)

def test_timeseries_factory_cumulative(self):
import datetime
MY_CUMULATIVE_METRIC = 'custom.googleapis.com/my_cumulative_metric'
METRIC_LABELS = {
'status': 'successful'
}

RESOURCE_TYPE = 'gce_instance'
RESOURCE_LABELS = {
'instance_id': '1234567890123456789',
'zone': 'us-central1-f'
}

client = self._makeOne(project=PROJECT, credentials=_Credentials())
client.connection = _Connection() # For safety's sake.
resource = client.resource(RESOURCE_TYPE, RESOURCE_LABELS)

VALUE = 42
VALUE2 = 43
RESET_TIME = datetime.datetime.utcnow()
TIME1 = datetime.datetime.utcnow()
TIME2 = datetime.datetime.utcnow()

# Construct a couple of time series assuming a cumulative metric.
cumulative_metric = client.metric(MY_CUMULATIVE_METRIC, METRIC_LABELS)
cumulative_timeseries = client.time_series(cumulative_metric,
resource,
VALUE,
start_time=RESET_TIME,
end_time=TIME1)

cumulative_timeseries2 = client.time_series(cumulative_metric,
resource,
VALUE2,
start_time=RESET_TIME,
end_time=TIME2)

self.assertEqual(cumulative_timeseries.points[0].start_time,
RESET_TIME)
self.assertEqual(cumulative_timeseries.points[0].end_time, TIME1)
self.assertEqual(cumulative_timeseries.points[0].value, VALUE)
self.assertEqual(cumulative_timeseries2.points[0].start_time,
RESET_TIME)
self.assertEqual(cumulative_timeseries2.points[0].end_time,
TIME2)
self.assertEqual(cumulative_timeseries2.points[0].value, VALUE2)

def test_fetch_metric_descriptor(self):
TYPE = 'custom.googleapis.com/my_metric'
NAME = 'projects/{project}/metricDescriptors/{type}'.format(
Expand Down
5 changes: 5 additions & 0 deletions gcloud/monitoring/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class TimeSeries(collections.namedtuple(
'TimeSeries', 'metric resource metric_kind value_type points')):
"""A single time series of metric values.

The preferred way to construct a
:class:`~gcloud.monitoring.timeseries.TimeSeries` object is
using the :meth:`~gcloud.monitoring.client.Client.time_series` factory
method of the :class:`~gcloud.monitoring.client.Client` class.

:type metric: :class:`~gcloud.monitoring.metric.Metric`
:param metric: A metric object.

Expand Down