Skip to content

Commit 53a08b7

Browse files
committed
updated watchdog handling and nfcli commands
1 parent 89f3c48 commit 53a08b7

File tree

52 files changed

+1285
-1787
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1285
-1787
lines changed

docs/norfab_changelog.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
# 0.16.5
2+
3+
## BUGS
4+
5+
1. Fixing Nornir N2G diagram calling UUID handling
6+
7+
## CHANGES
8+
9+
1. Splitting Nornir parse task into 3 tasks `parse_ttp`, `parse_napalm`, `parse_textfsm`
10+
2. Enhancing nfcli shell `nornir parse` commands
11+
3. Watchdog thread now starts automatically for every worker, so far implemented RAM usage monitoring
12+
13+
## FEATURES
14+
15+
1. Nornir service nfcli added `nornir parse textfsm` command
16+
2. Nfcli - adding these commands:
17+
18+
- `show workers statistics`
19+
- `show workers brief`
20+
21+
---
22+
123
# 0.16.4
224

325
## BREAKING CHANGES

docs/workers/nornir/services_nornir_service_tasks_parse.md

Lines changed: 123 additions & 50 deletions
Large diffs are not rendered by default.

norfab/clients/shell_clients/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def listen_events_thread(uuid, stop, NFCLIENT):
3636
start_time = time.time()
3737
time_fmt = "%d-%b-%Y %H:%M:%S.%f"
3838
richconsole.print(
39-
"-" * 45 + " Job Events " + "-" * 47 + "\n"
39+
"-" * 45 + " Job Events " + "-" * 47 + "\n\n"
4040
f"{datetime.now().strftime(time_fmt)[:-3]} {uuid} job started"
4141
)
4242
while not (stop.is_set() or NFCLIENT.exit_event.is_set()):
@@ -54,7 +54,7 @@ def listen_events_thread(uuid, stop, NFCLIENT):
5454
status,
5555
data,
5656
) = event
57-
if job_uuid != uuid.encode("utf-8"):
57+
if not job_uuid.decode("utf-8").startswith(uuid):
5858
NFCLIENT.event_queue.put(event)
5959
continue
6060

norfab/clients/shell_clients/nornir/nornir_picle_shell_cli.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,13 @@ class PicleConfig:
247247

248248
class NrCliPlugins(BaseModel):
249249
netmiko: NrCliPluginNetmiko = Field(
250-
None, description="Use Netmiko plugin to configure devices"
250+
None, description="Use Netmiko plugin to collect output from devices"
251251
)
252252
scrapli: NrCliPluginScrapli = Field(
253-
None, description="Use Scrapli plugin to configure devices"
253+
None, description="Use Scrapli plugin to collect output from devices"
254254
)
255255
napalm: NrCliPluginNapalm = Field(
256-
None, description="Use NAPALM plugin to configure devices"
256+
None, description="Use NAPALM plugin to collect output from devices"
257257
)
258258

259259

norfab/clients/shell_clients/nornir/nornir_picle_shell_diagram.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ def run(uuid, *args, **kwargs):
286286
workers = kwargs.pop("workers", "all")
287287
timeout = kwargs.pop("timeout", 600)
288288
_ = kwargs.pop("verbose_result", False)
289-
nowait = kwargs.pop("nowait", False)
289+
nowait = kwargs.pop("nowait")
290290
ctime = time.strftime("%Y-%m-%d_%H-%M-%S")
291291
FM = kwargs.pop("FM", [])
292292
n2g_data = {} # to store collected from devices data
@@ -327,7 +327,7 @@ def run(uuid, *args, **kwargs):
327327
)
328328

329329
# retrieve output on a per-platform basis
330-
for platform in platforms:
330+
for index, platform in enumerate(platforms):
331331
n2g_data.setdefault(platform, [])
332332
cli_kwargs = copy.deepcopy(kwargs)
333333
cli_kwargs["FM"] = [platform]
@@ -348,14 +348,11 @@ def run(uuid, *args, **kwargs):
348348
"cli",
349349
workers=workers,
350350
kwargs=cli_kwargs,
351-
uuid=uuid,
351+
uuid=f"{uuid}-{index}",
352352
timeout=timeout,
353-
nowait=nowait,
353+
nowait=False,
354354
)
355355

