From 2af55db70fd787481ec379d13fe8f897fccf16f8 Mon Sep 17 00:00:00 2001 From: Ken Rimey Date: Tue, 29 Mar 2016 21:45:33 -0400 Subject: [PATCH 01/61] Add gcloud.monitoring with fetching of metric descriptors. --- docs/index.rst | 9 ++ docs/monitoring-client.rst | 16 +++ docs/monitoring-label.rst | 7 ++ docs/monitoring-metric.rst | 7 ++ gcloud/monitoring/__init__.py | 24 ++++ gcloud/monitoring/client.py | 68 +++++++++++ gcloud/monitoring/connection.py | 47 ++++++++ gcloud/monitoring/label.py | 51 +++++++++ gcloud/monitoring/metric.py | 175 +++++++++++++++++++++++++++++ scripts/verify_included_modules.py | 1 + 10 files changed, 405 insertions(+) create mode 100644 docs/monitoring-client.rst create mode 100644 docs/monitoring-label.rst create mode 100644 docs/monitoring-metric.rst create mode 100644 gcloud/monitoring/__init__.py create mode 100644 gcloud/monitoring/client.py create mode 100644 gcloud/monitoring/connection.py create mode 100644 gcloud/monitoring/label.py create mode 100644 gcloud/monitoring/metric.py diff --git a/docs/index.rst b/docs/index.rst index a9c1094a6789..a1e402e22d98 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -119,6 +119,15 @@ logging-metric logging-sink +.. toctree:: + :maxdepth: 0 + :hidden: + :caption: Cloud Monitoring + + Client + monitoring-label + monitoring-metric + .. toctree:: :maxdepth: 0 :hidden: diff --git a/docs/monitoring-client.rst b/docs/monitoring-client.rst new file mode 100644 index 000000000000..8083d39d5f4f --- /dev/null +++ b/docs/monitoring-client.rst @@ -0,0 +1,16 @@ +Monitoring Client +================= + +.. automodule:: gcloud.monitoring.client + :members: + :undoc-members: + :show-inheritance: + +Connection +~~~~~~~~~~ + +.. automodule:: gcloud.monitoring.connection + :members: + :undoc-members: + :show-inheritance: + diff --git a/docs/monitoring-label.rst b/docs/monitoring-label.rst new file mode 100644 index 000000000000..827058c92662 --- /dev/null +++ b/docs/monitoring-label.rst @@ -0,0 +1,7 @@ +Label Descriptors +================= + +.. automodule:: gcloud.monitoring.label + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/monitoring-metric.rst b/docs/monitoring-metric.rst new file mode 100644 index 000000000000..79e608b63932 --- /dev/null +++ b/docs/monitoring-metric.rst @@ -0,0 +1,7 @@ +Metric Descriptors +================== + +.. automodule:: gcloud.monitoring.metric + :members: + :undoc-members: + :show-inheritance: diff --git a/gcloud/monitoring/__init__.py b/gcloud/monitoring/__init__.py new file mode 100644 index 000000000000..20f1a1f74247 --- /dev/null +++ b/gcloud/monitoring/__init__.py @@ -0,0 +1,24 @@ +# 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. + +"""Google Monitoring API wrapper.""" + +from gcloud.monitoring.client import Client +from gcloud.monitoring.connection import Connection +from gcloud.monitoring.label import LabelDescriptor +from gcloud.monitoring.metric import Metric +from gcloud.monitoring.metric import MetricDescriptor + + +SCOPE = Connection.SCOPE diff --git a/gcloud/monitoring/client.py b/gcloud/monitoring/client.py new file mode 100644 index 000000000000..fcbfe4c75f47 --- /dev/null +++ b/gcloud/monitoring/client.py @@ -0,0 +1,68 @@ +# 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. + +"""Client for interacting with the Google Monitoring API.""" + +from gcloud.client import JSONClient +from gcloud.monitoring.connection import Connection +from gcloud.monitoring.metric import MetricDescriptor + + +class Client(JSONClient): + """Client to bundle configuration needed for API requests. + + :type project: string + :param project: The target project. If not passed, falls back to the + default inferred from the environment. + + :type credentials: :class:`oauth2client.client.OAuth2Credentials` or + :class:`NoneType` + :param credentials: The OAuth2 Credentials to use for the connection + owned by this client. If not passed (and if no ``http`` + object is passed), falls back to the default inferred + from the environment. + + :type http: :class:`httplib2.Http` or class that defines ``request()`` + :param http: An optional HTTP object to make requests. If not passed, an + ``http`` object is created that is bound to the + ``credentials`` for the current object. + """ + + _connection_class = Connection + + def fetch_metric_descriptor(self, metric_type): + """Look up a metric descriptor by type. + + :type metric_type: string + :param metric_type: The metric type name. + + :rtype: :class:`~gcloud.monitoring.metric.MetricDescriptor` + :returns: The metric descriptor instance. + + :raises: :class:`gcloud.exceptions.NotFound` + """ + return MetricDescriptor._fetch(self, metric_type) + + def list_metric_descriptors(self, filter=None): + """List all metric descriptors for the project. + + :type filter: string or ``NoneType`` + :param filter: An optional filter string describing the metric + descriptors to be returned. + + :rtype: list of :class:`~gcloud.monitoring.metric.MetricDescriptor` + :returns: A list of metric descriptor instances. + """ + # Allow "filter" as a parameter name: pylint: disable=redefined-builtin + return MetricDescriptor._list(self, filter) diff --git a/gcloud/monitoring/connection.py b/gcloud/monitoring/connection.py new file mode 100644 index 000000000000..5887da62e65d --- /dev/null +++ b/gcloud/monitoring/connection.py @@ -0,0 +1,47 @@ +# 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. + +"""Create / interact with gcloud monitoring connections.""" + +from gcloud import connection as base_connection + + +class Connection(base_connection.JSONConnection): + """A connection to Google Monitoring via the JSON REST API. + + :type credentials: :class:`oauth2client.client.OAuth2Credentials` + :param credentials: (Optional) The OAuth2 Credentials to use for this + connection. + + :type http: :class:`httplib2.Http` or class that defines ``request()`` + :param http: (Optional) HTTP object to make requests. + + :type api_base_url: string + :param api_base_url: The base of the API call URL. Defaults to the value + :attr:`Connection.API_BASE_URL`. + """ + + API_BASE_URL = 'https://monitoring.googleapis.com' + """The base of the API call URL.""" + + API_VERSION = 'v3' + """The version of the API, used in building the API call's URL.""" + + API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' + """A template for the URL of a particular API call.""" + + SCOPE = ('https://www.googleapis.com/auth/monitoring.read', + 'https://www.googleapis.com/auth/monitoring', + 'https://www.googleapis.com/auth/cloud-platform') + """The scopes required for authenticating as a Monitoring consumer.""" diff --git a/gcloud/monitoring/label.py b/gcloud/monitoring/label.py new file mode 100644 index 000000000000..0439abfa41e2 --- /dev/null +++ b/gcloud/monitoring/label.py @@ -0,0 +1,51 @@ +# 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. + +"""Label Descriptors for the Google Monitoring API.""" + +import collections + + +class LabelDescriptor(collections.namedtuple('LabelDescriptor', + 'key value_type description')): + """Schema specification and documentation for a single label. + + :type key: string + :param key: The name of the label. + + :type value_type: string + :param value_type: The type of the label. It must be one of ``"STRING"``, + ``"BOOL"``, or ``"INT64"``. + + :type description: string + :param description: A human-readable description for the label. + """ + __slots__ = () + + @classmethod + def _from_dict(cls, info): + """Construct a label descriptor from the parsed JSON representation. + + :type info: dict + :param info: + A ``dict`` parsed from the JSON wire-format representation. + + :rtype: :class:`LabelDescriptor` + :returns: A label descriptor. + """ + return cls( + info.get('key', ''), + info.get('valueType', 'STRING'), + info.get('description', ''), + ) diff --git a/gcloud/monitoring/metric.py b/gcloud/monitoring/metric.py new file mode 100644 index 000000000000..7bb9f3e5ca72 --- /dev/null +++ b/gcloud/monitoring/metric.py @@ -0,0 +1,175 @@ +# 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. + +"""Metric Descriptors for the Google Monitoring API.""" + +# Features intentionally omitted from this first version of the client library: +# - Creating and deleting metric descriptors. + +import collections + +from gcloud.monitoring.label import LabelDescriptor + + +class MetricDescriptor(collections.namedtuple( + 'MetricDescriptor', ('name type labels metric_kind value_type unit' + ' description display_name'))): + """Specification of a metric type and its schema. + + Metric descriptor instances are immutable. + + :type name: string + :param name: The "resource name" of the metric descriptor. For example: + ``"projects//metricDescriptors/"`` + + :type type: string + :param type: The metric type including a DNS name prefix. For example: + ``"compute.googleapis.com/instance/cpu/utilization"`` + + :type labels: list of :class:`~gcloud.monitoring.label.LabelDescriptor` + :param labels: A sequence of label descriptors specifying the labels used + to identify a specific instance of this metric. + + :type metric_kind: string + :param metric_kind: The kind of measurement. It must be one of ``"GAUGE"``, + ``"DELTA"``, or ``"CUMULATIVE"``. + + :type value_type: string + :param value_type: The value type of the metric. It must be one of + ``"BOOL"``, ``"INT64"``, ``"DOUBLE"``, ``"STRING"``, + ``"DISTRIBUTION"``, or ``"MONEY"``. + + :type unit: string + :param unit: The unit in which the metric value is reported. + + :type description: string + :param description: A detailed description of the metric. + + :type display_name: string + :param display_name: A concise name for the metric. + """ + __slots__ = () + + @classmethod + def _fetch(cls, client, metric_type): + """Look up a metric descriptor by type. + + :type client: :class:`gcloud.monitoring.client.Client` + :param client: The client to use. + + :type metric_type: string + :param metric_type: The metric type name. + + :rtype: :class:`MetricDescriptor` + :returns: The metric descriptor instance. + + :raises: :class:`gcloud.exceptions.NotFound` + """ + path = '/projects/{project}/metricDescriptors/{type}'.format( + project=client.project, + type=metric_type) + info = client.connection.api_request('GET', path) + return cls._from_dict(info) + + @classmethod + def _list(cls, client, filter=None): + """List all metric descriptors for the project. + + :type client: :class:`gcloud.monitoring.client.Client` + :param client: The client to use. + + :type filter: string or None + :param filter: An optional filter string describing the metric + descriptors to be returned. + + :rtype: list of :class:`MetricDescriptor` + :returns: A list of metric descriptor instances. + """ + # Allow "filter" as a parameter name: pylint: disable=redefined-builtin + + path = '/projects/{}/metricDescriptors/'.format(client.project) + + def _descriptors(): + page_token = None + while True: + params = {} + + if filter is not None: + params['filter'] = filter + + if page_token is not None: + params['pageToken'] = page_token + + response = client.connection.api_request('GET', path, + query_params=params) + for info in response.get('metricDescriptors', []): + yield cls._from_dict(info) + + page_token = response.get('nextPageToken') + if not page_token: + break + + return list(_descriptors()) + + @classmethod + def _from_dict(cls, info): + """Construct a metric descriptor from the parsed JSON representation. + + :type info: dict + :param info: + A ``dict`` parsed from the JSON wire-format representation. + + :rtype: :class:`MetricDescriptor` + :returns: A metric descriptor. + """ + return cls( + type=info.get('type', ''), + name=info.get('name', ''), + description=info.get('description', ''), + display_name=info.get('displayName', ''), + labels=tuple(LabelDescriptor._from_dict(label) + for label in info.get('labels', [])), + metric_kind=info['metricKind'], + value_type=info['valueType'], + unit=info.get('unit', ''), + ) + + +class Metric(collections.namedtuple('Metric', 'type labels')): + """A specific metric identified by specifying values for all labels. + + :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:`MetricDescriptor`. + """ + __slots__ = () + + @classmethod + def _from_dict(cls, info): + """Construct a metric object from the parsed JSON representation. + + :type info: dict + :param info: + A ``dict`` parsed from the JSON wire-format representation. + + :rtype: :class:`Metric` + :returns: A metric object. + """ + return cls( + type=info.get('type', ''), + labels=info.get('labels', {}), + ) diff --git a/scripts/verify_included_modules.py b/scripts/verify_included_modules.py index 41bb658ebe23..551c81d975e7 100644 --- a/scripts/verify_included_modules.py +++ b/scripts/verify_included_modules.py @@ -37,6 +37,7 @@ 'gcloud.dns.__init__', 'gcloud.iterator', 'gcloud.logging.__init__', + 'gcloud.monitoring.__init__', 'gcloud.pubsub.__init__', 'gcloud.resource_manager.__init__', 'gcloud.search.__init__', From a03740e080459982e55cb7d8ac50305e89d3258d Mon Sep 17 00:00:00 2001 From: Ken Rimey Date: Wed, 30 Mar 2016 17:34:45 -0400 Subject: [PATCH 02/61] Add fetching of resource descriptors. --- docs/index.rst | 1 + docs/monitoring-resource.rst | 7 ++ gcloud/monitoring/__init__.py | 2 + gcloud/monitoring/client.py | 29 +++++- gcloud/monitoring/resource.py | 160 ++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 docs/monitoring-resource.rst create mode 100644 gcloud/monitoring/resource.py diff --git a/docs/index.rst b/docs/index.rst index a1e402e22d98..3acb2fca3e77 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -127,6 +127,7 @@ Client monitoring-label monitoring-metric + monitoring-resource .. toctree:: :maxdepth: 0 diff --git a/docs/monitoring-resource.rst b/docs/monitoring-resource.rst new file mode 100644 index 000000000000..8f6c23d470a7 --- /dev/null +++ b/docs/monitoring-resource.rst @@ -0,0 +1,7 @@ +Resource Descriptors +==================== + +.. automodule:: gcloud.monitoring.resource + :members: + :undoc-members: + :show-inheritance: diff --git a/gcloud/monitoring/__init__.py b/gcloud/monitoring/__init__.py index 20f1a1f74247..48e601ba0d0e 100644 --- a/gcloud/monitoring/__init__.py +++ b/gcloud/monitoring/__init__.py @@ -19,6 +19,8 @@ from gcloud.monitoring.label import LabelDescriptor from gcloud.monitoring.metric import Metric from gcloud.monitoring.metric import MetricDescriptor +from gcloud.monitoring.resource import Resource +from gcloud.monitoring.resource import ResourceDescriptor SCOPE = Connection.SCOPE diff --git a/gcloud/monitoring/client.py b/gcloud/monitoring/client.py index fcbfe4c75f47..7d1278f75173 100644 --- a/gcloud/monitoring/client.py +++ b/gcloud/monitoring/client.py @@ -17,6 +17,7 @@ from gcloud.client import JSONClient from gcloud.monitoring.connection import Connection from gcloud.monitoring.metric import MetricDescriptor +from gcloud.monitoring.resource import ResourceDescriptor class Client(JSONClient): @@ -57,7 +58,7 @@ def fetch_metric_descriptor(self, metric_type): def list_metric_descriptors(self, filter=None): """List all metric descriptors for the project. - :type filter: string or ``NoneType`` + :type filter: string or None :param filter: An optional filter string describing the metric descriptors to be returned. @@ -66,3 +67,29 @@ def list_metric_descriptors(self, filter=None): """ # Allow "filter" as a parameter name: pylint: disable=redefined-builtin return MetricDescriptor._list(self, filter) + + def fetch_resource_descriptor(self, resource_type): + """Look up a resource descriptor by type. + + :type resource_type: string + :param resource_type: The resource type name. + + :rtype: :class:`~gcloud.monitoring.resource.ResourceDescriptor` + :returns: The resource descriptor instance. + + :raises: :class:`gcloud.exceptions.NotFound` + """ + return ResourceDescriptor._fetch(self, resource_type) + + def list_resource_descriptors(self, filter=None): + """List all resource descriptors for the project. + + :type filter: string or None + :param filter: An optional filter string describing the resource + descriptors to be returned. + + :rtype: list of :class:`~gcloud.monitoring.resource.ResourceDescriptor` + :returns: A list of resource descriptor instances. + """ + # Allow "filter" as a parameter name: pylint: disable=redefined-builtin + return ResourceDescriptor._list(self, filter) diff --git a/gcloud/monitoring/resource.py b/gcloud/monitoring/resource.py new file mode 100644 index 000000000000..ed633bae50f1 --- /dev/null +++ b/gcloud/monitoring/resource.py @@ -0,0 +1,160 @@ +# 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. + +"""Resource Descriptors for the Google Monitoring API.""" + +import collections + +from gcloud.monitoring.label import LabelDescriptor + + +class ResourceDescriptor(collections.namedtuple( + 'ResourceDescriptor', ('name type display_name description labels'))): + """Specification of a monitored resource type and its schema. + + Resource descriptor instances are immutable. + + :type name: string + :param name: + The "resource name" of the monitored resource descriptor: + ``"projects//monitoredResourceDescriptors/"`` + + :type type: string + :param type: + The monitored resource type. For example: ``"gce_instance"`` + + :type display_name: string + :param display_name: + A concise name that might be displayed in user interfaces. + + :type description: string + :param description: + A detailed description that might be used in documentation. + + :type labels: list of :class:`~gcloud.monitoring.label.LabelDescriptor` + :param labels: + A sequence of label descriptors specifying the labels used + to identify a specific instance of this monitored resource. + """ + __slots__ = () + + @classmethod + def _fetch(cls, client, resource_type): + """Look up a resource descriptor by type. + + :type client: :class:`gcloud.monitoring.client.Client` + :param client: The client to use. + + :type resource_type: string + :param resource_type: The resource type name. + + :rtype: :class:`ResourceDescriptor` + :returns: The resource descriptor instance. + + :raises: :class:`gcloud.exceptions.NotFound` + """ + path = '/projects/{}/monitoredResourceDescriptors/{}'.format( + client.project, resource_type) + info = client.connection.api_request('GET', path) + return cls._from_dict(info) + + @classmethod + def _list(cls, client, filter=None): + """List all resource descriptors for the project. + + :type client: :class:`gcloud.monitoring.client.Client` + :param client: The client to use. + + :type filter: string or None + :param filter: An optional filter string describing the resource + descriptors to be returned. + + :rtype: list of :class:`ResourceDescriptor` + :returns: A list of resource descriptor instances. + """ + # Allow "filter" as a parameter name: pylint: disable=redefined-builtin + + path = '/projects/{}/monitoredResourceDescriptors/'.format( + client.project) + + def _descriptors(): + page_token = None + while True: + params = {} + + if filter is not None: + params['filter'] = filter + + if page_token is not None: + params['pageToken'] = page_token + + response = client.connection.api_request('GET', path, + query_params=params) + for info in response.get('resourceDescriptors', []): + yield cls._from_dict(info) + + page_token = response.get('nextPageToken') + if not page_token: + break + + return list(_descriptors()) + + @classmethod + def _from_dict(cls, info): + """Construct a resource descriptor from the parsed JSON representation. + + :type info: dict + :param info: + A ``dict`` parsed from the JSON wire-format representation. + + :rtype: :class:`ResourceDescriptor` + :returns: A resource descriptor. + """ + return cls( + name=info.get('name', ''), + type=info.get('type', ''), + display_name=info.get('displayName', ''), + description=info.get('description', ''), + labels=tuple(LabelDescriptor._from_dict(label) + for label in info.get('labels', [])), + ) + + +class Resource(collections.namedtuple('Resource', 'type labels')): + """A monitored resource identified by specifying values for all labels. + + :type type: string + :param type: The resource type name. + + :type labels: dict + :param labels: A mapping from label names to values for all labels + enumerated in the associated :class:`ResourceDescriptor`. + """ + __slots__ = () + + @classmethod + def _from_dict(cls, info): + """Construct a resource object from the parsed JSON representation. + + :type info: dict + :param info: + A ``dict`` parsed from the JSON wire-format representation. + + :rtype: :class:`Resource` + :returns: A resource object. + """ + return cls( + type=info.get('type', ''), + labels=info.get('labels', {}), + ) From 6f57e6a175dfaa27fab775e6a00d645ea2b8ef99 Mon Sep 17 00:00:00 2001 From: Ken Rimey Date: Thu, 31 Mar 2016 18:01:42 -0400 Subject: [PATCH 03/61] Add querying of time series. --- docs/index.rst | 3 +- docs/monitoring-timeseries.rst | 7 + gcloud/monitoring/__init__.py | 3 + gcloud/monitoring/client.py | 91 +++- gcloud/monitoring/timeseries.py | 706 ++++++++++++++++++++++++++++++++ 5 files changed, 808 insertions(+), 2 deletions(-) create mode 100644 docs/monitoring-timeseries.rst create mode 100644 gcloud/monitoring/timeseries.py diff --git a/docs/index.rst b/docs/index.rst index 3acb2fca3e77..cd77db13620d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -125,9 +125,10 @@ :caption: Cloud Monitoring Client - monitoring-label + monitoring-timeseries monitoring-metric monitoring-resource + monitoring-label .. toctree:: :maxdepth: 0 diff --git a/docs/monitoring-timeseries.rst b/docs/monitoring-timeseries.rst new file mode 100644 index 000000000000..120f7280c885 --- /dev/null +++ b/docs/monitoring-timeseries.rst @@ -0,0 +1,7 @@ +Time Series +=========== + +.. automodule:: gcloud.monitoring.timeseries + :members: + :undoc-members: + :show-inheritance: diff --git a/gcloud/monitoring/__init__.py b/gcloud/monitoring/__init__.py index 48e601ba0d0e..823c143fcd07 100644 --- a/gcloud/monitoring/__init__.py +++ b/gcloud/monitoring/__init__.py @@ -21,6 +21,9 @@ from gcloud.monitoring.metric import MetricDescriptor from gcloud.monitoring.resource import Resource from gcloud.monitoring.resource import ResourceDescriptor +from gcloud.monitoring.timeseries import Point +from gcloud.monitoring.timeseries import TimeSeries +from gcloud.monitoring.timeseries import Query SCOPE = Connection.SCOPE diff --git a/gcloud/monitoring/client.py b/gcloud/monitoring/client.py index 7d1278f75173..33062132c38f 100644 --- a/gcloud/monitoring/client.py +++ b/gcloud/monitoring/client.py @@ -12,12 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Client for interacting with the Google Monitoring API.""" +"""Client for interacting with the Google Monitoring API. + +Example:: + + from gcloud import monitoring + client = monitoring.Client() + query = client.query(minutes=5) + print query.as_dataframe() # Requires pandas. + +At present, the client supports querying of time series, metric descriptors, +and monitored resource descriptors. + +See the documentation for +`Google Monitoring API V3 `_. +""" from gcloud.client import JSONClient from gcloud.monitoring.connection import Connection from gcloud.monitoring.metric import MetricDescriptor from gcloud.monitoring.resource import ResourceDescriptor +from gcloud.monitoring.timeseries import Query class Client(JSONClient): @@ -42,9 +57,69 @@ class Client(JSONClient): _connection_class = Connection + def query(self, + metric_type=Query.DEFAULT_METRIC_TYPE, + resource_type=None, + end_time=None, start_time=None, + days=0, hours=0, minutes=0): + """Construct a query object for listing time series. + + Example:: + + query = client.query(minutes=5) + print query.as_dataframe() # Requires pandas. + + :type metric_type: string + :param metric_type: The metric type name. The default value is + ``"compute.googleapis.com/instance/cpu/utilization"``, but + please note that this default value is provided only for + demonstration purposes and is subject to change. + + :type resource_type: string + :param resource_type: An optional resource type to filter by. + For example: ``"gce_instance"`` + + :type end_time: :class:`datetime.datetime` or string 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. If the + ``days``/``hours``/``minutes`` parameters are not used, the + end time can alternatively be provided as a timestamp string + in RFC3339 UTC "Zulu" format. + + :type start_time: :class:`datetime.datetime` or string or None + :param start_time: An optional start time (exclusive) of the time + interval for which results should be returned, as either a + datetime object or a timestamp string. If omitted and no + non-zero duration is specified, the interval is a point in + time. If any of ``days``, ``hours``, or ``minutes`` is non-zero, + these are combined and subtracted from the end time to determine + the start time. + + :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. + + :rtype: :class:`~gcloud.monitoring.timeseries.Query` + :returns: The query object. + """ + return Query(self, metric_type, resource_type, + end_time=end_time, start_time=start_time, + days=days, hours=hours, minutes=minutes) + def fetch_metric_descriptor(self, metric_type): """Look up a metric descriptor by type. + Example:: + + METRIC = 'compute.googleapis.com/instance/cpu/utilization' + print client.fetch_metric_descriptor(METRIC) + :type metric_type: string :param metric_type: The metric type name. @@ -58,6 +133,11 @@ def fetch_metric_descriptor(self, metric_type): def list_metric_descriptors(self, filter=None): """List all metric descriptors for the project. + Example:: + + for descriptor in client.list_metric_descriptors(): + print descriptor.type + :type filter: string or None :param filter: An optional filter string describing the metric descriptors to be returned. @@ -71,6 +151,10 @@ def list_metric_descriptors(self, filter=None): def fetch_resource_descriptor(self, resource_type): """Look up a resource descriptor by type. + Example:: + + print client.fetch_resource_descriptor('gce_instance') + :type resource_type: string :param resource_type: The resource type name. @@ -84,6 +168,11 @@ def fetch_resource_descriptor(self, resource_type): def list_resource_descriptors(self, filter=None): """List all resource descriptors for the project. + Example:: + + for descriptor in client.list_resource_descriptors(): + print descriptor.type + :type filter: string or None :param filter: An optional filter string describing the resource descriptors to be returned. diff --git a/gcloud/monitoring/timeseries.py b/gcloud/monitoring/timeseries.py new file mode 100644 index 000000000000..f9f087c54a67 --- /dev/null +++ b/gcloud/monitoring/timeseries.py @@ -0,0 +1,706 @@ +# 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 in the Google Monitoring API.""" + +# Features intentionally omitted from this first version of the client library: +# - Creating time series. +# - Natural representation of distribution values. + +import collections +import copy +import datetime +import itertools + +import six + +from gcloud.monitoring.metric import Metric +from gcloud.monitoring.resource import Resource + +TOP_RESOURCE_LABELS = [ + 'project_id', + 'aws_account', + 'location', + 'region', + 'zone', +] + + +class Query(object): + """Query object for listing time series. + + 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 + ``"compute.googleapis.com/instance/cpu/utilization"``, but + please note that this default value is provided only for + demonstration purposes and is subject to change. + + :type resource_type: string + :param resource_type: An optional resource type to filter by. + For example: ``"gce_instance"`` + + :type end_time: :class:`datetime.datetime` or string 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. If the + ``days``/``hours``/``minutes`` parameters are not used, the + end time can alternatively be provided as a timestamp string + in RFC3339 UTC "Zulu" format. + + :type start_time: :class:`datetime.datetime` or string or None + :param start_time: An optional start time (exclusive) of the time + interval for which results should be returned, as either a + datetime object or a timestamp string. If omitted and no + non-zero duration is specified, the interval is a point in + time. If any of ``days``, ``hours``, or ``minutes`` is non-zero, + these are combined and subtracted from the end time to determine + the start time. + + :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. + """ + + DEFAULT_METRIC_TYPE = 'compute.googleapis.com/instance/cpu/utilization' + + def __init__(self, client, + metric_type=DEFAULT_METRIC_TYPE, resource_type=None, + end_time=None, start_time=None, + days=0, hours=0, minutes=0): + if end_time is None: + end_time = datetime.datetime.utcnow().replace(second=0, + microsecond=0) + + if days or hours or minutes: + if start_time is not None: + raise ValueError('Duration and start time both specified.') + start_time = end_time - datetime.timedelta(days=days, + hours=hours, + minutes=minutes) + + self._client = client + self._end_time = end_time + self._start_time = start_time + self._filter = _Filter(metric_type, resource_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 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_group(self, group_id): + """Copy the query and add filtering by group. + + :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') + + :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_resource_labels(self, *args, **kwargs): + """Copy the query and add filtering by resource labels. + + Examples:: + + query = query.select_resource_labels(zone='us-central1-a') + query = query.select_resource_labels(zone_prefix='europe-') + + A keyword argument ``