From 29794f15c157f1b31a54c3e8008ac0ecb0dced41 Mon Sep 17 00:00:00 2001 From: Przemek Rogala Date: Fri, 16 Aug 2024 17:35:54 +0100 Subject: [PATCH] [2.x] Allow metrics to be enabled selectively and disable all by default. (#363) * Allow metrics to be enabled selectively and disable all by default. * Add change fragments. --- changes/325.added | 1 + changes/325.changed | 1 + development/development.env | 2 + development/nautobot_config.py | 1 + docs/admin/install.md | 26 +++++++--- nautobot_device_lifecycle_mgmt/__init__.py | 1 + nautobot_device_lifecycle_mgmt/metrics.py | 47 ++++++++++++++----- .../tests/test_metrics.py | 18 +++---- 8 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 changes/325.added create mode 100644 changes/325.changed diff --git a/changes/325.added b/changes/325.added new file mode 100644 index 00000000..a7ae41e6 --- /dev/null +++ b/changes/325.added @@ -0,0 +1 @@ +Added `enabled_metrics` setting to allow selective enabling of metrics. \ No newline at end of file diff --git a/changes/325.changed b/changes/325.changed new file mode 100644 index 00000000..a6739d80 --- /dev/null +++ b/changes/325.changed @@ -0,0 +1 @@ +DLM metrics are now disabled by default. \ No newline at end of file diff --git a/development/development.env b/development/development.env index 54f0b870..231d24f9 100644 --- a/development/development.env +++ b/development/development.env @@ -36,3 +36,5 @@ POSTGRES_DB=${NAUTOBOT_DB_NAME} MYSQL_USER=${NAUTOBOT_DB_USER} MYSQL_DATABASE=${NAUTOBOT_DB_NAME} MYSQL_ROOT_HOST=% + +NAUTOBOT_DLM_ENABLED_METRICS = "nautobot_lcm_software_compliance_per_device_type,nautobot_lcm_software_compliance_per_inventory_item,nautobot_lcm_hw_end_of_support_per_part_number,nautobot_lcm_hw_end_of_support_per_location" diff --git a/development/nautobot_config.py b/development/nautobot_config.py index f2961173..5bd313a6 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -137,5 +137,6 @@ "barchart_bar_width": float(os.environ.get("BARCHART_BAR_WIDTH", 0.1)), "barchart_width": int(os.environ.get("BARCHART_WIDTH", 12)), "barchart_height": int(os.environ.get("BARCHART_HEIGHT", 5)), + "enabled_metrics": [x for x in os.environ.get("NAUTOBOT_DLM_ENABLED_METRICS", "").split(",") if x], }, } diff --git a/docs/admin/install.md b/docs/admin/install.md index 4f5b800d..a3d40759 100644 --- a/docs/admin/install.md +++ b/docs/admin/install.md @@ -69,9 +69,23 @@ sudo systemctl restart nautobot nautobot-worker nautobot-scheduler ## App Configuration The app behavior can be controlled with the following list of settings: -| Key | Example | Default | Description | -| -------------------- | ------------------------- | ------- | -------------------------------------------------------------------- | -| `expired_field` | `end_of_support` | | The field name representing the expiry date. | -| `barchart_bar_width` | `0.1` | `0.15` | The width of the table bar within the overview report. | -| `barchart_width` | `12` | | The width of the barchart within the overview report. | -| `barchart_height` | `5` | | The height of the barchart within the overview report. | + +| Key | ENV VAR | Example | Default | Description | +| -------------------- | ------- | ------------------------- | ------- | -------------------------------------------------------------------- | +| `expired_field` | `NAUTOBOT_DLM_EXPIRED_FIELD` | `end_of_support` | `end_of_support` | The field name representing the expiry date. | +| `barchart_bar_width` | `BARCHART_BAR_WIDTH` | `0.1` | `0.15` | The width of the table bar within the overview report. | +| `barchart_width` | `BARCHART_WIDTH` | `12` | `12` | The width of the barchart within the overview report. | +| `barchart_height` | `BARCHART_HEIGHT` | `5` | `5` | The height of the barchart within the overview report. | +| `enabled_metrics` | `NAUTOBOT_DLM_ENABLED_METRICS` | `["nautobot_lcm_hw_end_of_support_per_location"]` | `[]` | Enables metrics corresponding to the provided, comma separated, entries. | + +### Available Metric Names + +Following are the metric names that can be defined in `enabled_metrics`: + +- `nautobot_lcm_software_compliance_per_device_type`: Number of devices with valid/invalid software by device_type. + +- `nautobot_lcm_software_compliance_per_inventory_item`: Number of inventory items with valid/invalid software. + +- `nautobot_lcm_hw_end_of_support_per_part_number`: Number of End of Support devices and inventory items per Part Number. + +- `nautobot_lcm_hw_end_of_support_per_location`: Number of End of Support devices and inventory items per Location. diff --git a/nautobot_device_lifecycle_mgmt/__init__.py b/nautobot_device_lifecycle_mgmt/__init__.py index 5e514e73..5292838c 100644 --- a/nautobot_device_lifecycle_mgmt/__init__.py +++ b/nautobot_device_lifecycle_mgmt/__init__.py @@ -26,6 +26,7 @@ class NautobotDeviceLifecycleManagementConfig(NautobotAppConfig): "barchart_bar_width": 0.1, "barchart_width": 12, "barchart_height": 5, + "enabled_metrics": [], } caching_config = {} docs_view_name = "plugins:nautobot_device_lifecycle_mgmt:docs" diff --git a/nautobot_device_lifecycle_mgmt/metrics.py b/nautobot_device_lifecycle_mgmt/metrics.py index 7d9b1b85..6cdaf9e4 100644 --- a/nautobot_device_lifecycle_mgmt/metrics.py +++ b/nautobot_device_lifecycle_mgmt/metrics.py @@ -2,6 +2,7 @@ from datetime import datetime +from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.db.models import Count, F, IntegerField, OuterRef, Q, Subquery, Value from django.db.models.functions import Coalesce @@ -14,6 +15,8 @@ InventoryItemSoftwareValidationResult, ) +PLUGIN_CFG = settings.PLUGINS_CONFIG["nautobot_device_lifecycle_mgmt"] + def metrics_lcm_validation_report_device_type(): """Calculate number of devices with valid/invalid software by device_type. @@ -115,8 +118,8 @@ def metrics_lcm_validation_report_inventory_item(): yield inventory_item_software_compliance_gauge -def metrics_lcm_hw_end_of_support(): # pylint: disable=too-many-locals - """Calculate number of End of Support devices and inventory items per Part Number and per Location. +def metrics_lcm_hw_end_of_support_part_number(): # pylint: disable=too-many-locals + """Calculate number of End of Support devices and inventory items per Part Number. Yields: GaugeMetricFamily: Prometheus Metrics @@ -126,11 +129,6 @@ def metrics_lcm_hw_end_of_support(): # pylint: disable=too-many-locals "Nautobot LCM Hardware End of Support per Part Number", labels=["part_number"], ) - hw_end_of_support_location_gauge = GaugeMetricFamily( - "nautobot_lcm_hw_end_of_support_per_location", - "Nautobot LCM Hardware End of Support per Location", - labels=["location"], - ) today = datetime.today().date() hw_end_of_support = HardwareLCM.objects.filter(end_of_support__lt=today) @@ -184,6 +182,27 @@ def metrics_lcm_hw_end_of_support(): # pylint: disable=too-many-locals yield hw_end_of_support_part_number_gauge + +def metrics_lcm_hw_end_of_support_location(): # pylint: disable=too-many-locals + """Calculate number of End of Support devices and inventory items per Location. + + Yields: + GaugeMetricFamily: Prometheus Metrics + """ + hw_end_of_support_location_gauge = GaugeMetricFamily( + "nautobot_lcm_hw_end_of_support_per_location", + "Nautobot LCM Hardware End of Support per Location", + labels=["location"], + ) + + today = datetime.today().date() + hw_end_of_support = HardwareLCM.objects.filter(end_of_support__lt=today) + hw_end_of_support_device_types = hw_end_of_support.exclude(device_type__isnull=True).values_list( + "device_type", flat=True + ) + hw_end_of_support_invitems = hw_end_of_support.exclude(inventory_item__isnull=True).values_list( + "inventory_item", flat=True + ) # Initialize per location count to 0 for all locations device_location_types = LocationType.objects.filter(content_types=ContentType.objects.get_for_model(Device)) init_location_counts = ( @@ -229,8 +248,12 @@ def metrics_lcm_hw_end_of_support(): # pylint: disable=too-many-locals yield hw_end_of_support_location_gauge -metrics = [ - metrics_lcm_hw_end_of_support, - metrics_lcm_validation_report_device_type, - metrics_lcm_validation_report_inventory_item, -] +metrics = [] +if "nautobot_lcm_software_compliance_per_device_type" in PLUGIN_CFG["enabled_metrics"]: + metrics.append(metrics_lcm_validation_report_device_type) +if "nautobot_lcm_software_compliance_per_inventory_item" in PLUGIN_CFG["enabled_metrics"]: + metrics.append(metrics_lcm_validation_report_inventory_item) +if "nautobot_lcm_hw_end_of_support_per_part_number" in PLUGIN_CFG["enabled_metrics"]: + metrics.append(metrics_lcm_hw_end_of_support_part_number) +if "nautobot_lcm_hw_end_of_support_per_location" in PLUGIN_CFG["enabled_metrics"]: + metrics.append(metrics_lcm_hw_end_of_support_location) diff --git a/nautobot_device_lifecycle_mgmt/tests/test_metrics.py b/nautobot_device_lifecycle_mgmt/tests/test_metrics.py index 3396828c..1d840d64 100644 --- a/nautobot_device_lifecycle_mgmt/tests/test_metrics.py +++ b/nautobot_device_lifecycle_mgmt/tests/test_metrics.py @@ -4,7 +4,8 @@ from nautobot.core.testing import TestCase from nautobot_device_lifecycle_mgmt.metrics import ( - metrics_lcm_hw_end_of_support, + metrics_lcm_hw_end_of_support_part_number, + metrics_lcm_hw_end_of_support_location, metrics_lcm_validation_report_device_type, metrics_lcm_validation_report_inventory_item, ) @@ -54,22 +55,20 @@ def test_metrics_lcm_validation_report_inventory_item(self): sample_labels = tuple(sample.labels.items()) self.assertEqual(expected_ts_samples[sample_labels], sample.value) - def test_metrics_lcm_hw_end_of_support_does_not_error(self): + def test_metrics_lcm_hw_end_of_support_location_does_not_error(self): """Query providing data to hw_end_of_support_location_gauge metric should not error out. Guards against https://github.com/nautobot/nautobot-app-device-lifecycle-mgmt/issues/309 """ - metric_gen = metrics_lcm_hw_end_of_support() - # skip hw_end_of_support_part_number_gauge - next(metric_gen) + metric_gen = metrics_lcm_hw_end_of_support_location() try: # Get hw_end_of_support_location_gauge next(metric_gen) except ProgrammingError: - self.fail("hw_end_of_support_location_gauge query bug") + self.fail("metrics_lcm_hw_end_of_support_location query bug") def test_metrics_lcm_hw_end_of_support_part_number(self): """Test metric hw_end_of_support_part_number_gauge.""" - metric_gen = metrics_lcm_hw_end_of_support() + metric_gen = metrics_lcm_hw_end_of_support_part_number() # Get hw_end_of_support_part_number_gauge metric = next(metric_gen) @@ -87,10 +86,7 @@ def test_metrics_lcm_hw_end_of_support_part_number(self): def test_metrics_lcm_hw_end_of_support_location(self): """Test metric hw_end_of_support_location_gauge.""" - metric_gen = metrics_lcm_hw_end_of_support() - - # skip hw_end_of_support_part_number_gauge - next(metric_gen) + metric_gen = metrics_lcm_hw_end_of_support_location() # Get hw_end_of_support_location_gauge metric = next(metric_gen)