Skip to content

Commit a757ac7

Browse files
authored
Handle junipers templates with set commands (#58)
1 parent 79c3ec3 commit a757ac7

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

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+
)

0 commit comments

Comments
 (0)