Skip to content

Commit 74ac4c1

Browse files
author
Bill Prin
committed
Add Factory Methods for Metric/Resource/Timeseries
1 parent 4dbd7f9 commit 74ac4c1

File tree

5 files changed

+214
-1
lines changed

5 files changed

+214
-1
lines changed

gcloud/monitoring/client.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,22 @@
2828
https://cloud.google.com/monitoring/api/v3/
2929
"""
3030

31+
import datetime
32+
3133
from gcloud.client import JSONClient
3234
from gcloud.monitoring.connection import Connection
3335
from gcloud.monitoring.group import Group
36+
from gcloud.monitoring.metric import Metric
3437
from gcloud.monitoring.metric import MetricDescriptor
3538
from gcloud.monitoring.metric import MetricKind
3639
from gcloud.monitoring.metric import ValueType
3740
from gcloud.monitoring.query import Query
41+
from gcloud.monitoring.resource import Resource
3842
from gcloud.monitoring.resource import ResourceDescriptor
43+
from gcloud.monitoring.timeseries import Point
44+
from gcloud.monitoring.timeseries import TimeSeries
45+
46+
_UTCNOW = datetime.datetime.utcnow # To be replaced by tests.
3947

4048

4149
class Client(JSONClient):
@@ -195,6 +203,126 @@ def metric_descriptor(self, type_,
195203
display_name=display_name,
196204
)
197205

206+
@staticmethod
207+
def metric(type_, labels):
208+
"""Construct a metric object.
209+
210+
The type of the Metric should match the type of a MetricDescriptor.
211+
For a list of built-in Metric types and the associated labels, see:
212+
213+
https://cloud.google.com/monitoring/api/metrics
214+
215+
Here is an example of creating using a built-in metric type:
216+
217+
>>> type_ = 'compute.googleapis.com/instance/cpu/utilization'
218+
>>> metric = client.metric(type_, labels={
219+
... 'instance_name': 'my-instance',
220+
... })
221+
222+
For custom metrics, the simplest way to get the type is from the
223+
:class:`~gcloud.monitoring.metric.MetricDescriptor` class used to
224+
create the custom metric::
225+
226+
>>> metric = client.metric(metric_descriptor.type, labels={
227+
... 'instance_name': 'my-instance',
228+
... })
229+
230+
:type type_: string
231+
:param type_: The metric type name.
232+
233+
:type labels: dict
234+
:param labels: A mapping from label names to values for all labels
235+
enumerated in the associated :class:`MetricDescriptor`.
236+
237+
:rtype: :class:`~gcloud.monitoring.metric.Metric`
238+
:returns: The Metric created with the passed in arguments
239+
"""
240+
return Metric(type=type_, labels=labels)
241+
242+
@staticmethod
243+
def resource(type_, labels):
244+
"""Construct a resource object.
245+
246+
For a list of possible MonitoredResources and their associated labels,
247+
see:
248+
249+
https://cloud.google.com/monitoring/api/resources
250+
251+
For example, to create a Resource object for a GCE instance::
252+
253+
>>> resource = client.resource('gce_instance', labels= {
254+
... 'project_id': 'my-project-id',
255+
... 'instance_id': 'my-instance-id',
256+
... 'zone': 'us-central1-f'
257+
... })
258+
259+
:type type_: string
260+
:param type_: The monitored resource type name.
261+
262+
:type labels: dict
263+
:param labels: A mapping from label names to values for all labels
264+
enumerated in the associated
265+
:class:`ResourceDescriptor`.
266+
267+
:rtype: :class:`gcloud.resource.Resource`
268+
:returns: The Resource created with the passed in arguments
269+
"""
270+
return Resource(type_, labels)
271+
272+
@staticmethod
273+
def timeseries(metric, resource, value,
274+
start_time=None, end_time=None):
275+
"""Constructs a TimeSeries object for a single data point.
276+
277+
.. note::
278+
279+
While TimeSeries objects returned by the API can have
280+
multiple data points when queried as aggregates, TimeSeries created
281+
on the client-side can have at most one point.
282+
283+
For example::
284+
285+
>>> timeseries = client.timeseries(metric, resource, value,
286+
... end_time=end)
287+
288+
For more information, see:
289+
290+
https://cloud.google.com/monitoring/api/ref_v3/rest/v3/TimeSeries
291+
292+
:type metric: :class:`~gcloud.monitoring.metric.Metric`
293+
:param metric: A metric object.
294+
295+
:type resource: :class:`~gcloud.monitoring.resource.Resource`
296+
:param resource: A resource object.
297+
298+
:type value: bool, int, string, or float
299+
:param value: The value of the data point to create for the TimeSeries
300+
Note that this parameter can be multiple types and the
301+
TypedValue of the `gcloud.monitoring.TimeSeries.Point` will be
302+
mapped from the type passed in. For example, a Python float
303+
will be sent to the API as a doubleValue.
304+
305+
:type start_time: :class:`datetime.datetime`
306+
:param start_time:
307+
The start time for the point written to the
308+
timeseries. Defaults to None, which has meaning dependent on the
309+
metric_kind of the metric being written to.
310+
311+
:type end_time: :class`datetime.datetime`
312+
:param end_time:
313+
The end time for the point written to the timeseries. Defaults to
314+
the current time, as obtained by calling
315+
:meth:`datetime.datetime.utcnow`
316+
317+
:rtype: :class:`gcloud.timeseries.TimeSeries`
318+
:returns: The TimeSeries created with the passed in arguments
319+
"""
320+
if end_time is None:
321+
end_time = _UTCNOW()
322+
point = Point(value=value, start_time=start_time, end_time=end_time)
323+
return TimeSeries(metric=metric, resource=resource, metric_kind=None,
324+
value_type=None, points=[point])
325+
198326
def fetch_metric_descriptor(self, metric_type):
199327
"""Look up a metric descriptor by type.
200328
@@ -210,7 +338,7 @@ def fetch_metric_descriptor(self, metric_type):
210338
:returns: The metric descriptor instance.
211339
212340
:raises: :class:`gcloud.exceptions.NotFound` if the metric descriptor
213-
is not found.
341+
is not found.
214342
"""
215343
return MetricDescriptor._fetch(self, metric_type)
216344

gcloud/monitoring/metric.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ def __repr__(self):
319319
class Metric(collections.namedtuple('Metric', 'type labels')):
320320
"""A specific metric identified by specifying values for all labels.
321321
322+
The preferred way to construct a metric object is using the
323+
:meth:`~gcloud.monitoring.client.Client.metric` factory method
324+
of the :class:`~gcloud.monitoring.client.Client` class.
325+
322326
:type type: string
323327
:param type: The metric type name.
324328

