scrapli 2.0.0a2__py3-none-musllinux_1_1_aarch64.whl
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.
- scrapli/__init__.py +30 -0
- scrapli/auth.py +216 -0
- scrapli/cli.py +1414 -0
- scrapli/cli_decorators.py +148 -0
- scrapli/cli_parse.py +161 -0
- scrapli/cli_result.py +197 -0
- scrapli/definitions/__init__.py +1 -0
- scrapli/definitions/arista_eos.yaml +64 -0
- scrapli/definitions/cisco_iosxe.yaml +63 -0
- scrapli/definitions/cisco_iosxr.yaml +47 -0
- scrapli/definitions/cisco_nxos.yaml +64 -0
- scrapli/definitions/juniper_junos.yaml +85 -0
- scrapli/definitions/nokia_srlinux.yaml +35 -0
- scrapli/exceptions.py +49 -0
- scrapli/ffi.py +76 -0
- scrapli/ffi_mapping.py +202 -0
- scrapli/ffi_mapping_cli.py +646 -0
- scrapli/ffi_mapping_netconf.py +1612 -0
- scrapli/ffi_mapping_options.py +1031 -0
- scrapli/ffi_types.py +154 -0
- scrapli/helper.py +95 -0
- scrapli/lib/__init__.py +1 -0
- scrapli/lib/libscrapli.0.0.1-alpha.10.dylib +0 -0
- scrapli/lib/libscrapli.so.0.0.1-alpha.10 +0 -0
- scrapli/netconf.py +2804 -0
- scrapli/netconf_decorators.py +148 -0
- scrapli/netconf_result.py +72 -0
- scrapli/py.typed +0 -0
- scrapli/session.py +122 -0
- scrapli/transport.py +401 -0
- scrapli-2.0.0a2.dist-info/METADATA +49 -0
- scrapli-2.0.0a2.dist-info/RECORD +35 -0
- scrapli-2.0.0a2.dist-info/WHEEL +5 -0
- scrapli-2.0.0a2.dist-info/licenses/LICENSE +21 -0
- scrapli-2.0.0a2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
"""scrapli.cli_decorators"""
|
2
|
+
|
3
|
+
from collections.abc import Awaitable, Callable
|
4
|
+
from ctypes import c_uint64
|
5
|
+
from functools import update_wrapper
|
6
|
+
from typing import TYPE_CHECKING, Concatenate, ParamSpec
|
7
|
+
|
8
|
+
from scrapli.cli_result import Result
|
9
|
+
from scrapli.exceptions import OptionsException
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from scrapli.cli import Cli
|
13
|
+
|
14
|
+
P = ParamSpec("P")
|
15
|
+
|
16
|
+
|
17
|
+
def handle_operation_timeout(
|
18
|
+
wrapped: Callable[Concatenate["Cli", P], Result],
|
19
|
+
) -> Callable[Concatenate["Cli", P], Result]:
|
20
|
+
"""
|
21
|
+
Wraps a Cli operation and sets the timeout value for the duration of the operation.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
wrapped: the operation function
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
callable: the wrapper function
|
28
|
+
|
29
|
+
Raises:
|
30
|
+
N/A
|
31
|
+
|
32
|
+
"""
|
33
|
+
|
34
|
+
def wrapper(inst: "Cli", /, *args: P.args, **kwargs: P.kwargs) -> Result:
|
35
|
+
"""
|
36
|
+
The operation timeout wrapper.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
inst: the Cli instance
|
40
|
+
args: the arguments to pass to the wrapped function
|
41
|
+
kwargs: the keyword arguments to pass to the wrapped function
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
Result: the result of the wrapped function
|
45
|
+
|
46
|
+
Raises:
|
47
|
+
OptionsException: if the operation timeout failed to set
|
48
|
+
|
49
|
+
"""
|
50
|
+
operation_timeout_ns = kwargs.get("operation_timeout_ns")
|
51
|
+
if operation_timeout_ns is None:
|
52
|
+
return wrapped(inst, *args, **kwargs)
|
53
|
+
|
54
|
+
if operation_timeout_ns == inst.session_options.operation_timeout_ns:
|
55
|
+
return wrapped(inst, *args, **kwargs)
|
56
|
+
|
57
|
+
if not isinstance(operation_timeout_ns, int):
|
58
|
+
# ignore an invalid type for the timeout
|
59
|
+
return wrapped(inst, *args, **kwargs)
|
60
|
+
|
61
|
+
status = inst.ffi_mapping.options_mapping.session.set_operation_timeout_ns(
|
62
|
+
inst._ptr_or_exception(),
|
63
|
+
c_uint64(operation_timeout_ns),
|
64
|
+
)
|
65
|
+
if status != 0:
|
66
|
+
raise OptionsException("failed to set session operation timeout")
|
67
|
+
|
68
|
+
res = wrapped(inst, *args, **kwargs)
|
69
|
+
|
70
|
+
status = inst.ffi_mapping.options_mapping.session.set_operation_timeout_ns(
|
71
|
+
inst._ptr_or_exception(),
|
72
|
+
c_uint64(operation_timeout_ns),
|
73
|
+
)
|
74
|
+
if status != 0:
|
75
|
+
raise OptionsException("failed to set session operation timeout")
|
76
|
+
|
77
|
+
return res
|
78
|
+
|
79
|
+
update_wrapper(wrapper=wrapper, wrapped=wrapped)
|
80
|
+
|
81
|
+
return wrapper
|
82
|
+
|
83
|
+
|
84
|
+
def handle_operation_timeout_async(
|
85
|
+
wrapped: Callable[Concatenate["Cli", P], Awaitable[Result]],
|
86
|
+
) -> Callable[Concatenate["Cli", P], Awaitable[Result]]:
|
87
|
+
"""
|
88
|
+
Wraps a Cli operation and sets the timeout value for the duration of the operation.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
wrapped: the operation function
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
callable: the wrapper function
|
95
|
+
|
96
|
+
Raises:
|
97
|
+
N/A
|
98
|
+
|
99
|
+
"""
|
100
|
+
|
101
|
+
async def wrapper(inst: "Cli", /, *args: P.args, **kwargs: P.kwargs) -> Result:
|
102
|
+
"""
|
103
|
+
The operation timeout wrapper.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
inst: the Cli instance
|
107
|
+
args: the arguments to pass to the wrapped function
|
108
|
+
kwargs: the keyword arguments to pass to the wrapped function
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
Result: the result of the wrapped function
|
112
|
+
|
113
|
+
Raises:
|
114
|
+
OptionsException: if the operation timeout failed to set
|
115
|
+
|
116
|
+
"""
|
117
|
+
operation_timeout_ns = kwargs.get("operation_timeout_ns")
|
118
|
+
if operation_timeout_ns is None:
|
119
|
+
return await wrapped(inst, *args, **kwargs)
|
120
|
+
|
121
|
+
if operation_timeout_ns == inst.session_options.operation_timeout_ns:
|
122
|
+
return await wrapped(inst, *args, **kwargs)
|
123
|
+
|
124
|
+
if not isinstance(operation_timeout_ns, int):
|
125
|
+
# ignore an invalid type for the timeout
|
126
|
+
return await wrapped(inst, *args, **kwargs)
|
127
|
+
|
128
|
+
status = inst.ffi_mapping.options_mapping.session.set_operation_timeout_ns(
|
129
|
+
inst._ptr_or_exception(),
|
130
|
+
c_uint64(operation_timeout_ns),
|
131
|
+
)
|
132
|
+
if status != 0:
|
133
|
+
raise OptionsException("failed to set session operation timeout")
|
134
|
+
|
135
|
+
res = await wrapped(inst, *args, **kwargs)
|
136
|
+
|
137
|
+
status = inst.ffi_mapping.options_mapping.session.set_operation_timeout_ns(
|
138
|
+
inst._ptr_or_exception(),
|
139
|
+
c_uint64(operation_timeout_ns),
|
140
|
+
)
|
141
|
+
if status != 0:
|
142
|
+
raise OptionsException("failed to set session operation timeout")
|
143
|
+
|
144
|
+
return res
|
145
|
+
|
146
|
+
update_wrapper(wrapper=wrapper, wrapped=wrapped)
|
147
|
+
|
148
|
+
return wrapper
|
scrapli/cli_parse.py
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
"""cli_parse"""
|
2
|
+
|
3
|
+
import urllib.request
|
4
|
+
from importlib import import_module, resources
|
5
|
+
from io import BytesIO, TextIOWrapper
|
6
|
+
from logging import getLogger
|
7
|
+
from typing import Any, TextIO
|
8
|
+
|
9
|
+
from scrapli.exceptions import ParsingException
|
10
|
+
|
11
|
+
logger = getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
def textfsm_get_template(platform: str, command: str) -> TextIO | None:
|
15
|
+
"""
|
16
|
+
Find correct TextFSM template based on platform and command executed
|
17
|
+
|
18
|
+
Args:
|
19
|
+
platform: ntc-templates device type; i.e. cisco_ios, arista_eos, etc.
|
20
|
+
command: string of command that was executed (to find appropriate template)
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
None or TextIO of opened template
|
24
|
+
|
25
|
+
Raises:
|
26
|
+
N/A
|
27
|
+
|
28
|
+
"""
|
29
|
+
try:
|
30
|
+
import_module(name=".templates", package="ntc_templates")
|
31
|
+
cli_table_obj = getattr(import_module(name=".clitable", package="textfsm"), "CliTable")
|
32
|
+
except ModuleNotFoundError as exc:
|
33
|
+
raise ParsingException("optional extra 'textfsm' not found") from exc
|
34
|
+
|
35
|
+
template_dir = f"{resources.files('ntc_templates')}/templates"
|
36
|
+
|
37
|
+
cli_table = cli_table_obj("index", template_dir)
|
38
|
+
template_index = cli_table.index.GetRowMatch({"Platform": platform, "Command": command})
|
39
|
+
|
40
|
+
if not template_index:
|
41
|
+
logger.warning(
|
42
|
+
f"No match in ntc_templates index for platform `{platform}` and command `{command}`"
|
43
|
+
)
|
44
|
+
return None
|
45
|
+
|
46
|
+
template_name = cli_table.index.index[template_index]["Template"]
|
47
|
+
|
48
|
+
return open(f"{template_dir}/{template_name}", encoding="utf-8")
|
49
|
+
|
50
|
+
|
51
|
+
def _textfsm_to_dict(
|
52
|
+
structured_output: list[Any] | dict[str, Any], header: list[str]
|
53
|
+
) -> list[Any] | dict[str, Any]:
|
54
|
+
"""
|
55
|
+
Create list of dicts from textfsm output and header
|
56
|
+
|
57
|
+
Args:
|
58
|
+
structured_output: parsed textfsm output
|
59
|
+
header: list of strings representing column headers for textfsm output
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
output: structured data
|
63
|
+
|
64
|
+
Raises:
|
65
|
+
N/A
|
66
|
+
|
67
|
+
"""
|
68
|
+
logger.debug("converting textfsm output to dictionary representation")
|
69
|
+
|
70
|
+
header_lower = [h.lower() for h in header]
|
71
|
+
structured_output = [dict(zip(header_lower, row)) for row in structured_output]
|
72
|
+
|
73
|
+
return structured_output
|
74
|
+
|
75
|
+
|
76
|
+
def textfsm_parse(
|
77
|
+
template: str | TextIO, output: str, to_dict: bool = True
|
78
|
+
) -> list[Any] | dict[str, Any]:
|
79
|
+
"""
|
80
|
+
Parse output with TextFSM and ntc-templates, try to return structured output
|
81
|
+
|
82
|
+
Args:
|
83
|
+
template: TextIO or string of URL or filesystem path to template to use to parse data
|
84
|
+
output: unstructured output from device to parse
|
85
|
+
to_dict: convert textfsm output from list of lists to list of dicts -- basically create dict
|
86
|
+
from header and row data so it is easier to read/parse the output
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
output: structured data
|
90
|
+
|
91
|
+
Raises:
|
92
|
+
ScrapliException: If raise_err is set and a textfsm parsing error occurs, raises from the
|
93
|
+
originating textfsm.parser.TextFSMError exception.
|
94
|
+
|
95
|
+
"""
|
96
|
+
import textfsm
|
97
|
+
|
98
|
+
if isinstance(template, str):
|
99
|
+
if template.startswith("http://") or template.startswith("https://"):
|
100
|
+
with urllib.request.urlopen(template) as response:
|
101
|
+
re_table = textfsm.TextFSM(
|
102
|
+
TextIOWrapper(
|
103
|
+
BytesIO(response.read()),
|
104
|
+
encoding=response.headers.get_content_charset(),
|
105
|
+
)
|
106
|
+
)
|
107
|
+
else:
|
108
|
+
re_table = textfsm.TextFSM(open(template, mode="rb"))
|
109
|
+
else:
|
110
|
+
re_table = textfsm.TextFSM(template)
|
111
|
+
|
112
|
+
try:
|
113
|
+
structured_output: list[Any] | dict[str, Any] = re_table.ParseText(output)
|
114
|
+
|
115
|
+
if to_dict:
|
116
|
+
structured_output = _textfsm_to_dict(
|
117
|
+
structured_output=structured_output, header=re_table.header
|
118
|
+
)
|
119
|
+
|
120
|
+
return structured_output
|
121
|
+
except textfsm.parser.TextFSMError as exc:
|
122
|
+
raise ParsingException("failed parsing output with 'textfsm'") from exc
|
123
|
+
|
124
|
+
return []
|
125
|
+
|
126
|
+
|
127
|
+
def genie_parse(platform: str, command: str, output: str) -> list[Any] | dict[str, Any]:
|
128
|
+
"""
|
129
|
+
Parse output with Cisco genie parsers, try to return structured output
|
130
|
+
|
131
|
+
Args:
|
132
|
+
platform: genie device type; i.e. iosxe, iosxr, etc.
|
133
|
+
command: string of command that was executed (to find appropriate parser)
|
134
|
+
output: unstructured output from device to parse
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
output: structured data
|
138
|
+
|
139
|
+
Raises:
|
140
|
+
N/A
|
141
|
+
|
142
|
+
"""
|
143
|
+
try:
|
144
|
+
device = getattr(import_module(name=".conf.base", package="genie"), "Device")
|
145
|
+
get_parser = getattr(
|
146
|
+
import_module(name=".libs.parser.utils", package="genie"), "get_parser"
|
147
|
+
)
|
148
|
+
except ModuleNotFoundError as exc:
|
149
|
+
raise ParsingException("optional extra 'genie' not found") from exc
|
150
|
+
|
151
|
+
genie_device = device("scrapli_device", custom={"abstraction": {"order": ["os"]}}, os=platform)
|
152
|
+
|
153
|
+
try:
|
154
|
+
get_parser(command, genie_device)
|
155
|
+
genie_parsed_result = genie_device.parse(command, output=output)
|
156
|
+
if isinstance(genie_parsed_result, list | dict):
|
157
|
+
return genie_parsed_result
|
158
|
+
except Exception as exc:
|
159
|
+
raise ParsingException("failed parsing output with 'genie'") from exc
|
160
|
+
|
161
|
+
return []
|
scrapli/cli_result.py
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
"""scrapli.result"""
|
2
|
+
|
3
|
+
from typing import Any, TextIO
|
4
|
+
|
5
|
+
from scrapli.cli_parse import genie_parse, textfsm_get_template, textfsm_parse
|
6
|
+
from scrapli.exceptions import ParsingException
|
7
|
+
|
8
|
+
OPERATION_DELIMITER = "__libscrapli__"
|
9
|
+
|
10
|
+
|
11
|
+
class Result:
|
12
|
+
"""
|
13
|
+
Result represents a set of results from some Cli operation(s).
|
14
|
+
|
15
|
+
Args:
|
16
|
+
N/A
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
None
|
20
|
+
|
21
|
+
Raises:
|
22
|
+
N/A
|
23
|
+
|
24
|
+
"""
|
25
|
+
|
26
|
+
def __init__( # noqa: PLR0913
|
27
|
+
self,
|
28
|
+
*,
|
29
|
+
host: str,
|
30
|
+
port: int,
|
31
|
+
inputs: str,
|
32
|
+
start_time: int,
|
33
|
+
splits: list[int],
|
34
|
+
results_raw: bytes,
|
35
|
+
results: str,
|
36
|
+
results_failed_indicator: str,
|
37
|
+
textfsm_platform: str,
|
38
|
+
genie_platform: str,
|
39
|
+
) -> None:
|
40
|
+
self.host = host
|
41
|
+
self.port = port
|
42
|
+
self.inputs = inputs.split(OPERATION_DELIMITER)
|
43
|
+
self.start_time = start_time
|
44
|
+
self.splits = splits
|
45
|
+
self.results_raw = results_raw.split(OPERATION_DELIMITER.encode())
|
46
|
+
self.results = results.split(OPERATION_DELIMITER)
|
47
|
+
self.results_failed_indicator = results_failed_indicator
|
48
|
+
self.textfsm_platform = textfsm_platform
|
49
|
+
self.genie_platform = genie_platform
|
50
|
+
|
51
|
+
def extend(self, result: "Result") -> None:
|
52
|
+
"""
|
53
|
+
Extends this Result object with another Result object.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
result: the result object with which to extend this result object
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
N/A
|
60
|
+
|
61
|
+
Raises:
|
62
|
+
N/A
|
63
|
+
|
64
|
+
"""
|
65
|
+
self.inputs.extend(result.inputs)
|
66
|
+
self.results_raw.extend(result.results_raw)
|
67
|
+
self.results.extend(result.results)
|
68
|
+
self.splits.extend(result.splits)
|
69
|
+
|
70
|
+
@property
|
71
|
+
def failed(self) -> bool:
|
72
|
+
"""
|
73
|
+
Returns True if any failed indicators were seen, otherwise False.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
N/A
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
bool: True for failed, otherwise False
|
80
|
+
|
81
|
+
Raises:
|
82
|
+
N/A
|
83
|
+
|
84
|
+
"""
|
85
|
+
return bool(self.results_failed_indicator)
|
86
|
+
|
87
|
+
@property
|
88
|
+
def end_time(self) -> int:
|
89
|
+
"""
|
90
|
+
Returns the end time of the operations in unix nano.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
N/A
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
int: end time in unix nano
|
97
|
+
|
98
|
+
Raises:
|
99
|
+
N/A
|
100
|
+
|
101
|
+
"""
|
102
|
+
if not self.splits:
|
103
|
+
# if we had no splits it was a "noop" type op (like enter mode when
|
104
|
+
# you are already in the requested mode), so we'll lie and say it
|
105
|
+
# was a 1ns op time
|
106
|
+
return self.start_time + 1
|
107
|
+
|
108
|
+
return self.splits[-1]
|
109
|
+
|
110
|
+
@property
|
111
|
+
def elapsed_time_seconds(self) -> float:
|
112
|
+
"""
|
113
|
+
Returns the number of seconds the operation took.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
N/A
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
float: duration in seconds
|
120
|
+
|
121
|
+
Raises:
|
122
|
+
N/A
|
123
|
+
|
124
|
+
"""
|
125
|
+
return (self.end_time - self.start_time) / 1_000_000_000
|
126
|
+
|
127
|
+
@property
|
128
|
+
def result(self) -> str:
|
129
|
+
"""
|
130
|
+
Returns the results joined on newline chars. Note this does *not* include inputs sent.
|
131
|
+
|
132
|
+
Args:
|
133
|
+
N/A
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
str: joined results
|
137
|
+
|
138
|
+
Raises:
|
139
|
+
N/A
|
140
|
+
|
141
|
+
"""
|
142
|
+
return "\n".join(self.results)
|
143
|
+
|
144
|
+
def textfsm_parse(
|
145
|
+
self,
|
146
|
+
index: int = 0,
|
147
|
+
template: str | TextIO | None = None,
|
148
|
+
to_dict: bool = True,
|
149
|
+
) -> list[Any] | dict[str, Any]:
|
150
|
+
"""
|
151
|
+
Parse results with textfsm, always return structured data
|
152
|
+
|
153
|
+
Returns an empty list if parsing fails!
|
154
|
+
|
155
|
+
Args:
|
156
|
+
index: the index of the result to parse, assumes first/zeroith if not provided
|
157
|
+
template: string path to textfsm template or opened textfsm template file
|
158
|
+
to_dict: convert textfsm output from list of lists to list of dicts -- basically create
|
159
|
+
dict from header and row data so it is easier to read/parse the output
|
160
|
+
|
161
|
+
Returns:
|
162
|
+
structured_result: empty list or parsed data from textfsm
|
163
|
+
|
164
|
+
Raises:
|
165
|
+
N/A
|
166
|
+
|
167
|
+
"""
|
168
|
+
if template is None:
|
169
|
+
template = textfsm_get_template(
|
170
|
+
platform=self.textfsm_platform, command=self.inputs[index]
|
171
|
+
)
|
172
|
+
|
173
|
+
if template is None:
|
174
|
+
raise ParsingException("no template provided or available for input")
|
175
|
+
|
176
|
+
return textfsm_parse(template=template, output=self.result, to_dict=to_dict)
|
177
|
+
|
178
|
+
def genie_parse(
|
179
|
+
self,
|
180
|
+
index: int = 0,
|
181
|
+
) -> dict[str, Any] | list[Any]:
|
182
|
+
"""
|
183
|
+
Parse results with genie, always return structured data
|
184
|
+
|
185
|
+
Returns an empty list if parsing fails!
|
186
|
+
|
187
|
+
Args:
|
188
|
+
index: the index of the result to parse, assumes first/zeroith if not provided
|
189
|
+
|
190
|
+
Returns:
|
191
|
+
structured_result: empty list or parsed data from genie
|
192
|
+
|
193
|
+
Raises:
|
194
|
+
N/A
|
195
|
+
|
196
|
+
"""
|
197
|
+
return genie_parse(self.genie_platform, self.inputs[index], self.results[index])
|
@@ -0,0 +1 @@
|
|
1
|
+
"""scrapli.definitions"""
|
@@ -0,0 +1,64 @@
|
|
1
|
+
---
|
2
|
+
prompt_pattern: '^[^<\r\n]{1,}[>#$]\s?$'
|
3
|
+
default_mode: 'privileged_exec'
|
4
|
+
modes:
|
5
|
+
- name: 'exec'
|
6
|
+
prompt_pattern: '^[\w.\-@()/: ]{1,63}>\s?$'
|
7
|
+
accessible_modes:
|
8
|
+
- name: 'privileged_exec'
|
9
|
+
instructions:
|
10
|
+
- send_prompted_input:
|
11
|
+
input: 'enable'
|
12
|
+
prompt_exact: 'Password:'
|
13
|
+
response: '__lookup::enable'
|
14
|
+
- name: 'privileged_exec'
|
15
|
+
prompt_pattern: '^[\w.\-@()/: ]{1,63}#\s?$'
|
16
|
+
prompt_excludes:
|
17
|
+
- '(config'
|
18
|
+
accessible_modes:
|
19
|
+
- name: 'exec'
|
20
|
+
instructions:
|
21
|
+
- send_input:
|
22
|
+
input: 'disable'
|
23
|
+
- name: 'configuration'
|
24
|
+
instructions:
|
25
|
+
- send_input:
|
26
|
+
input: 'configure terminal'
|
27
|
+
- name: 'bash'
|
28
|
+
instructions:
|
29
|
+
- send_input:
|
30
|
+
input: 'bash'
|
31
|
+
- name: 'configuration'
|
32
|
+
prompt_pattern: '^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:+]{0,63}\)#\s?$'
|
33
|
+
accessible_modes:
|
34
|
+
- name: 'privileged_exec'
|
35
|
+
instructions:
|
36
|
+
- send_input:
|
37
|
+
input: 'end'
|
38
|
+
- name: 'bash'
|
39
|
+
prompt_pattern: '^\[[\w.\-@()\/: ]{1,63}~\]\$'
|
40
|
+
accessible_modes:
|
41
|
+
- name: 'privileged_exec'
|
42
|
+
instructions:
|
43
|
+
- send_input:
|
44
|
+
input: 'exit'
|
45
|
+
failure_indicators:
|
46
|
+
- '% Ambiguous command'
|
47
|
+
- '% Error'
|
48
|
+
- '% Incomplete command'
|
49
|
+
- '% Invalid input'
|
50
|
+
- '% Cannot commit'
|
51
|
+
- '% Unavailable command'
|
52
|
+
- '% Duplicate sequence number'
|
53
|
+
on_open_instructions:
|
54
|
+
- enter_mode:
|
55
|
+
requested_mode: 'privileged_exec'
|
56
|
+
- send_input:
|
57
|
+
input: 'term width 32767'
|
58
|
+
- send_input:
|
59
|
+
input: 'term len 0'
|
60
|
+
on_close_instructions:
|
61
|
+
- enter_mode:
|
62
|
+
requested_mode: 'privileged_exec'
|
63
|
+
- write:
|
64
|
+
input: 'exit'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
---
|
2
|
+
prompt_pattern: '^.*[>#$]\s?+$'
|
3
|
+
default_mode: 'privileged_exec'
|
4
|
+
modes:
|
5
|
+
- name: 'exec'
|
6
|
+
prompt_pattern: '^[\w.\-@/:]{1,63}>\s?+$'
|
7
|
+
accessible_modes:
|
8
|
+
- name: 'privileged_exec'
|
9
|
+
instructions:
|
10
|
+
- send_prompted_input:
|
11
|
+
input: 'enable'
|
12
|
+
prompt_exact: 'Password:'
|
13
|
+
response: '__lookup::enable'
|
14
|
+
- name: 'privileged_exec'
|
15
|
+
prompt_pattern: '^[\w.\-@/:]{1,63}#\s?+$'
|
16
|
+
accessible_modes:
|
17
|
+
- name: 'exec'
|
18
|
+
instructions:
|
19
|
+
- send_input:
|
20
|
+
input: 'disable'
|
21
|
+
- name: 'configuration'
|
22
|
+
instructions:
|
23
|
+
- send_input:
|
24
|
+
input: 'configure terminal'
|
25
|
+
- name: 'tclsh'
|
26
|
+
instructions:
|
27
|
+
- send_input:
|
28
|
+
input: 'tclsh'
|
29
|
+
- name: 'configuration'
|
30
|
+
prompt_pattern: '^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#\s?+$'
|
31
|
+
prompt_excludes:
|
32
|
+
- 'tcl)'
|
33
|
+
accessible_modes:
|
34
|
+
- name: 'privileged_exec'
|
35
|
+
instructions:
|
36
|
+
- send_input:
|
37
|
+
input: 'end'
|
38
|
+
- name: 'tclsh'
|
39
|
+
prompt_pattern: '^([\w.\-@/+>:]+\(tcl\)[>#]|\+>)\s?+$'
|
40
|
+
accessible_modes:
|
41
|
+
- name: 'privileged_exec'
|
42
|
+
instructions:
|
43
|
+
- send_input:
|
44
|
+
input: 'tclquit'
|
45
|
+
failure_indicators:
|
46
|
+
- '% Ambiguous command'
|
47
|
+
- '% Incomplete command'
|
48
|
+
- '% Invalid input detected'
|
49
|
+
- '% Unknown command'
|
50
|
+
on_open_instructions:
|
51
|
+
- enter_mode:
|
52
|
+
requested_mode: 'privileged_exec'
|
53
|
+
- send_input:
|
54
|
+
input: 'term width 512'
|
55
|
+
- send_input:
|
56
|
+
input: 'term len 0'
|
57
|
+
on_close_instructions:
|
58
|
+
- enter_mode:
|
59
|
+
requested_mode: 'privileged_exec'
|
60
|
+
- write:
|
61
|
+
input: 'exit'
|
62
|
+
ntc_templates_platform: 'cisco_iosxe'
|
63
|
+
genie_platform: 'iosxe'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
---
|
2
|
+
prompt_pattern: '^.*[>#$]\s?+$'
|
3
|
+
default_mode: 'privileged_exec'
|
4
|
+
modes:
|
5
|
+
- name: 'privileged_exec'
|
6
|
+
prompt_pattern: '^[\w.\-@/:]{1,63}#\s?$'
|
7
|
+
accessible_modes:
|
8
|
+
- name: 'configuration'
|
9
|
+
instructions:
|
10
|
+
- send_input:
|
11
|
+
input: 'configure terminal'
|
12
|
+
- name: 'configuration_exclusive'
|
13
|
+
instructions:
|
14
|
+
- send_input:
|
15
|
+
input: 'configure exclusive'
|
16
|
+
- name: 'configuration'
|
17
|
+
prompt_pattern: '^[\w.\-@/:]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$'
|
18
|
+
accessible_modes:
|
19
|
+
- name: 'privileged_exec'
|
20
|
+
instructions:
|
21
|
+
- send_input:
|
22
|
+
input: 'end'
|
23
|
+
- name: 'configuration_exclusive'
|
24
|
+
prompt_pattern: '^[\w.\-@/:]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$'
|
25
|
+
accessible_modes:
|
26
|
+
- name: 'privileged_exec'
|
27
|
+
instructions:
|
28
|
+
- send_input:
|
29
|
+
input: 'end'
|
30
|
+
failure_indicators:
|
31
|
+
- '% Ambiguous command'
|
32
|
+
- '% Incomplete command'
|
33
|
+
- '% Invalid input detected'
|
34
|
+
on_open_instructions:
|
35
|
+
- enter_mode:
|
36
|
+
requested_mode: 'privileged_exec'
|
37
|
+
- send_input:
|
38
|
+
input: 'term width 512'
|
39
|
+
- send_input:
|
40
|
+
input: 'term len 0'
|
41
|
+
on_close_instructions:
|
42
|
+
- enter_mode:
|
43
|
+
requested_mode: 'privileged_exec'
|
44
|
+
- write:
|
45
|
+
input: 'exit'
|
46
|
+
ntc_templates_platform: 'cisco_iosxe'
|
47
|
+
genie_platform: 'iosxe'
|