356-
if nowait:
357-
return job_results, Outputters.outputter_nested
358-
359356
# populate n2g data dictionary keyed by platform and save results to files
360357
for worker, results in job_results.items():
361358
if results["failed"]:

norfab/clients/shell_clients/nornir/nornir_picle_shell_parse.py

Lines changed: 134 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
import builtins
22
from enum import Enum
3-
from typing import List, Union
3+
from typing import List, Union, Any
4+
5+
try:
6+
from ttp_templates import list_templates_refs, list_templates
7+
8+
HAS_TTP_TEMPLATES = True
9+
except Exception as e:
10+
HAS_TTP_TEMPLATES = False
411

512
from picle.models import Outputters, PipeFunctionsModel
6-
from pydantic import (
7-
BaseModel,
8-
Field,
9-
StrictStr,
10-
)
13+
from pydantic import BaseModel, Field, StrictStr, StrictBool, model_validator
1114

1215
from ..common import ClientRunJobArgs, listen_events, log_error_or_result
1316
from .nornir_picle_shell_common import (
1417
NorniHostsFilters,
1518
NornirCommonArgs,
1619
)
20+
from .nornir_picle_shell_cli import (
21+
NrCliPluginNetmiko,
22+
NrCliPluginScrapli,
23+
NrCliPluginNapalm,
24+
)
25+
from norfab.workers.nornir_worker.parse_task import ParseTTPInput
1726

1827

1928
class NapalmGettersEnum(str, Enum):
@@ -49,7 +58,9 @@ class NapalmGettersEnum(str, Enum):
4958

5059

5160
class NapalmGettersModel(NorniHostsFilters, NornirCommonArgs, ClientRunJobArgs):
52-
getters: NapalmGettersEnum = Field(..., description="Select NAPALM getters")
61+
getters: Union[NapalmGettersEnum, List[NapalmGettersEnum]] = Field(
62+
..., description="Select NAPALM getters"
63+
)
5364

5465
@staticmethod
5566
@listen_events
@@ -62,10 +73,10 @@ def run(uuid, *args, **kwargs):
6273

