diff --git a/.gitignore b/.gitignore index c8dc455..2bfbc13 100644 --- a/.gitignore +++ b/.gitignore @@ -190,50 +190,11 @@ $RECYCLE.BIN/ ### PyCharm ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr +.idea # CMake cmake-build-*/ -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - # File-based project format *.iws @@ -246,50 +207,18 @@ out/ # JIRA plugin atlassian-ide-plugin.xml -# Cursive Clojure plugin -.idea/replstate.xml - # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - ### PyCharm Patch ### # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 # *.iml # modules.xml # .idea/misc.xml -# *.ipr - -# Sonarlint plugin -# https://plugins.jetbrains.com/plugin/7973-sonarlint -.idea/**/sonarlint/ - -# SonarQube Plugin -# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin -.idea/**/sonarIssues.xml - -# Markdown Navigator plugin -# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced -.idea/**/markdown-navigator.xml -.idea/**/markdown-navigator-enh.xml -.idea/**/markdown-navigator/ - -# Cache file creation bug -# See https://youtrack.jetbrains.com/issue/JBR-2257 -.idea/$CACHE_FILE$ - -# CodeStream plugin -# https://plugins.jetbrains.com/plugin/12206-codestream -.idea/codestream.xml ### vscode ### .vscode/* diff --git a/development/Dockerfile b/development/Dockerfile index 5911fe5..fbcb7e6 100644 --- a/development/Dockerfile +++ b/development/Dockerfile @@ -50,6 +50,14 @@ RUN curl -sSL https://install.python-poetry.org | python3 - && \ # Copy in the source code WORKDIR /source + +# Copy in only pyproject.toml/poetry.lock to help with caching this layer if no updates to dependencies +COPY poetry.lock pyproject.toml /source/ +# --no-root declares not to install the project package since we're wanting to take advantage of caching dependency installation +# and the project is copied in and installed after this step +RUN poetry install --no-interaction --no-ansi --no-root + +# Copy in the rest of the source code and install local Nautobot plugin COPY . /source # Get container's installed Nautobot version as a forced constraint diff --git a/development/nautobot_config.py b/development/nautobot_config.py index 4765bf2..80cb216 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -249,7 +249,7 @@ NAPALM_ARGS = {} # Enable installed plugins. Add the name of each plugin to the list. -PLUGINS = ["nautobot_capacity_metrics"] +PLUGINS = ["nautobot_capacity_metrics", "nautobot_capacity_metrics.test_models"] # Plugins configuration settings. These settings are used by various plugins that the user may have installed. # Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. @@ -268,6 +268,7 @@ }, "ipam": {"IPAddress": True, "Prefix": True}, "extras": {"GitRepository": True}, + "test_models": {"_module": "nautobot_capacity_metrics", "TestModel": True}, }, "queues": True, "versions": { diff --git a/docs/index.md b/docs/index.md index d840638..a7267f5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -112,7 +112,7 @@ The behavior of the app_metrics feature can be controlled with the following lis - `gitrepositories` boolean (default **False**), publish stats about the gitrepositories (success, warning, info, failure) - `jobs` boolean (default **False**), publish stats about the jobs (success, warning, info, failure) - `queues` boolean (default **False**), publish stats about RQ Worker (nbr of worker, nbr and type of job in the different queues) -- `models` nested dict, publish the count for a given object (Nbr Device, Nbr IP etc.. ). The first level must be the name of the module in lowercase (dcim, ipam etc..), the second level must be the name of the object (usually starting with a uppercase) +- `models` nested dict, publish the count for a given object (Nbr Device, Nbr IP etc.. ). The first level must be the name of the module in lowercase (dcim, ipam etc..), the second level must be the name of the object (usually starting with a uppercase). Note that you can include models from plugins by specifying the module name under the "_module" key - `versions` nested dict, publish the versions of installed software ```python diff --git a/nautobot_capacity_metrics/metrics.py b/nautobot_capacity_metrics/metrics.py index e7d5b79..3a7d968 100644 --- a/nautobot_capacity_metrics/metrics.py +++ b/nautobot_capacity_metrics/metrics.py @@ -3,6 +3,7 @@ import importlib import platform from collections.abc import Iterable +from copy import deepcopy import django from packaging import version @@ -111,10 +112,12 @@ def metric_models(params): nautobot_model_count: with model name and application name as labels """ gauge = GaugeMetricFamily("nautobot_model_count", "Per Nautobot Model count", labels=["app", "name"]) - for app, _ in params.items(): - for model, _ in params[app].items(): + for app in params: + app_config = deepcopy(params[app]) # Avoid changing the dictionary we are iterating over + module = app_config.pop("_module", "nautobot") + for model in app_config: try: - models = importlib.import_module(f"nautobot.{app}.models") + models = importlib.import_module(f"{module}.{app}.models") model_class = getattr(models, model) gauge.add_metric([app, model], model_class.objects.count()) except ModuleNotFoundError: diff --git a/nautobot_capacity_metrics/test_models/__init__.py b/nautobot_capacity_metrics/test_models/__init__.py new file mode 100644 index 0000000..2ac96b2 --- /dev/null +++ b/nautobot_capacity_metrics/test_models/__init__.py @@ -0,0 +1,30 @@ +"""Plugin declaration for nautobot_capacity_metrics.test_models. + +Why is this here? In order to test the plugin model count metric mechanics, a plugin metric had to be introduced. +This was put into a separate app so that it is not installed whenever this plugin is installed, rather just when tests +are ran within this plugins development environment. +""" + +__version__ = "1.0.0" + +from nautobot.extras.plugins import PluginConfig + + +class TestConfig(PluginConfig): + """Plugin configuration for the nautobot_capacity_metrics plugin.""" + + name = "nautobot_capacity_metrics.test_models" + verbose_name = "Metrics & Monitoring Extension Test Model Plugin" + version = __version__ + author = "Network to Code, LLC" + author_email = "opensource@networktocode.com" + description = "Plugin that exists solely to test nautobot_capacity_metrics, don't install.." + base_url = "capacity-metrics-test" + required_settings = [] + min_version = "1.2.0" + max_version = "1.9999" + default_settings = {} + caching_config = {} + + +config = TestConfig # pylint:disable=invalid-name diff --git a/nautobot_capacity_metrics/test_models/migrations/0001_initial.py b/nautobot_capacity_metrics/test_models/migrations/0001_initial.py new file mode 100644 index 0000000..0f25156 --- /dev/null +++ b/nautobot_capacity_metrics/test_models/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.16 on 2022-12-20 13:24 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="TestModel", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True + ), + ), + ("name", models.CharField(max_length=20)), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/nautobot_capacity_metrics/test_models/migrations/__init__.py b/nautobot_capacity_metrics/test_models/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nautobot_capacity_metrics/test_models/models.py b/nautobot_capacity_metrics/test_models/models.py new file mode 100644 index 0000000..af3865c --- /dev/null +++ b/nautobot_capacity_metrics/test_models/models.py @@ -0,0 +1,10 @@ +"""Models for the testing plugin.""" + +from django.db.models import CharField +from nautobot.core.models import BaseModel + + +class TestModel(BaseModel): + """This is a model solely used for the testing of the capacity metrics plugin.""" + + name = CharField(max_length=20) diff --git a/nautobot_capacity_metrics/tests/test_endpoints.py b/nautobot_capacity_metrics/tests/test_endpoints.py index ccb76f1..62425bd 100644 --- a/nautobot_capacity_metrics/tests/test_endpoints.py +++ b/nautobot_capacity_metrics/tests/test_endpoints.py @@ -21,3 +21,9 @@ def test_endpoint(self): self.assertEqual(resp.status_code, status.HTTP_200_OK) resp = self.client.get(self.rq_metric_url) self.assertEqual(resp.status_code, status.HTTP_200_OK) + + def test_model_count_metrics(self): + """Ensure that the model count metrics work correctly.""" + resp = self.client.get(self.app_metric_url) + if "TestModel" not in resp.content.decode("utf-8"): + self.fail("nautobot_capacity_metrics.test_models.models.TestModel does not report its count.")