Browse Source

Fixes test_metrics test

Ceilometer API was removed in Queens, meaning that test_metrics has
not way to validate the metrics collected by the ceilometer-polling

The metrics are instead sent and collected by Gnocchi.

This patch adds a Gnocchi client, and updates the test_metrics
to use this client properly.

Change-Id: Id999d758505de10abb356befb854736e1904f6ee
Claudiu Belu 2 years ago
5 changed files with 87 additions and 40 deletions
  1. +1
  2. +12
  3. +0
  4. +50
  5. +24

+ 1
- 1
oswin_tempest_plugin/ View File

@@ -79,7 +79,7 @@ HyperVGroup = [
"published by the compute node's ceilometer-polling "
"agent. The value must be greater by ~15-20 seconds "
"than the agent's publish interval, defined in its "
"pipeline.yaml file (typically, the intervals are 600 "
"polling.yaml file (typically, the intervals are 600 "

+ 12
- 0
oswin_tempest_plugin/ View File

@@ -51,3 +51,15 @@ class OSWinTempestPlugin(plugins.TempestPlugin):
return [(, opts)
for group, opts in project_config.list_opts()]

def get_service_clients(self):
metric_config = config.service_client_config('metric')
metric_v1_params = {
'name': 'metric_v1',
'service_version': 'metric.v1',
'module_path': '',
'client_names': ['GnocchiClient'],

return [metric_v1_params]

+ 0
- 0
oswin_tempest_plugin/services/ View File

+ 50
- 0
oswin_tempest_plugin/services/ View File

@@ -0,0 +1,50 @@
# Copyright 2018 Cloudbase Solutions Srl
# 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
# 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.

from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client

from oswin_tempest_plugin import config

CONF = config.CONF

class GnocchiClient(rest_client.RestClient):

uri_prefix = 'v1'

def deserialize(self, body):
return json.loads(body.replace("\n", ""))

def _helper_list(self, uri):
resp, body = self.get(uri)
self.expected_success(200, resp.status)
body = self.deserialize(body)
return rest_client.ResponseBodyList(resp, body)

def list_resources(self):
uri = '%s/resource/generic' % self.uri_prefix
return self._helper_list(uri)

def list_samples(self, resource_id, meter_name):
"""Returns a list of samples for the given resource and meter.

:returns: list, each item being a list containing the following values
in this order: timestamp, granularity, value.
uri = '%s/resource/generic/%s/metric/%s/measures' % (
self.uri_prefix, resource_id, meter_name)
return self._helper_list(uri)

+ 24
- 39
oswin_tempest_plugin/tests/scenario/ View File

@@ -15,12 +15,6 @@

import time

# NOTE(claudiub): ceilometer might not be installed, it is not mandatory.
from ceilometer.tests.tempest.service import client as telemetry_client
except Exception:
telemetry_client = None

from oslo_log import log as logging
from tempest import clients

@@ -36,11 +30,10 @@ class ClientManager(clients.Manager):
def __init__(self, *args, **kwargs):
super(ClientManager, self).__init__(*args, **kwargs)


def _set_telemetry_clients(self):
self.telemetry_client = telemetry_client.TelemetryClient(
self.auth_provider, **telemetry_client.Manager.telemetry_params)
def set_gnocchi_client(self):
self.gnocchi_client = self.metric_v1.GnocchiClient()

class MetricsCollectionTestCase(test_base.TestBase):
@@ -65,7 +58,7 @@ class MetricsCollectionTestCase(test_base.TestBase):
a. Configure tempest's polled_metric_delay, by adding the
following line in tempest.conf, in the hyperv section:
polled_metrics_delay = <desired value>
b. Set the interval value in pipeline.yaml on the compute node to
b. Set the interval value in polling.yaml on the compute node to
the desired value and restart the ceilometer polling agent. The
interval value is set either for the 'meter_source' or for each
of the following: 'cpu_source', 'disk_source', 'network_source'.
@@ -73,7 +66,7 @@ class MetricsCollectionTestCase(test_base.TestBase):
Note: If the polled_metrics_delay value is too low, the tests might not
find any samples and fail because of this. As a recommandation,
polled_metrics_delay's value should be:
polled_metric_delay = <pipeline.yaml interval value> + <15-20 seconds>
polled_metric_delay = <polling.yaml interval value> + <15-20 seconds>

1. test_metrics - tests values for the following metrics:
@@ -84,7 +77,7 @@ class MetricsCollectionTestCase(test_base.TestBase):
1. Ceilometer agent on the compute node is running.
2. Ceilometer agent on the compute node has the polling interval
defined in pipeline.yaml lower than the polled_metrics_delay defined
defined in polling.yaml lower than the polled_metrics_delay defined
in this test suite.
3. The compute nodes' nova-compute and neutron-hyperv-agent services
have been configured to enable metrics collection.
@@ -96,9 +89,9 @@ class MetricsCollectionTestCase(test_base.TestBase):
def skip_checks(cls):
super(MetricsCollectionTestCase, cls).skip_checks()

if (not CONF.service_available.ceilometer or
not CONF.telemetry.deprecated_api_enabled):
raise cls.skipException("Ceilometer API support is required.")
for service in ['ceilometer', 'gnocchi']:
if not getattr(CONF.service_available, service):
raise cls.skipException("%s service is required." % service)

if not CONF.hyperv.collected_metrics:
raise cls.skipException("Collected metrics not configured.")
@@ -108,26 +101,20 @@ class MetricsCollectionTestCase(test_base.TestBase):
super(MetricsCollectionTestCase, cls).setup_clients()

# Telemetry client
cls.telemetry_client = cls.os_primary.telemetry_client
cls.telemetry_client = cls.os_primary.gnocchi_client

def _telemetry_check_samples(self, resource_id, meter_name):
def _check_samples(self, resource_id, meter_name):"Checking %(meter_name)s for resource %(resource_id)s" % {
'meter_name': meter_name, 'resource_id': resource_id})

samples = self.telemetry_client.list_samples(meter_name)
'Telemetry client returned no samples.')

resource_samples = [s for s in samples if
s['resource_id'] == resource_id]
samples = self.telemetry_client.list_samples(resource_id, meter_name)
'No meter %(meter_name)s samples for resource '
'%(resource_id)s found.' % {'meter_name': meter_name,
'resource_id': resource_id})
'Client returned no samples for the given resource '
'"%(resource_id)s" and meter "%(meter_name)s".' % {
'resource_id': resource_id, 'meter_name': meter_name})

non_zero_valued_samples = [s for s in resource_samples if
s['counter_volume'] > 0]
non_zero_valued_samples = [s for s in samples if s[2] > 0]
'All meter %(meter_name)s samples for resource '
@@ -145,12 +132,12 @@ class MetricsCollectionTestCase(test_base.TestBase):
# %(OS-EXT-SRV-ATTR:instance_name)s-%(instance_id)s-%(port_id)s
# the instance returned by self.servers_client does not contain the
# OS-EXT-SRV-ATTR:instance_name field. Which means that the resource_id
# must be found in ceilometer's resources.
# must be found in gnocchi's resources.
start_res_id = server['id']
resources = self.telemetry_client.list_resources()
res_ids = [r['resource_id'] for r in resources
if r['resource_id'].startswith('instance-') and
start_res_id in r['resource_id']]
res_ids = [r['id'] for r in resources
if r['original_resource_id'].startswith('instance-') and
start_res_id in r['original_resource_id']]

self.assertEqual(1, len(res_ids))
return res_ids[0]
@@ -162,19 +149,17 @@ class MetricsCollectionTestCase(test_base.TestBase):

# TODO(claudiub): Add more metrics.

if 'cpu' in CONF.hyperv.collected_metrics:
cpu_res_id = self._get_instance_cpu_resource_id(server)
self._telemetry_check_samples(cpu_res_id, 'cpu')
self._check_samples(cpu_res_id, 'cpu')

if 'network.outgoing.bytes' in CONF.hyperv.collected_metrics:
port_res_id = self._get_instance_port_resource_id(server)
self._check_samples(port_res_id, 'network.outgoing.bytes')

if '' in CONF.hyperv.collected_metrics:
disk_resource_id = self._get_instance_disk_resource_id(server)
self._telemetry_check_samples(disk_resource_id, '')
self._check_samples(disk_resource_id, '')

def test_metrics(self):
server_tuple = self._create_server()