gcloud/monitoring/resource.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ def __repr__(self):
158158
class Resource(collections.namedtuple('Resource', 'type labels')):
159159
"""A monitored resource identified by specifying values for all labels.
160160
161+
The preferred way to construct a resource object is using the
162+
:meth:`~gcloud.monitoring.client.Client.resource` factory method
163+
of the :class:`~gcloud.monitoring.client.Client` class.
164+
161165
:type type: string
162166
:param type: The resource type name.
163167

gcloud/monitoring/test_client.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,79 @@ def test_metric_descriptor_factory(self):
157157
self.assertEqual(descriptor.description, DESCRIPTION)
158158
self.assertEqual(descriptor.display_name, '')
159159

160+
def test_metric_factory(self):
161+
TYPE = 'custom.googleapis.com/my_metric'
162+
LABELS = {
163+
'instance_name': 'my-instance'
164+
}
165+
166+
client = self._makeOne(project=PROJECT, credentials=_Credentials())
167+
client.connection = _Connection() # For safety's sake.
168+
metric = client.metric(TYPE, LABELS)
169+
self.assertEqual(metric.type, TYPE)
170+
self.assertEqual(metric.labels, LABELS)
171+
172+
def test_resource_factory(self):
173+
TYPE = 'https://cloud.google.com/monitoring/api/resources'
174+
LABELS = {
175+
'project_id': 'my-project-id',
176+
'instance_id': 'my-instance-id',
177+
'zone': 'us-central1-f'
178+
}
179+
180+
client = self._makeOne(project=PROJECT, credentials=_Credentials())
181+
client.connection = _Connection() # For safety's sake.
182+
resource = client.resource(TYPE, LABELS)
183+
self.assertEqual(resource.type, TYPE)
184+
self.assertEqual(resource.labels, LABELS)
185+
186+
def test_timeseries_factory(self):
187+
import datetime
188+
from gcloud._testing import _Monkey
189+
import gcloud.monitoring.client
190+
METRIC_TYPE = 'custom.googleapis.com/my_metric'
191+
METRIC_LABELS = {
192+
'instance_name': 'my-instance'
193+
}
194+
195+
RESOURCE_TYPE = 'https://cloud.google.com/monitoring/api/resources'
196+
RESOURCE_LABELS = {
197+
'project_id': 'my-project-id',
198+
'instance_id': 'my-instance-id',
199+
'zone': 'us-central1-f'
200+
}
201+
202+
VALUE = 42
203+
204+
client = self._makeOne(project=PROJECT, credentials=_Credentials())
205+
client.connection = _Connection() # For safety's sake.
206+
metric = client.metric(METRIC_TYPE, METRIC_LABELS)
207+
resource = client.resource(RESOURCE_TYPE, RESOURCE_LABELS)
208+
NOW = datetime.datetime.utcnow()
209+
timeseries = client.timeseries(metric, resource, VALUE, end_time=NOW)
210+
211+
self.assertEqual(timeseries.metric, metric)
212+
self.assertEqual(timeseries.resource, resource)
213+
self.assertEqual(len(timeseries.points), 1)
214+
self.assertEqual(timeseries.points[0].value, VALUE)
215+
self.assertIsNone(timeseries.points[0].start_time)
216+
self.assertEqual(timeseries.points[0].end_time, NOW)
217+
218+
NEW_NOW = datetime.datetime.utcnow()
219+
timeseries_with_start = client.timeseries(metric,
220+
resource,
221+
VALUE,
222+
start_time=NOW,
223+
end_time=NEW_NOW)
224+
self.assertEqual(timeseries_with_start.points[0].start_time, NOW)
225+
self.assertEqual(timeseries_with_start.points[0].end_time, NEW_NOW)
226+
227+
with _Monkey(gcloud.monitoring.client, _UTCNOW=lambda: NOW):
228+
timeseries_no_end = client.timeseries(metric, resource, VALUE)
229+
230+
self.assertEqual(timeseries_no_end.points[0].end_time, NOW)
231+
self.assertIsNone(timeseries_no_end.points[0].start_time)
232+
160233
def test_fetch_metric_descriptor(self):
161234
TYPE = 'custom.googleapis.com/my_metric'
162235
NAME = 'projects/{project}/metricDescriptors/{type}'.format(

gcloud/monitoring/timeseries.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class TimeSeries(collections.namedtuple(
3232
'TimeSeries', 'metric resource metric_kind value_type points')):
3333
"""A single time series of metric values.
3434
35+
The preferred way to construct a TimeSeries object is using the
36+
:meth:`~gcloud.monitoring.client.Client.timeseries` factory method
37+
of the :class:`~gcloud.monitoring.client.Client` class.
38+
3539
:type metric: :class:`~gcloud.monitoring.metric.Metric`
3640
:param metric: A metric object.
3741

0 commit comments

Comments
 (0)