6374
result = NFCLIENT.run_job(
6475
"nornir",
65-
"parse",
76+
"parse_napalm",
6677
workers=workers,
6778
args=args,
68-
kwargs={"plugin": "napalm", **kwargs},
79+
kwargs=kwargs,
6980
uuid=uuid,
7081
timeout=timeout,
7182
nowait=nowait,
@@ -80,16 +91,119 @@ class PicleConfig:
8091
outputter = Outputters.outputter_nested
8192

8293

83-
class TTPParseModel(NorniHostsFilters, NornirCommonArgs, ClientRunJobArgs):
84-
template: StrictStr = Field(
85-
..., description="TTP Template to parse commands output"
94+
class TTPStructureOptions(str, Enum):
95+
list_ = "list"
96+
dictionary = "dictionary"
97+
flat_list = "flat_list"
98+
99+
100+
class TTPParseNrCliPluginNetmiko(NrCliPluginNetmiko):
101+
@staticmethod
102+
def run(*args, **kwargs):
103+
kwargs["plugin"] = "netmiko"
104+
return TTPParseModel.run(*args, **kwargs)
105+
106+
107+
class TTPParseNrCliPluginScrapli(NrCliPluginScrapli):
108+
@staticmethod
109+
def run(*args, **kwargs):
110+
kwargs["plugin"] = "scrapli"
111+
return TTPParseModel.run(*args, **kwargs)
112+
113+
114+
class TTPParseNrCliPluginNapalm(NrCliPluginNapalm):
115+
@staticmethod
116+
def run(*args, **kwargs):
117+
kwargs["plugin"] = "napalm"
118+
return TTPParseModel.run(*args, **kwargs)
119+
120+
121+
class TTPParseNrCliPlugins(BaseModel):
122+
netmiko: TTPParseNrCliPluginNetmiko = Field(
123+
None, description="Use Netmiko plugin to collect output from devices"
124+
)
125+
scrapli: TTPParseNrCliPluginScrapli = Field(
126+
None, description="Use Scrapli plugin to collect output from devices"
127+
)
128+
napalm: TTPParseNrCliPluginNapalm = Field(
129+
None, description="Use NAPALM plugin to collect output from devices"
130+
)
131+
132+
133+
class TTPParseModel(
134+
NorniHostsFilters, NornirCommonArgs, ClientRunJobArgs, ParseTTPInput
135+
):
136+
commands: Union[StrictStr, List[StrictStr]] = Field(
137+
None,
138+
description="List of commands to collect form devices",
139+
json_schema_extra={"multiline": True},
140+
)
141+
plugin: TTPParseNrCliPlugins = Field(
142+
None, description="CLI connection plugin parameters"
86143
)
87-
commands: Union[List[StrictStr], StrictStr] = Field(
88-
None, description="Commands to collect form devices"
144+
enable: StrictBool = Field(
145+
None, description="Enter exec mode", json_schema_extra={"presence": True}
89146
)
147+
strict: StrictBool = Field(
148+
True,
149+
description="Strict mode, raise error on empty results",
150+
json_schema_extra={"presence": True},
151+
)
152+
153+
@staticmethod
154+
def source_template(choice):
155+
if choice and choice.startswith("nf://"):
156+
return ClientRunJobArgs.walk_norfab_files()
157+
elif choice and choice.startswith("ttp://") and HAS_TTP_TEMPLATES:
158+
return list_templates_refs()
159+
else:
160+
return ["nf://", "ttp://"]
90161

91162
@staticmethod
92-
def source_template():
163+
def source_get():
164+
if HAS_TTP_TEMPLATES:
165+
return [t.replace(".txt", "") for t in list_templates()["get"]]
166+
return []
167+
168+
@staticmethod
169+
@listen_events
170+
def run(uuid, *args, **kwargs):
171+
NFCLIENT = builtins.NFCLIENT
172+
workers = kwargs.pop("workers", "all")
173+
timeout = kwargs.pop("timeout", 600)
174+
verbose_result = kwargs.pop("verbose_result", False)
175+
nowait = kwargs.pop("nowait", False)
176+
177+
result = NFCLIENT.run_job(
178+
"nornir",
179+
"parse_ttp",
180+
workers=workers,
181+
args=args,
182+
kwargs=kwargs,
183+
uuid=uuid,
184+
timeout=timeout,
185+
nowait=nowait,
186+
)
187+
188+
if nowait:
189+
return result, Outputters.outputter_nested
190+
191+
return log_error_or_result(result, verbose_result=verbose_result)
192+
193+
class PicleConfig:
194+
outputter = Outputters.outputter_nested
195+
196+
197+
class TextFSMParseModel(NorniHostsFilters, NornirCommonArgs, ClientRunJobArgs):
198+
template: StrictStr = Field(None, description="Path to a TextFSM template file")
199+
commands: Union[StrictStr, List[StrictStr]] = Field(
200+
None,
201+
description="List of commands to parse form devices",
202+
json_schema_extra={"multiline": True},
203+
)
204+
205+
@staticmethod
206+
def source_template(choice):
93207
return ClientRunJobArgs.walk_norfab_files()
94208

95209
@staticmethod
@@ -103,10 +217,10 @@ def run(uuid, *args, **kwargs):
103217

104218
result = NFCLIENT.run_job(
105219
"nornir",
106-
"parse",
220+
"parse_textfsm",
107221
workers=workers,
108222
args=args,
109-
kwargs={"plugin": "ttp", **kwargs},
223+
kwargs=kwargs,
110224
uuid=uuid,
111225
timeout=timeout,
112226
nowait=nowait,
@@ -128,6 +242,9 @@ class NornirParseShell(BaseModel):
128242
ttp: TTPParseModel = Field(
129243
None, description="Parse devices output using TTP templates"
130244
)
245+
textfsm: TextFSMParseModel = Field(
246+
None, description="Parse devices output using TextFSM templates"
247+
)
131248

132249
class PicleConfig:
133250
subshell = True

0 commit comments

Comments
 (0)