Skip to content

Commit fc45a92

Browse files
authored
Version 2.2.0 (#55)
* Change URLs in Usage to docs * Update project description * Add video presentation * Closes #50: Add template field to ConfigDiffScript (#51) * Add name template for searching in DataSource * Add docs for templating files name in DataSource * Closes #47: Move plugin to separete menu item in navbar (#52) * Move plugin into separate menu item * Add tab for devices with their config compliance * Closes #53: Add netbox-rq to installation process docs * Changelog for 2.2.0 version
1 parent 7492978 commit fc45a92

File tree

20 files changed

+234
-130
lines changed

20 files changed

+234
-130
lines changed

.github/workflows/commit.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
strategy:
3232
max-parallel: 10
3333
matrix:
34-
netbox_version: ["v3.5.9", "v3.6.4"]
34+
netbox_version: ["v3.5.9", "v3.6.9"]
3535
steps:
3636
- name: Checkout
3737
uses: actions/checkout@v3

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,19 @@ python manage.py collectstatic --noinput
9797
Restart NetBox service:
9898

9999
```bash
100-
systemctl restart netbox
100+
systemctl restart netbox netbox-rq
101101
```
102102
<!--install-end-->
103103
<!--usage-start-->
104104
## Usage
105105

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)
107113
<!--usage-end-->
108114

109115
## Screenshots

docs/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 2.2.0 (2024-02-06)
4+
5+
* [#47](https://github.com/miaow2/netbox-config-diff/issues/47) Move plugin to separete menu item in navbar and add tab for devices with compliance result
6+
* [#50](https://github.com/miaow2/netbox-config-diff/issues/50) Add template field for device name in DataSource to ConfigDiffScript
7+
* [#53](https://github.com/miaow2/netbox-config-diff/issues/53) Add netbox-rq to installation process docs
8+
39
## 2.1.0 (2023-10-26)
410

511
* [#35](https://github.com/miaow2/netbox-config-diff/issues/35) Add ability to define password for accessing priviliged exec mode

docs/colliecting-diffs.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# Usage
33

4-
Under `Plugins` navbar menu you can find plugin
4+
In navbar serach for `Config Diff Plugin` menu
55

66
![Screenshot of navbar](media/screenshots/navbar.png)
77

@@ -42,6 +42,17 @@ If you have configs in NetBox DataSource, you can define it, the script instead
4242
!!! note
4343
Only synced DataSources are acceptable
4444

45+
If in your DataSource config names are different from the hostnames of the devices, you can specify config name with Jinja2 template in `Name template` field.
46+
Reference device with `{{ object }}` variable.
47+
48+
For example, config name is virtual chassis name plus `config` (`switchname-config`) and your devices names are `switchname1`, `switchname2` and etc.
49+
50+
You can define Jinja2 template with logic to use virtual chassis name if device is in chassis, else use device name:
51+
52+
```
53+
{% if object.virtual_chassis %}{{ object.virtual_chassis.name }}-config{% else %}{{ object.name }}{% endif %}
54+
```
55+
4556
![Screenshot of the script](media/screenshots/script.png)
4657

4758
## Results

docs/media/screenshots/navbar.png

2.11 KB
Loading

docs/media/screenshots/script.png

1 KB
Loading

netbox_config_diff/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
__author__ = "Artem Kotik"
44
__email__ = "miaow2@yandex.ru"
5-
__version__ = "2.1.0"
5+
__version__ = "2.2.0"
66

77

88
class ConfigDiffConfig(PluginConfig):

netbox_config_diff/compliance/base.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
from dcim.models import Device, DeviceRole, Site
1010
from django.conf import settings
1111
from django.db.models import Q
12-
from extras.scripts import MultiObjectVar, ObjectVar
12+
from extras.scripts import MultiObjectVar, ObjectVar, TextVar
1313
from jinja2.exceptions import TemplateError
1414
from netutils.config.compliance import diff_network_config
1515
from utilities.exceptions import AbortScript
16+
from utilities.utils import render_jinja2
1617

1718
from netbox_config_diff.models import ConplianceDeviceDataClass
1819

@@ -52,6 +53,11 @@ class ConfigDiffBase(SecretsMixin):
5253
},
5354
description="Define synced DataSource, if you want compare configs stored in it wihout connecting to devices",
5455
)
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+
)
5561

5662
def run_script(self, data: dict) -> None:
5763
devices = self.validate_data(data)
@@ -155,17 +161,26 @@ def get_devices_with_rendered_configs(self, devices: Iterable[Device]) -> Iterat
155161
auth_secondary=auth_secondary,
156162
rendered_config=rendered_config,
157163
error=error,
164+
device=device,
158165
)
159166

160167
def get_config_from_datasource(self, devices: list[ConplianceDeviceDataClass]) -> None:
161168
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():
163178
if config := df.data_as_string:
164179
device.actual_config = config
165180
else:
166181
device.error = f"Data in file {df} is broken, skiping device {device.name}"
167182
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}"
169184

170185
def get_actual_configs(self, devices: list[ConplianceDeviceDataClass]) -> None:
171186
if self.data["data_source"]:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
]

netbox_config_diff/models/data_models.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import traceback
22
from dataclasses import dataclass
33

4+
from dcim.models import Device
45
from scrapli import AsyncScrapli
56

67
from netbox_config_diff.choices import ConfigComplianceStatusChoices
@@ -113,10 +114,12 @@ def send_to_db(self) -> None:
113114

114115
class ConplianceDeviceDataClass(BaseDeviceDataClass):
115116
command: str
117+
device: Device | None = None
116118

117-
def __init__(self, command: str, **kwargs) -> None:
119+
def __init__(self, command: str, device: Device, **kwargs) -> None:
118120
super().__init__(**kwargs)
119121
self.command = command
122+
self.device = device
120123

121124
async def get_actual_config(self) -> None:
122125
if self.error is not None:

0 commit comments

Comments
 (0)