netbox-config-diff 2.1.0__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. {netbox-config-diff-2.1.0/netbox_config_diff.egg-info → netbox-config-diff-2.2.0}/PKG-INFO +10 -4
  2. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/README.md +8 -2
  3. netbox-config-diff-2.2.0/docs/media/screenshots/navbar.png +0 -0
  4. netbox-config-diff-2.2.0/docs/media/screenshots/script.png +0 -0
  5. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/__init__.py +1 -1
  6. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/compliance/base.py +18 -3
  7. netbox-config-diff-2.2.0/netbox_config_diff/migrations/0008_alter_configcompliance_device.py +21 -0
  8. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/models/data_models.py +4 -1
  9. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/models/models.py +1 -1
  10. netbox-config-diff-2.2.0/netbox_config_diff/navigation.py +47 -0
  11. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/configcompliance/config.html +1 -1
  12. netbox-config-diff-2.1.0/netbox_config_diff/templates/netbox_config_diff/configcompliance.html → netbox-config-diff-2.2.0/netbox_config_diff/templates/netbox_config_diff/configcompliance/data.html +8 -8
  13. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/configcompliance/missing_extra.html +1 -1
  14. netbox-config-diff-2.1.0/netbox_config_diff/templates/netbox_config_diff/configcompliance/base.html → netbox-config-diff-2.2.0/netbox_config_diff/templates/netbox_config_diff/configcompliance.html +1 -1
  15. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/views/compliance.py +41 -1
  16. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0/netbox_config_diff.egg-info}/PKG-INFO +10 -4
  17. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff.egg-info/SOURCES.txt +2 -1
  18. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/pyproject.toml +1 -1
  19. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/tests/conftest.py +1 -0
  20. netbox-config-diff-2.1.0/docs/media/screenshots/navbar.png +0 -0
  21. netbox-config-diff-2.1.0/docs/media/screenshots/script.png +0 -0
  22. netbox-config-diff-2.1.0/netbox_config_diff/navigation.py +0 -46
  23. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/LICENSE +0 -0
  24. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/MANIFEST.in +0 -0
  25. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/development/configuration.py +0 -0
  26. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/compliance-diff.png +0 -0
  27. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/compliance-error.png +0 -0
  28. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/compliance-list.png +0 -0
  29. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/compliance-missing-extra.png +0 -0
  30. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/compliance-ok.png +0 -0
  31. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/config-temp-substitute.png +0 -0
  32. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-approve-button.png +0 -0
  33. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-approved.png +0 -0
  34. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-collecting-diff-button.png +0 -0
  35. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-completed.png +0 -0
  36. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-created.png +0 -0
  37. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-diffs-tab.png +0 -0
  38. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-job-log.png +0 -0
  39. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-schedule-button.png +0 -0
  40. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-scheduled.png +0 -0
  41. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-unapprove-button.png +0 -0
  42. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/cr-unschedule-button.png +0 -0
  43. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/platformsetting.png +0 -0
  44. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/render-temp-substitute.png +0 -0
  45. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/script-list.png +0 -0
  46. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/docs/media/screenshots/substitute.png +0 -0
  47. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/api/__init__.py +0 -0
  48. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/api/serializers.py +0 -0
  49. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/api/urls.py +0 -0
  50. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/api/views.py +0 -0
  51. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/choices.py +0 -0
  52. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/compliance/__init__.py +0 -0
  53. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/compliance/secrets.py +0 -0
  54. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/compliance/utils.py +0 -0
  55. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/configurator/__init__.py +0 -0
  56. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/configurator/base.py +0 -0
  57. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/configurator/exceptions.py +0 -0
  58. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/configurator/factory.py +0 -0
  59. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/configurator/platforms.py +0 -0
  60. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/configurator/utils.py +0 -0
  61. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/constants.py +0 -0
  62. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/filtersets.py +0 -0
  63. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/forms.py +0 -0
  64. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/graphql.py +0 -0
  65. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/jobs.py +0 -0
  66. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/0001_initial.py +0 -0
  67. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/0002_add_script.py +0 -0
  68. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/0003_configcompliance_actual_config_and_more.py +0 -0
  69. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/0004_update_script.py +0 -0
  70. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/0005_configcompliance_extra_missing.py +0 -0
  71. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/0006_substitute.py +0 -0
  72. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/0007_configurationrequest.py +0 -0
  73. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/migrations/__init__.py +0 -0
  74. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/models/__init__.py +0 -0
  75. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/models/base.py +0 -0
  76. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/scripts/config_diff.py +0 -0
  77. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/search.py +0 -0
  78. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/static/netbox_config_diff/diff2html-ui.min.js +0 -0
  79. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/static/netbox_config_diff/diff2html.dark.min.css +0 -0
  80. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/static/netbox_config_diff/diff2html.min.css +0 -0
  81. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/tables.py +0 -0
  82. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/configurationrequest/base.html +0 -0
  83. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/configurationrequest/diffs.html +0 -0
  84. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/configurationrequest.html +0 -0
  85. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/inc/diff.html +0 -0
  86. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/inc/job_log.html +0 -0
  87. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/platformsetting.html +0 -0
  88. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/templates/netbox_config_diff/substitute.html +0 -0
  89. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/urls.py +0 -0
  90. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/views/__init__.py +0 -0
  91. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/views/base.py +0 -0
  92. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff/views/configuration.py +0 -0
  93. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff.egg-info/dependency_links.txt +0 -0
  94. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff.egg-info/requires.txt +0 -0
  95. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/netbox_config_diff.egg-info/top_level.txt +0 -0
  96. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/requirements/base.txt +0 -0
  97. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/requirements/dev.txt +0 -0
  98. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/requirements/test.txt +0 -0
  99. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/setup.cfg +0 -0
  100. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/tests/factories.py +0 -0
  101. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/tests/test_compliance.py +0 -0
  102. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/tests/test_compliance_utils.py +0 -0
  103. {netbox-config-diff-2.1.0 → netbox-config-diff-2.2.0}/tests/test_urls.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-config-diff
