Skip to content

Commit d00fdf5

Browse files
authored
Version 2.3.0 (#61)
* 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 * Handle junipers templates with set commands (#58) * Reverse columns in diff (#59) * Add python 3.12 and Netbox 3.7.5 in CI * Add warning about using set commands in Juniper * Version 2.3.0
1 parent fc45a92 commit d00fdf5

File tree

8 files changed

+83
-10
lines changed

8 files changed

+83
-10
lines changed

.github/workflows/commit.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
strategy:
99
max-parallel: 10
1010
matrix:
11-
python: ["3.10", "3.11"]
11+
python: ["3.10", "3.11", "3.12"]
1212
steps:
1313
- name: Checkout
1414
uses: actions/checkout@v3
@@ -31,7 +31,7 @@ jobs:
3131
strategy:
3232
max-parallel: 10
3333
matrix:
34-
netbox_version: ["v3.5.9", "v3.6.9"]
34+
netbox_version: ["v3.5.9", "v3.6.9", "v3.7.5"]
3535
steps:
3636
- name: Checkout
3737
uses: actions/checkout@v3

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ This is possible thanks to the scrapli_cfg. Read [Scrapli](https://github.com/sc
4444

4545
| NetBox Version | Plugin Version |
4646
|----------------|----------------|
47-
| 3.5, 3.6 | =>0.1.0 |
47+
| 3.5, 3.6, 3.7 | =>0.1.0 |
4848

4949
<!--install-start-->
5050
## Installing

docs/configuratiom-management.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Supported platforms:
1212

1313
Plugin using [scrapli-cfg](https://github.com/scrapli/scrapli_cfg) for this feature.
1414

15+
!!! warning
16+
If you use Juniper and render config in set commands, please read next info.
17+
Plugin uses `load override` command to load config to a device, set commands load with `load set`.
18+
With `load set` commnad you can't replace all config, because this command uses `merge` action.
19+
So, please, be careful when using set commands in rendering config and pushig it with plugin, it can have unexpected side effects.
20+
1521
## Substitutes
1622

1723
If you render not full configuration, it is acceptable to pull missing config sections from the actual configuration to render full configuration.

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.2.0"
5+
__version__ = "2.3.0"
66

77

88
class ConfigDiffConfig(PluginConfig):

netbox_config_diff/compliance/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def __init__(self, choices, *args, **kwargs):
3030

3131
def get_unified_diff(rendered_config: str, actual_config: str, device: str) -> str:
3232
diff = unified_diff(
33-
rendered_config.strip().splitlines(),
3433
actual_config.splitlines(),
34+
rendered_config.strip().splitlines(),
3535
fromfiledate=device,
3636
tofiledate=device,
3737
lineterm="",

netbox_config_diff/configurator/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async def _collect_one_diff(self, device: ConfiguratorDeviceDataClass) -> None:
126126
)
127127
device.rendered_config = rendered_config
128128
else:
129-
actual_config = await conn.get_config()
129+
actual_config = await conn.get_config(config_template=device.rendered_config)
130130
device.actual_config = conn.clean_config(actual_config.result)
131131

132132
device.diff = get_unified_diff(device.rendered_config, device.actual_config, device.name)

netbox_config_diff/configurator/platforms.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import re
2-
from typing import Pattern
2+
from typing import Any, Pattern
33

44
from scrapli_cfg.exceptions import TemplateError
55
from scrapli_cfg.platform.core.arista_eos import AsyncScrapliCfgEOS
@@ -94,13 +94,17 @@ async def render_substituted_config(
9494
"""
9595
self.logger.info("fetching configuration and replacing with provided substitutes")
9696

97-
source_config = await self.get_config(source=source)
97+
source_config = await self.get_config(config_template=config_template, source=source)
9898
return source_config, self._render_substituted_config(
9999
config_template=config_template,
100100
substitutes=substitutes,
101101
source_config=source_config.result,
102102
)
103103

104+
async def get_config(self, **kwargs) -> ScrapliCfgResponse:
105+
kwargs.pop("config_template", None)
106+
return await super().get_config(**kwargs)
107+
104108

105109
class CustomAsyncScrapliCfgEOS(CustomScrapliCfg, AsyncScrapliCfgEOS):
106110
pass
@@ -119,4 +123,67 @@ class CustomAsyncScrapliCfgNXOS(CustomScrapliCfg, AsyncScrapliCfgNXOS):
119123

120124

121125
class CustomAsyncScrapliCfgJunos(CustomScrapliCfg, AsyncScrapliCfgJunos):
122-
pass
126+
is_set_config = False
127+
128+
async def get_config(self, config_template: str, source: str = "running") -> ScrapliCfgResponse:
129+
response = self._pre_get_config(source=source)
130+
131+
command = "show configuration"
132+
if re.findall(r"^set\s+", config_template, flags=re.I | re.M):
133+
self.is_set_config = True
134+
command += " | display set"
135+
136+
if self._in_configuration_session is True:
137+
config_result = await self.conn.send_config(config=f"run {command}")
138+
else:
139+
config_result = await self.conn.send_command(command=command)
140+
141+
return self._post_get_config(
142+
response=response,
143+
source=source,
144+
scrapli_responses=[config_result],
145+
result=config_result.result,
146+
)
147+
148+
async def load_config(self, config: str, replace: bool = False, **kwargs: Any) -> ScrapliCfgResponse:
149+
"""
150+
Load configuration to a device
151+
152+
Supported kwargs:
153+
set: bool indicating config is a "set" style config (ignored if replace is True)
154+
155+
Args:
156+
config: string of the configuration to load
157+
replace: replace the configuration or not, if false configuration will be loaded as a
158+
merge operation
159+
kwargs: additional kwargs that the implementing classes may need for their platform,
160+
see above for junos supported kwargs
161+
162+
Returns:
163+
ScrapliCfgResponse: response object
164+
165+
Raises:
166+
N/A
167+
168+
"""
169+
response = self._pre_load_config(config=config)
170+
171+
config = self._prepare_load_config(config=config, replace=replace)
172+
173+
config_result = await self.conn.send_config(config=config, privilege_level="root_shell")
174+
175+
if self.is_set_config is True:
176+
load_config = f"load set {self.filesystem}{self.candidate_config_filename}"
177+
else:
178+
if self._replace is True:
179+
load_config = f"load override {self.filesystem}{self.candidate_config_filename}"
180+
else:
181+
load_config = f"load merge {self.filesystem}{self.candidate_config_filename}"
182+
183+
load_result = await self.conn.send_config(config=load_config)
184+
self._in_configuration_session = True
185+
186+
return self._post_load_config(
187+
response=response,
188+
scrapli_responses=[config_result, load_result],
189+
)

tests/test_compliance_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,4 @@ def test_exclude_lines(regex: str, expected: str) -> None:
5656
ids=["diff", "no diff"],
5757
)
5858
def test_get_unified_diff(render: str, actual: str, expected: str) -> None:
59-
assert get_unified_diff(render, actual, "test-1") == expected
59+
assert get_unified_diff(actual, render, "test-1") == expected

0 commit comments

Comments
 (0)