diff --git a/docker-compose/tango-prometheus-exporter/code/__init__.py b/docker-compose/tango-prometheus-exporter/code/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/docker-compose/tango-prometheus-exporter/lofar2-policy.json b/docker-compose/tango-prometheus-exporter/lofar2-policy.json index 994d9dd1877b87ab7ccecbcfe325c97333dd7f92..e7da467993c3fed8f4fef44c107bbc8f0608d072 100644 --- a/docker-compose/tango-prometheus-exporter/lofar2-policy.json +++ b/docker-compose/tango-prometheus-exporter/lofar2-policy.json @@ -5,7 +5,7 @@ }, "devices": { - "STAT/AntennaField/1": { + "stat/antennafield/1": { "include": [ "HBAT_ANT_mask_RW" ], @@ -13,31 +13,31 @@ "HBAT_BF_delay_steps_*" ] }, - "STAT/APSCT/1": { + "stat/apsct/1": { }, - "STAT/APSPU/1": { + "stat/apspu/1": { }, - "STAT/Beamlet/1": { + "stat/beamlet/1": { "exclude": [ "FPGA_beamlet_subband_select_*", "FPGA_bf_weights_*" ] }, - "STAT/Boot/1": { + "stat/boot/1": { }, - "STAT/DigitalBeam/1": { + "stat/digitalbeam/1": { }, - "STAT/Docker/1": { + "stat/docker/1": { }, - "STAT/Observation/*":{ + "stat/observation/*":{ }, - "STAT/ObservationControl/1":{ + "stat/observationcontrol/1":{ }, - "STAT/PSOC/1": { + "stat/psoc/1": { }, - "STAT/PCON/1": { + "stat/pcon/1": { }, - "STAT/RECV/1": { + "stat/recv/1": { "include": [ "ANT_mask_RW", "RCU_mask_RW" @@ -51,7 +51,7 @@ "*_ITRF_offsets_R" ] }, - "STAT/SDP/1": { + "stat/sdp/1": { "include": [ "TR_fpga_mask_RW" ], @@ -65,7 +65,7 @@ "FPGA_wg_phase_*" ] }, - "STAT/SST/1": { + "stat/sst/1": { "exclude": [ "sst_R", "sst_timestamp_R", @@ -74,14 +74,14 @@ "subbands_calibrated_R" ] }, - "STAT/TileBeam/1": { + "stat/tilebeam/1": { }, - "STAT/UNB2/1": { + "stat/unb2/1": { "include": [ "UNB2_mask_RW" ] }, - "STAT/XST/1": { + "stat/xst/1": { "exclude": [ "FPGA_xst_ring_*", "FPGA_xst_rx_align_*", diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/prometheus/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/default/prometheus/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/prometheus/test_tango_prometheus_client.py b/tangostationcontrol/tangostationcontrol/integration_test/default/prometheus/test_tango_prometheus_client.py new file mode 100644 index 0000000000000000000000000000000000000000..f0d1354f6ce9779d3585bd1119f2820652651810 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/prometheus/test_tango_prometheus_client.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the LOFAR 2.0 Station Software +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +import importlib +import sys, os +import numpy + +from tango import Database + +from tangostationcontrol.integration_test.base import BaseIntegrationTestCase +from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy + +module_name = 'ArchiverPolicy' +file_path = os.path.join(os.path.realpath('..'), 'docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py') +spec = importlib.util.spec_from_file_location(module_name, file_path) +tpc_policy = importlib.util.module_from_spec(spec) +sys.modules[module_name] = tpc_policy +spec.loader.exec_module(tpc_policy) + +module_name = 'CustomCollector' +spec = importlib.util.spec_from_file_location(module_name, file_path) +tpc_cc = importlib.util.module_from_spec(spec) +sys.modules[module_name] = tpc_cc +spec.loader.exec_module(tpc_cc) + +class TestPrometheusClient(BaseIntegrationTestCase): + + config_path = os.path.join(os.path.realpath('..'), 'docker-compose/tango-prometheus-exporter/lofar2-policy.json') + CONFIG = tpc_policy.ArchiverPolicy.load_config(config_path) + + def setUp(self): + super().setUp() + + def initialise_collector(self): + db = Database() + station = db.get_property("station","name")["name"][0] + custom_collector = tpc_cc.CustomCollector(self.CONFIG, station) + self.assertIsNotNone(custom_collector) + return custom_collector + + def setup_recv_proxy(self, device_name='stat/recv/1'): + # setup RECV + recv_proxy = TestDeviceProxy(device_name) + recv_proxy.off() + recv_proxy.warm_boot() + recv_proxy.set_defaults() + return recv_proxy + + def test_tango_db_devices(self): + """ Test if device names are correctly retrieved from Tango DB """ + policy = tpc_policy.ArchiverPolicy(self.CONFIG) + db_devices = policy.device_list() + self.assertNotEqual(len(db_devices), 0) + + def test_policy_devices(self): + """ Test if device names are correctly filtered with policy file """ + policy = tpc_policy.ArchiverPolicy(self.CONFIG) + db_devices = policy.device_list() + policy_devices = policy.devices() + self.assertLessEqual(len(policy_devices), len(db_devices)) + config_retrieved_devices = [*policy.config['devices'].keys()] # list of device names from policy file + for d in config_retrieved_devices: + if '*' not in d: # filter out wildcards + self.assertIn(d, policy_devices) + + def test_archiver_policy_attribute_list(self): + """ Test if the full set of archiving policy for the given device is retrieved """ + device_name = 'stat/recv/1' + recv_proxy = self.setup_recv_proxy(device_name) + policy = tpc_policy.ArchiverPolicy(self.CONFIG) + attribute_list = policy.attribute_list(device_name, recv_proxy.get_attribute_list()) + include = policy.config['devices']['stat/recv/1']['include'] # attribute that must be included + for i in include: + if '*' not in i: # exclude wildcard + self.assertIn(i, attribute_list) + exclude = policy.config['devices']['stat/recv/1']['exclude'] # attribute that must be excluded + for e in exclude: + if '*' not in e: # exclude wildcard + self.assertNotIn(e, attribute_list) + + def test_label_metric_list(self): + """ Test whether the metric label list matches up with the ones defined in the GaugeMetricFamily constructor""" + collector = self.initialise_collector() + attribute_metrics, scraping_metrics = collector.collect() + expected_attribute_labels = ['station', 'device', 'name', 'str_value', 'type', 'x', 'y', 'idx'] + expected_scraping_labels = ['station', 'device'] + numpy.testing.assert_equal([*attribute_metrics.samples[0].labels.keys()], expected_attribute_labels) + numpy.testing.assert_equal([*scraping_metrics.samples[0].labels.keys()], expected_scraping_labels) + + def test_collector_metrics_with_devices_in_off(self): + """ Test if the metrics are exposed even if devices are in OFF state """ + device_name = 'stat/recv/1' + recv_proxy = TestDeviceProxy(device_name) + recv_proxy.off() + collector = self.initialise_collector() + expected_attrs = ['State', 'Status'] # only state attributes are scraped when device is in OFF + metrics = collector.device_metrics(device_name) + actual_attrs = [metrics[0][0][2], metrics[1][0][2]] + numpy.testing.assert_equal(sorted(actual_attrs), expected_attrs) + + def test_collector_metrics(self): + """ Test if the metrics are correctly exposed """ + device_name = 'stat/recv/1' + recv_proxy = self.setup_recv_proxy(device_name) + collector = self.initialise_collector() + expected_attr_values = recv_proxy.ANT_error_R + numpy.testing.assert_equal(expected_attr_values, numpy.array([True] * 96)) + attribute_metrics, scraping_metrics = collector.collect() + metric_samples = attribute_metrics.samples + # Test attribute metrics ANT_error_R + samples_values = [] + for s in metric_samples: + if (s.labels['name'] == 'ANT_error_R'): + samples_values.append(numpy.bool(s.value)) + numpy.testing.assert_equal(samples_values, expected_attr_values) + # Test scraping metrics + total_scraping_time = scraping_metrics.samples[-1].value + self.assertLess(total_scraping_time, 10) # Set acceptable scraping time ? diff --git a/tangostationcontrol/tangostationcontrol/test/prometheus/__init__.py b/tangostationcontrol/tangostationcontrol/test/prometheus/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tangostationcontrol/tangostationcontrol/test/prometheus/test_archiver_policy.py b/tangostationcontrol/tangostationcontrol/test/prometheus/test_archiver_policy.py new file mode 100644 index 0000000000000000000000000000000000000000..9b8f53478227e5390eea98c4a9436b8363eaa7c3 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/test/prometheus/test_archiver_policy.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the LOFAR 2.0 Station Software +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +import sys, os +import importlib.util + +from tangostationcontrol.test import base + +module_name = 'ArchiverPolicy' +file_path = os.path.join(os.path.realpath('..'), 'docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py') +spec = importlib.util.spec_from_file_location(module_name, file_path) +tpc = importlib.util.module_from_spec(spec) +sys.modules[module_name] = tpc +spec.loader.exec_module(tpc) + +class TestArchiverPolicy(base.TestCase): + + config_path = os.path.join(os.path.realpath('..'), 'docker-compose/tango-prometheus-exporter/lofar2-policy.json') + CONFIG = tpc.ArchiverPolicy.load_config(config_path) + + def test_config_file(self): + """ Test if policy config file is correctly retrieved """ + empty_policy = tpc.ArchiverPolicy() # empty config file + expected_config = {'default': {}, 'devices': {}} + self.assertEqual(empty_policy.config, expected_config) + policy = tpc.ArchiverPolicy(self.CONFIG) + self.assertEqual([*policy.config], ['default', 'devices']) # dict keys + diff --git a/tangostationcontrol/test-requirements.txt b/tangostationcontrol/test-requirements.txt index b838488f5b1c37e796e6c9a0a7dc0a5c2e0ba341..b8fe7099dac8cd826106e9f74136dd2c78f41bc3 100644 --- a/tangostationcontrol/test-requirements.txt +++ b/tangostationcontrol/test-requirements.txt @@ -19,3 +19,5 @@ testscenarios>=0.5.0 # Apache-2.0/BSD testtools>=2.4.0 # MIT timeout-decorator>=0.5 # MIT xenon>=0.8.0 # MIT +prometheus_client # Apache-2.0 +python-logstash-async # MIT