3
- Version: 2.1.0
4
- Summary: Find diff between the intended device configuration and actual.
3
+ Version: 2.2.0
4
+ Summary: Push rendered device configurations from NetBox to devices and apply them.
5
5
  Author: Artem Kotik
6
6
  Author-email: miaow2@yandex.ru
7
7
  License: Apache License
@@ -328,13 +328,19 @@ python manage.py collectstatic --noinput
328
328
  Restart NetBox service:
329
329
 
330
330
  ```bash
331
- systemctl restart netbox
331
+ systemctl restart netbox netbox-rq
332
332
  ```
333
333
  <!--install-end-->
334
334
  <!--usage-start-->
335
335
  ## Usage
336
336
 
337
- Read this [doc](docs/colliecting-diffs.md) about collecting diffs, for configuration management read [this](docs/configuratiom-management.md)
337
+ Read this [doc](https://miaow2.github.io/netbox-config-diff/colliecting-diffs/) about collecting diffs, for configuration management read [this](https://miaow2.github.io/netbox-config-diff/configuratiom-management/)
338
+
339
+ ## Video
340
+
341
+ My presention about plugin at October NetBox community call (19.10.2023).
342
+
343
+ [![October NetBox community call](https://img.youtube.com/vi/B4uhtYh278o/0.jpg)](https://youtu.be/B4uhtYh278o?t=425)
338
344
  <!--usage-end-->
339
345
 
340
346
  ## Screenshots
@@ -97,13 +97,19 @@ python manage.py collectstatic --noinput
97
97
  Restart NetBox service:
98
98
 
99
99
  ```bash
100
- systemctl restart netbox
100
+ systemctl restart netbox netbox-rq
101
101
  ```
102
102
  <!--install-end-->
103
103
  <!--usage-start-->
104
104
  ## Usage
105
105
 
106
- Read this [doc](docs/colliecting-diffs.md) about collecting diffs, for configuration management read [this](docs/configuratiom-management.md)
106
+ Read this [doc](https://miaow2.github.io/netbox-config-diff/colliecting-diffs/) about collecting diffs, for configuration management read [this](https://miaow2.github.io/netbox-config-diff/configuratiom-management/)
107
+
108
+ ## Video
109
+
110
+ My presention about plugin at October NetBox community call (19.10.2023).
111
+
112
+ [![October NetBox community call](https://img.youtube.com/vi/B4uhtYh278o/0.jpg)](https://youtu.be/B4uhtYh278o?t=425)
107
113
  <!--usage-end-->
108
114
 
109
115
  ## Screenshots
@@ -2,7 +2,7 @@ from extras.plugins import PluginConfig
2
2
 
3
3
  __author__ = "Artem Kotik"
4
4
  __email__ = "miaow2@yandex.ru"
5
- __version__ = "2.1.0"
5
+ __version__ = "2.2.0"
6
6
 
7
7
 
8
8
  class ConfigDiffConfig(PluginConfig):
@@ -9,10 +9,11 @@ from dcim.choices import DeviceStatusChoices
9
9
  from dcim.models import Device, DeviceRole, Site
10
10
  from django.conf import settings
11
11
  from django.db.models import Q
12
- from extras.scripts import MultiObjectVar, ObjectVar
12
+ from extras.scripts import MultiObjectVar, ObjectVar, TextVar
13
13
  from jinja2.exceptions import TemplateError
14
14
  from netutils.config.compliance import diff_network_config
15
15
  from utilities.exceptions import AbortScript
16
+ from utilities.utils import render_jinja2
16
17
 
17
18
  from netbox_config_diff.models import ConplianceDeviceDataClass
18
19
 
@@ -52,6 +53,11 @@ class ConfigDiffBase(SecretsMixin):
52
53
  },
53
54
  description="Define synced DataSource, if you want compare configs stored in it wihout connecting to devices",
54
55
  )
56
+ name_template = TextVar(
57
+ required=False,
58
+ description="Jinja2 template code for the device name in Data source. "
59
+ "Reference the object as <code>{{ object }}</code>.",
60
+ )
55
61
 
56
62
  def run_script(self, data: dict) -> None:
57
63
  devices = self.validate_data(data)
@@ -155,17 +161,26 @@ class ConfigDiffBase(SecretsMixin):
155
161
  auth_secondary=auth_secondary,
156
162
  rendered_config=rendered_config,
157
163
  error=error,
164
+ device=device,
158
165
  )
159
166
 
160
167
  def get_config_from_datasource(self, devices: list[ConplianceDeviceDataClass]) -> None:
161
168
  for device in devices:
162
- if df := DataFile.objects.filter(source=self.data["data_source"], path__icontains=device.name).first():
169
+ if self.data["name_template"]:
170
+ try:
171
+ device_name = render_jinja2(self.data["name_template"], {"object": device.device}).strip()
172
+ except Exception as e:
173
+ self.log_failure(f"Error in rendering data source name for {device.name}: {e}, using device name.")
174
+ device_name = device.name
175
+ else:
176
+ device_name = device.name
177
+ if df := DataFile.objects.filter(source=self.data["data_source"], path__icontains=device_name).first():
163
178
  if config := df.data_as_string:
164
179
  device.actual_config = config
165
180
  else:
166
181
  device.error = f"Data in file {df} is broken, skiping device {device.name}"
167
182
  else:
168
- device.error = f"Not found file in DataSource for device {device.name}"
183
+ device.error = f"Not found file in DataSource for name {device_name}"
169
184
 
170
185
  def get_actual_configs(self, devices: list[ConplianceDeviceDataClass]) -> None:
171
186
  if self.data["data_source"]:
@@ -0,0 +1,21 @@
1
+ from django.db import migrations, models
2
+ import django.db.models.deletion
3
+
4
+
5
+ class Migration(migrations.Migration):
6
+ dependencies = [
7
+ ("dcim", "0181_rename_device_role_device_role"),
8
+ ("netbox_config_diff", "0007_configurationrequest"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AlterField(
13
+ model_name="configcompliance",
14
+ name="device",
15
+ field=models.OneToOneField(
16
+ on_delete=django.db.models.deletion.CASCADE,
17
+ related_name="config_compliance",
18
+ to="dcim.device",
19
+ ),
20
+ ),
21
+ ]
@@ -1,6 +1,7 @@
1
1
  import traceback
2
2
  from dataclasses import dataclass
3
3
 
4
+ from dcim.models import Device
4
5
  from scrapli import AsyncScrapli
5
6
 
6
7
  from netbox_config_diff.choices import ConfigComplianceStatusChoices
@@ -113,10 +114,12 @@ class BaseDeviceDataClass:
113
114
 
114
115
  class ConplianceDeviceDataClass(BaseDeviceDataClass):
115
116
  command: str
117
+ device: Device | None = None
116
118
 
117
- def __init__(self, command: str, **kwargs) -> None:
119
+ def __init__(self, command: str, device: Device, **kwargs) -> None:
118
120
  super().__init__(**kwargs)
119
121
  self.command = command
122
+ self.device = device
120
123
 
121
124
  async def get_actual_config(self) -> None:
122
125
  if self.error is not None:
@@ -24,7 +24,7 @@ class ConfigCompliance(AbsoluteURLMixin, ChangeLoggingMixin, models.Model):
24
24
  device = models.OneToOneField(
25
25
  to="dcim.Device",
26
26
  on_delete=models.CASCADE,
27
- related_name="config_compliamce",
27
+ related_name="config_compliance",
28
28
  )
29
29
  status = models.CharField(
30
30
  max_length=50,
@@ -0,0 +1,47 @@
1
+ from extras.plugins import PluginMenu, PluginMenuButton, PluginMenuItem
2
+ from utilities.choices import ButtonColorChoices
3
+
4
+
5
+ def get_add_button(model: str) -> PluginMenuButton:
6
+ return PluginMenuButton(
7
+ link=f"plugins:netbox_config_diff:{model}_add",
8
+ title="Add",
9
+ icon_class="mdi mdi-plus-thick",
10
+ color=ButtonColorChoices.GREEN,
11
+ permissions=[f"netbox_config_diff.add_{model}"],
12
+ )
13
+
14
+
15
+ def get_menu_item(model: str, verbose_name: str, add_button: bool = True) -> PluginMenuItem:
16
+ return PluginMenuItem(
17
+ link=f"plugins:netbox_config_diff:{model}_list",
18
+ link_text=verbose_name,
19
+ buttons=[get_add_button(model)] if add_button else [],
20
+ permissions=[f"netbox_config_diff.view_{model}"],
21
+ )
22
+
23
+
24
+ compliance_items = (
25
+ get_menu_item("platformsetting", "Platform Settings"),
26
+ get_menu_item("configcompliance", "Config Compliances", add_button=False),
27
+ )
28
+
29
+ config_items = (
30
+ get_menu_item("configurationrequest", "Configuration Requests"),
31
+ get_menu_item("substitute", "Substitutes"),
32
+ PluginMenuItem(
33
+ link="plugins:netbox_config_diff:configurationrequest_job_list",
34
+ link_text="Jobs",
35
+ buttons=[],
36
+ permissions=["core.view_job"],
37
+ ),
38
+ )
39
+
40
+ menu = PluginMenu(
41
+ label="Config Diff Plugin",
42
+ groups=(
43
+ ("Compliance", compliance_items),
44
+ ("Config Management", config_items),
45
+ ),
46
+ icon_class="mdi mdi-vector-difference",
47
+ )
@@ -1,4 +1,4 @@
1
- {% extends "netbox_config_diff/configcompliance/base.html" %}
1
+ {% extends "netbox_config_diff/configcompliance.html" %}
2
2
 
3
3
  {% block title %}{{ object }} - {{ header }}{% endblock %}
4
4
 
@@ -1,37 +1,37 @@
1
- {% extends "netbox_config_diff/configcompliance/base.html" %}
1
+ {% extends base_template %}
2
2
  {% load static %}
3
3
 
4
4
  {% block content %}
5
5
  <div class="row mb-3">
6
6
  <div class="col col-md-6">
7
7
  <div class="card">
8
- <h5 class="card-header">{{ object|meta:"verbose_name"|bettertitle }}</h5>
8
+ <h5 class="card-header">{{ instance|meta:"verbose_name"|bettertitle }}</h5>
9
9
  <div class="card-body">
10
10
  <table class="table table-hover attr-table">
11
11
  <tr>
12
12
  <th scope="row">Device</th>
13
- <td>{{ object.device|linkify }}</td>
13
+ <td>{{ instance.device|linkify }}</td>
14
14
  </tr>
15
15
  <tr>
16
16
  <th scope="row">Status</th>
17
- <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
17
+ <td>{% badge instance.get_status_display bg_color=instance.get_status_color %}</td>
18
18
  </tr>
19
19
  </table>
20
20
  </div>
21
21
  </div>
22
22
  </div>
23
- {% if object.error %}
23
+ {% if instance.error %}
24
24
  <div class="col col-md-6">
25
25
  <div class="card">
26
26
  <h5 class="card-header">Error</h5>
27
27
  <div class="card-body">
28
- <pre class="block">{{ object.error }}</pre>
28
+ <pre class="block">{{ instance.error }}</pre>
29
29
  </div>
30
30
  </div>
31
31
  </div>
32
32
  {% endif %}
33
33
  </div>
34
- {% if object.diff %}
34
+ {% if instance.diff %}
35
35
  <div class="row mb-3">
36
36
  <div class="col col-md-12">
37
37
  <div class="card">
@@ -70,7 +70,7 @@
70
70
  stickyFileHeaders: false,
71
71
  drawFileList: false,
72
72
  };
73
- const jsonDiff = `{{ object.diff|safe }}`;
73
+ const jsonDiff = `{{ instance.diff|safe }}`;
74
74
  var targetElement = document.getElementById('diffElement');
75
75
  var diff2htmlUi = new Diff2HtmlUI(targetElement, jsonDiff, configuration);
76
76
  diff2htmlUi.draw();
@@ -1,4 +1,4 @@
1
- {% extends "netbox_config_diff/configcompliance/base.html" %}
1
+ {% extends "netbox_config_diff/configcompliance.html" %}
2
2
 
3
3
  {% block title %}{{ object }} - Missing/Extra{% endblock %}
4
4
 
@@ -1,5 +1,6 @@
1
+ from dcim.models import Device
1
2
  from django.http import HttpResponse
2
- from django.shortcuts import render
3
+ from django.shortcuts import redirect, render
3
4
  from django.utils.translation import gettext as _
4
5
  from netbox.views import generic
5
6
  from utilities.views import ViewTab, register_model_view
@@ -51,6 +52,14 @@ class BaseConfigComplianceConfigView(generic.ObjectView):
51
52
  @register_model_view(ConfigCompliance)
52
53
  class ConfigComplianceView(generic.ObjectView):
53
54
  queryset = ConfigCompliance.objects.all()
55
+ base_template = "netbox_config_diff/configcompliance.html"
56
+ template_name = "netbox_config_diff/configcompliance/data.html"
57
+
58
+ def get_extra_context(self, request, instance):
59
+ return {
60
+ "instance": instance,
61
+ "base_template": self.base_template,
62
+ }
54
63
 
55
64
 
56
65
  @register_model_view(ConfigCompliance, "rendered-config")
@@ -113,6 +122,37 @@ class ConfigComplianceMissingExtraConfigView(generic.ObjectView):
113
122
  )
114
123
 
115
124
 
125
+ @register_model_view(Device, "config_compliance", "config-compliance")
126
+ class ConfigComplianceDeviceView(generic.ObjectView):
127
+ queryset = Device.objects.all()
128
+ base_template = "dcim/device/base.html"
129
+ template_name = "netbox_config_diff/configcompliance/data.html"
130
+ tab = ViewTab(
131
+ label=_("Config Compliance"),
132
+ weight=2110,
133
+ badge=lambda obj: 1 if hasattr(obj, "config_compliance") else 0,
134
+ hide_if_empty=True,
135
+ )
136
+
137
+ def get(self, request, **kwargs):
138
+ instance = self.get_object(**kwargs)
139
+
140
+ if not hasattr(instance, "config_compliance"):
141
+ return redirect("dcim:device", pk=instance.pk)
142
+
143
+ return render(
144
+ request,
145
+ self.get_template_name(),
146
+ {
147
+ "object": instance,
148
+ "instance": instance.config_compliance,
149
+ "tab": self.tab,
150
+ "base_template": self.base_template,
151
+ **self.get_extra_context(request, instance),
152
+ },
153
+ )
154
+
155
+
116
156
  class ConfigComplianceListView(generic.ObjectListView):
117
157
  queryset = ConfigCompliance.objects.prefetch_related("device")
118
158
  filterset = ConfigComplianceFilterSet
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-config-diff
3
- Version: 2.1.0
4
- Summary: Find diff between the intended device configuration and actual.
3
+ Version: 2.2.0
4
+ Summary: Push rendered device configurations from NetBox to devices and apply them.
5
5
  Author: Artem Kotik
6
6
  Author-email: miaow2@yandex.ru
7
7
  License: Apache License
@@ -328,13 +328,19 @@ python manage.py collectstatic --noinput
328
328
  Restart NetBox service:
329
329
 
330
330
  ```bash
331
- systemctl restart netbox
331
+ systemctl restart netbox netbox-rq
332
332
  ```
333
333
  <!--install-end-->
334
334
  <!--usage-start-->
335
335
  ## Usage
336
336
 
337
- Read this [doc](docs/colliecting-diffs.md) about collecting diffs, for configuration management read [this](docs/configuratiom-management.md)
337
+ Read this [doc](https://miaow2.github.io/netbox-config-diff/colliecting-diffs/) about collecting diffs, for configuration management read [this](https://miaow2.github.io/netbox-config-diff/configuratiom-management/)
338
+
339
+ ## Video
340
+
341
+ My presention about plugin at October NetBox community call (19.10.2023).
342
+
343
+ [![October NetBox community call](https://img.youtube.com/vi/B4uhtYh278o/0.jpg)](https://youtu.be/B4uhtYh278o?t=425)
338
344
  <!--usage-end-->
339
345
 
340
346
  ## Screenshots
@@ -63,6 +63,7 @@ netbox_config_diff/migrations/0004_update_script.py
63
63
  netbox_config_diff/migrations/0005_configcompliance_extra_missing.py
64
64
  netbox_config_diff/migrations/0006_substitute.py
65
65
  netbox_config_diff/migrations/0007_configurationrequest.py
66
+ netbox_config_diff/migrations/0008_alter_configcompliance_device.py
66
67
  netbox_config_diff/migrations/__init__.py
67
68
  netbox_config_diff/models/__init__.py
68
69
  netbox_config_diff/models/base.py
@@ -76,8 +77,8 @@ netbox_config_diff/templates/netbox_config_diff/configcompliance.html
76
77
  netbox_config_diff/templates/netbox_config_diff/configurationrequest.html
77
78
  netbox_config_diff/templates/netbox_config_diff/platformsetting.html
78
79
  netbox_config_diff/templates/netbox_config_diff/substitute.html
79
- netbox_config_diff/templates/netbox_config_diff/configcompliance/base.html
80
80
  netbox_config_diff/templates/netbox_config_diff/configcompliance/config.html
81
+ netbox_config_diff/templates/netbox_config_diff/configcompliance/data.html
81
82
  netbox_config_diff/templates/netbox_config_diff/configcompliance/missing_extra.html
82
83
  netbox_config_diff/templates/netbox_config_diff/configurationrequest/base.html
83
84
  netbox_config_diff/templates/netbox_config_diff/configurationrequest/diffs.html
@@ -7,7 +7,7 @@ requires = [
7
7
 
8
8
  [project]
9
9
  name = "netbox-config-diff"
10
- description = "Find diff between the intended device configuration and actual."
10
+ description = "Push rendered device configurations from NetBox to devices and apply them."
11
11
  readme = "README.md"
12
12
  keywords = [
13
13
  "netbox",
@@ -90,6 +90,7 @@ def devicedataclass_factory() -> "DeviceDataClassDataFactory":
90
90
  "password": faker.password(),
91
91
  "auth_strict_key": False,
92
92
  "transport": "asyncssh",
93
+ "device": None,
93
94
  }
94
95
  return data | fields
95
96
 
@@ -1,46 +0,0 @@
1
- from extras.plugins import PluginMenuButton, PluginMenuItem
2
- from utilities.choices import ButtonColorChoices
3
-
4
-
5
- def get_add_button(model: str) -> PluginMenuButton:
6
- return PluginMenuButton(
7
- link=f"plugins:netbox_config_diff:{model}_add",
8
- title="Add",
9
- icon_class="mdi mdi-plus-thick",
10
- color=ButtonColorChoices.GREEN,
11
- permissions=[f"netbox_config_diff.add_{model}"],
12
- )
13
-
14
-
15
- menu_items = (
16
- PluginMenuItem(
17
- link="plugins:netbox_config_diff:platformsetting_list",
18
- link_text="Platform Settings",
19
- buttons=[get_add_button("platformsetting")],
20
- permissions=["netbox_config_diff.view_platformsetting"],
21
- ),
22
- PluginMenuItem(
23
- link="plugins:netbox_config_diff:configcompliance_list",
24
- link_text="Config Compliances",
25
- buttons=[],
26
- permissions=["netbox_config_diff.view_configcompliance"],
27
- ),
28
- PluginMenuItem(
29
- link="plugins:netbox_config_diff:configurationrequest_list",
30
- link_text="Configuration Requests",
31
- buttons=[get_add_button("configurationrequest")],
32
- permissions=["netbox_config_diff.view_configurationrequest"],
33
- ),
34
- PluginMenuItem(
35
- link="plugins:netbox_config_diff:configurationrequest_job_list",
36
- link_text="Jobs",
37
- buttons=[],
38
- permissions=["core.view_job"],
39
- ),
40
- PluginMenuItem(
41
- link="plugins:netbox_config_diff:substitute_list",
42
- link_text="Substitutes",
43
- buttons=[get_add_button("substitute")],
44
- permissions=["netbox_config_diff.view_substitute"],
45
- ),
46
- )