Understanding Gnocchi Metrics
Metrics are one of the main object types in Gnocchi. They are identified by a UUID and they can also be attached to a resource by using a resource name. Metrics store measures, and the way they do this is defined by archive policies. These are concepts that I will cover in future articles.
Basically, a metric designates any thing that can be measured: the CPU usage of a server, the temperature of a room or the number of bytes sent by a network interface.
In the Gnocchi architecture, the storage back-end is responsible for storing measures of created metrics. It receives timestamps and values, and pre-computes aggregations according to the defined archive policies.
Interacting With Metrics Using the API
Using the Gnocchi REST API, a metric can be created by simply passing an archive policy:
POST /v1/metric HTTP/1.1
Content-Length: 35
Content-Type: application/json
The API will respond with a created 201 CREATED
status code and some information about the created metric:
HTTP/1.1 201 Created
Location: http://localhost/v1/metric/4c964d57-9d67-4c49-bc61-d48d15b705c6
Content-Length: 206
Content-Type: application/json
We can then retrieve the complete information of this metric by using its UUID:
GET /v1/metric/4c964d57-9d67-4c49-bc61-d48d15b705c6 HTTP/1.1
Content-Length: 0
The response will be:
HTTP/1.1 200 OK
Content-Length: 532
Content-Type: application/json
Metric Source Code
Source code for the Metric object can be found in storage/__init__.py
:
storage/init.py:
class Metric(object):
def __init__(self, id, archive_policy,
creator=None,
name=None,
resource_id=None):
self.id = id
self.archive_policy = archive_policy
self.creator = creator
self.name = name
self.resource_id = resource_id
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, self.id)
def __str__(self):
return str(self.id)
def __eq__(self, other):
return (isinstance(other, Metric)
and self.id == other.id
and self.archive_policy == other.archive_policy
and self.creator == other.creator
and self.name == other.name
and self.resource_id == other.resource_id)
__hash__ = object.__hash__
Some exceptions related to metrics are also defined here. For example:
class MetricAlreadyExists(StorageError):
"""Error raised when this metric already exists."""
def __init__(self, metric):
self.metric = metric
super(MetricAlreadyExists, self).__init__(
"Metric %s already exists" % metric)
class LockedMetric(StorageError):
"""Error raised when this metric is already being handled by another."""
def __init__(self, metric):
self.metric = metric
super(LockedMetric, self).__init__("Metric %s is locked" % metric)
We can see these exceptions being raised by the drivers, which are also located in /storage/
. For example, when the file driver is trying to create a metric:
def _create_metric(self, metric):
path = self._build_metric_dir(metric)
try:
os.mkdir(path, 0o750)
except OSError as e:
if e.errno == errno.EEXIST:
raise storage.MetricAlreadyExists(metric)
raise
for agg in metric.archive_policy.aggregation_methods:
try:
os.mkdir(self._build_metric_path(metric, agg), 0o750)
except OSError as e:
if e.errno != errno.EEXIST:
raise
Since the file driver storages the metric data in the file system using actual files, when a file already exists, a system error will be produced and the OSError
exception will be catched.
This storage module makes use of the Python errno
module, which provides standard system errno
symbols. In this case, the exception's errno
would be EEXIST
, indicating the file already exists. The MetricAlreadyExists
exception is then raised.
Interacting With Metrics Using the Gnocchi Client
With the gnocchi client, we can create metrics like this:
gnocchi.metric.create({
'name': 'my_metric',
'archive_policy_name': 'high',
})
And we can delete it by passing the metric's UUID (if there is no resource ID) like this:
gnocchi.metric.delete(metric='1c0f69bf-67bb-4080-8e7c-53723f7e6194')