scrapli2 2.0.0__tar.gz
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.
- scrapli2-2.0.0/LICENSE +21 -0
- scrapli2-2.0.0/MANIFEST.in +5 -0
- scrapli2-2.0.0/PKG-INFO +59 -0
- scrapli2-2.0.0/README.md +11 -0
- scrapli2-2.0.0/pyproject.toml +115 -0
- scrapli2-2.0.0/requirements-dev.txt +8 -0
- scrapli2-2.0.0/requirements-docs.txt +5 -0
- scrapli2-2.0.0/requirements-genie.txt +2 -0
- scrapli2-2.0.0/requirements-textfsm.txt +2 -0
- scrapli2-2.0.0/requirements.txt +0 -0
- scrapli2-2.0.0/scrapli/__init__.py +33 -0
- scrapli2-2.0.0/scrapli/auth.py +237 -0
- scrapli2-2.0.0/scrapli/cli.py +2202 -0
- scrapli2-2.0.0/scrapli/cli_decorators.py +148 -0
- scrapli2-2.0.0/scrapli/cli_parse.py +161 -0
- scrapli2-2.0.0/scrapli/cli_result.py +371 -0
- scrapli2-2.0.0/scrapli/definitions/__init__.py +1 -0
- scrapli2-2.0.0/scrapli/definitions/aethra_atosnt.yaml +9 -0
- scrapli2-2.0.0/scrapli/definitions/alcatel_aos.yaml +9 -0
- scrapli2-2.0.0/scrapli/definitions/arista_eos.yaml +65 -0
- scrapli2-2.0.0/scrapli/definitions/aruba_aoscx.yaml +33 -0
- scrapli2-2.0.0/scrapli/definitions/aruba_wlc.yaml +62 -0
- scrapli2-2.0.0/scrapli/definitions/cisco_aireos.yaml +31 -0
- scrapli2-2.0.0/scrapli/definitions/cisco_asa.yaml +48 -0
- scrapli2-2.0.0/scrapli/definitions/cisco_cbs.yaml +50 -0
- scrapli2-2.0.0/scrapli/definitions/cisco_ftd.yaml +42 -0
- scrapli2-2.0.0/scrapli/definitions/cisco_iosxe.yaml +64 -0
- scrapli2-2.0.0/scrapli/definitions/cisco_iosxr.yaml +47 -0
- scrapli2-2.0.0/scrapli/definitions/cisco_nxos.yaml +67 -0
- scrapli2-2.0.0/scrapli/definitions/cumulus_linux.yaml +28 -0
- scrapli2-2.0.0/scrapli/definitions/cumulus_vtysh.yaml +42 -0
- scrapli2-2.0.0/scrapli/definitions/datacom_dmos.yaml +30 -0
- scrapli2-2.0.0/scrapli/definitions/datacom_dmswitch.yaml +32 -0
- scrapli2-2.0.0/scrapli/definitions/default.yaml +9 -0
- scrapli2-2.0.0/scrapli/definitions/dell_emc.yaml +46 -0
- scrapli2-2.0.0/scrapli/definitions/dell_enterprisesonic.yaml +40 -0
- scrapli2-2.0.0/scrapli/definitions/dlink_os.yaml +46 -0
- scrapli2-2.0.0/scrapli/definitions/edgecore_ecs.yaml +35 -0
- scrapli2-2.0.0/scrapli/definitions/eltex_esr.yaml +43 -0
- scrapli2-2.0.0/scrapli/definitions/fortinet_fortios.yaml +20 -0
- scrapli2-2.0.0/scrapli/definitions/fortinet_wlc.yaml +33 -0
- scrapli2-2.0.0/scrapli/definitions/hp_comware.yaml +31 -0
- scrapli2-2.0.0/scrapli/definitions/huawei_smartax.yaml +67 -0
- scrapli2-2.0.0/scrapli/definitions/huawei_vrp.yaml +56 -0
- scrapli2-2.0.0/scrapli/definitions/ipinfusion_ocnos.yaml +57 -0
- scrapli2-2.0.0/scrapli/definitions/juniper_junos.yaml +88 -0
- scrapli2-2.0.0/scrapli/definitions/mikrotik_routeros.yaml +9 -0
- scrapli2-2.0.0/scrapli/definitions/nokia_srlinux.yaml +50 -0
- scrapli2-2.0.0/scrapli/definitions/nokia_sros.yaml +31 -0
- scrapli2-2.0.0/scrapli/definitions/nokia_sros_classic.yaml +33 -0
- scrapli2-2.0.0/scrapli/definitions/nokia_sros_classic_aram.yaml +25 -0
- scrapli2-2.0.0/scrapli/definitions/paloalto_panos.yaml +36 -0
- scrapli2-2.0.0/scrapli/definitions/raisecom_ros.yaml +47 -0
- scrapli2-2.0.0/scrapli/definitions/ruckus_fastiron.yaml +45 -0
- scrapli2-2.0.0/scrapli/definitions/ruckus_unleashed.yaml +64 -0
- scrapli2-2.0.0/scrapli/definitions/ruijie_rgos.yaml +48 -0
- scrapli2-2.0.0/scrapli/definitions/siemens_roxii.yaml +28 -0
- scrapli2-2.0.0/scrapli/definitions/ubiquiti_edgeswitch.yaml +47 -0
- scrapli2-2.0.0/scrapli/definitions/versa_flexvnf.yaml +45 -0
- scrapli2-2.0.0/scrapli/definitions/vyos_vyos.yaml +35 -0
- scrapli2-2.0.0/scrapli/definitions/zyxel_dslam.yaml +18 -0
- scrapli2-2.0.0/scrapli/exceptions.py +69 -0
- scrapli2-2.0.0/scrapli/ffi.py +117 -0
- scrapli2-2.0.0/scrapli/ffi_mapping.py +507 -0
- scrapli2-2.0.0/scrapli/ffi_mapping_cli.py +1126 -0
- scrapli2-2.0.0/scrapli/ffi_mapping_netconf.py +1839 -0
- scrapli2-2.0.0/scrapli/ffi_options.py +342 -0
- scrapli2-2.0.0/scrapli/ffi_types.py +495 -0
- scrapli2-2.0.0/scrapli/helper.py +254 -0
- scrapli2-2.0.0/scrapli/lib/__init__.py +1 -0
- scrapli2-2.0.0/scrapli/lib/libscrapli-x86_64-linux-gnu.so.0.0.1 +0 -0
- scrapli2-2.0.0/scrapli/netconf.py +2910 -0
- scrapli2-2.0.0/scrapli/netconf_decorators.py +146 -0
- scrapli2-2.0.0/scrapli/netconf_result.py +166 -0
- scrapli2-2.0.0/scrapli/py.typed +0 -0
- scrapli2-2.0.0/scrapli/session.py +139 -0
- scrapli2-2.0.0/scrapli/transport.py +424 -0
- scrapli2-2.0.0/scrapli2.egg-info/PKG-INFO +59 -0
- scrapli2-2.0.0/scrapli2.egg-info/SOURCES.txt +82 -0
- scrapli2-2.0.0/scrapli2.egg-info/dependency_links.txt +1 -0
- scrapli2-2.0.0/scrapli2.egg-info/requires.txt +28 -0
- scrapli2-2.0.0/scrapli2.egg-info/top_level.txt +1 -0
- scrapli2-2.0.0/setup.cfg +4 -0
- scrapli2-2.0.0/setup.py +415 -0
scrapli2-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Carl Montanari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
scrapli2-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scrapli2
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Fast, flexible, sync/async, Python 3.10+ screen scraping client specifically for network devices
|
|
5
|
+
Author-email: Carl Montanari <carl.r.montanari@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Docs, https://carlmontanari.github.io/scrapli/
|
|
8
|
+
Project-URL: Homepage, https://github.com/carlmontanari/scrapli
|
|
9
|
+
Keywords: netconf,network,cli,ssh,telnet,async
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Operating System :: MacOS
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: nox==2025.5.1; extra == "dev"
|
|
25
|
+
Requires-Dist: black<26.0.0,>=25.1.0; extra == "dev"
|
|
26
|
+
Requires-Dist: isort<7.0.0,>=6.0.1; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff<1.0.0,>=0.11.8; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy<2.0.0,>=1.15.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest<9.0.0,>=8.3.5; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-cov<7.0.0,>=6.1.1; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio<1.0.0,>=0.26.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pyats>=20.2; extra == "dev"
|
|
33
|
+
Requires-Dist: genie>=20.2; extra == "dev"
|
|
34
|
+
Requires-Dist: ntc-templates<8.0.0,>=1.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: textfsm<2.0.0,>=1.1.0; extra == "dev"
|
|
36
|
+
Provides-Extra: full
|
|
37
|
+
Requires-Dist: ntc-templates<8.0.0,>=1.1.0; extra == "full"
|
|
38
|
+
Requires-Dist: textfsm<2.0.0,>=1.1.0; extra == "full"
|
|
39
|
+
Requires-Dist: pyats>=20.2; extra == "full"
|
|
40
|
+
Requires-Dist: genie>=20.2; extra == "full"
|
|
41
|
+
Provides-Extra: textfsm
|
|
42
|
+
Requires-Dist: ntc-templates<8.0.0,>=1.1.0; extra == "textfsm"
|
|
43
|
+
Requires-Dist: textfsm<2.0.0,>=1.1.0; extra == "textfsm"
|
|
44
|
+
Provides-Extra: genie
|
|
45
|
+
Requires-Dist: pyats>=20.2; extra == "genie"
|
|
46
|
+
Requires-Dist: genie>=20.2; extra == "genie"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
<p center><a href=""><img src=https://github.com/carlmontanari/scrapli/blob/main/scrapli.svg?sanitize=true/></a></p>
|
|
50
|
+
|
|
51
|
+
[](https://pypi.org/project/scrapli)
|
|
52
|
+
[](https://badge.fury.io/py/scrapli)
|
|
53
|
+
[](https://opensource.org/licenses/MIT)
|
|
54
|
+
|
|
55
|
+
scrapli
|
|
56
|
+
=======
|
|
57
|
+
|
|
58
|
+
scrapli -- scrap(e c)li -- is a python 3.10+ library focused on connecting to devices, specifically network devices
|
|
59
|
+
(routers/switches/firewalls/etc.) via Telnet, SSH or NETCONF.
|
scrapli2-2.0.0/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<p center><a href=""><img src=https://github.com/carlmontanari/scrapli/blob/main/scrapli.svg?sanitize=true/></a></p>
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/scrapli)
|
|
4
|
+
[](https://badge.fury.io/py/scrapli)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
scrapli
|
|
8
|
+
=======
|
|
9
|
+
|
|
10
|
+
scrapli -- scrap(e c)li -- is a python 3.10+ library focused on connecting to devices, specifically network devices
|
|
11
|
+
(routers/switches/firewalls/etc.) via Telnet, SSH or NETCONF.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "setuptools.build_meta"
|
|
3
|
+
requires = ["setuptools", "wheel", "ziglang==0.16.0"]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "scrapli2"
|
|
7
|
+
description = "Fast, flexible, sync/async, Python 3.10+ screen scraping client specifically for network devices"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
keywords = ["netconf", "network", "cli", "ssh", "telnet", "async"]
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [{ name = "Carl Montanari", email = "carl.r.montanari@gmail.com" }]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Operating System :: POSIX :: Linux",
|
|
15
|
+
"Operating System :: MacOS",
|
|
16
|
+
"Programming Language :: Python",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
]
|
|
25
|
+
dynamic = ["dependencies", "optional-dependencies", "version"]
|
|
26
|
+
[project.urls]
|
|
27
|
+
Docs = "https://carlmontanari.github.io/scrapli/"
|
|
28
|
+
Homepage = "https://github.com/carlmontanari/scrapli"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools]
|
|
31
|
+
packages = ["scrapli", "scrapli.definitions", "scrapli.lib"]
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.dynamic]
|
|
34
|
+
version = { attr = "scrapli.__version__" }
|
|
35
|
+
dependencies = { file = "requirements.txt" }
|
|
36
|
+
optional-dependencies.dev = { file = [
|
|
37
|
+
"requirements-dev.txt",
|
|
38
|
+
"requirements-genie.txt",
|
|
39
|
+
"requirements-textfsm.txt",
|
|
40
|
+
] }
|
|
41
|
+
optional-dependencies.full = { file = [
|
|
42
|
+
"requirements-textfsm.txt",
|
|
43
|
+
"requirements-genie.txt",
|
|
44
|
+
] }
|
|
45
|
+
optional-dependencies.textfsm = { file = "requirements-textfsm.txt" }
|
|
46
|
+
optional-dependencies.genie = { file = "requirements-genie.txt" }
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.package-data]
|
|
49
|
+
scrapli = [
|
|
50
|
+
"py.typed",
|
|
51
|
+
"scrapli/definitions/*.yaml",
|
|
52
|
+
"scrapli/lib/*.so.*",
|
|
53
|
+
"scrapli/lib/*.dylib",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.black]
|
|
57
|
+
line-length = 100
|
|
58
|
+
target-version = ["py313"]
|
|
59
|
+
|
|
60
|
+
[tool.isort]
|
|
61
|
+
profile = "black"
|
|
62
|
+
line_length = 100
|
|
63
|
+
multi_line_output = 3
|
|
64
|
+
include_trailing_comma = true
|
|
65
|
+
|
|
66
|
+
[tool.mypy]
|
|
67
|
+
python_version = "3.13"
|
|
68
|
+
pretty = true
|
|
69
|
+
ignore_missing_imports = true
|
|
70
|
+
warn_redundant_casts = true
|
|
71
|
+
warn_unused_configs = true
|
|
72
|
+
strict_optional = true
|
|
73
|
+
|
|
74
|
+
[tool.ruff]
|
|
75
|
+
include = ["setup.py", "scrapli/**.py", "examples/**.py", "tests/**.py"]
|
|
76
|
+
line-length = 100
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint]
|
|
79
|
+
select = [
|
|
80
|
+
"FIX",
|
|
81
|
+
"D",
|
|
82
|
+
"PL",
|
|
83
|
+
"ERA",
|
|
84
|
+
"C90",
|
|
85
|
+
"N",
|
|
86
|
+
"PERF",
|
|
87
|
+
"E",
|
|
88
|
+
"W",
|
|
89
|
+
"D",
|
|
90
|
+
"F",
|
|
91
|
+
"UP",
|
|
92
|
+
"FURB",
|
|
93
|
+
"RUF",
|
|
94
|
+
]
|
|
95
|
+
ignore = [
|
|
96
|
+
"D105", # Missing docstring in magic method
|
|
97
|
+
"D107", # Missing docstring in `__init__`
|
|
98
|
+
"D212", # Multi-line docstring summary should start at the first line
|
|
99
|
+
"D415", # First line should end with a period, question mark, or exclamation point
|
|
100
|
+
"N818", # Exception name `foo` should be named with an Error suffix
|
|
101
|
+
"UP015", # Unnecessary mode argument
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[tool.ruff.lint.pydocstyle]
|
|
105
|
+
convention = "google"
|
|
106
|
+
|
|
107
|
+
[tool.ruff.lint.per-file-ignores]
|
|
108
|
+
"scrapli/ffi_mapping_*.py" = ["PLR0913"]
|
|
109
|
+
"tests/**.py" = ["D", "E501", "PLR0913"]
|
|
110
|
+
|
|
111
|
+
[tool.pytest.ini_options]
|
|
112
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
113
|
+
log_cli = true
|
|
114
|
+
log_cli_level = "CRITICAL"
|
|
115
|
+
log_cli_format = "%(asctime)s [%(levelname)s] %(message)s"
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""scrapli"""
|
|
2
|
+
|
|
3
|
+
from scrapli.auth import LookupKeyValue
|
|
4
|
+
from scrapli.auth import Options as AuthOptions
|
|
5
|
+
from scrapli.cli import Cli, ReadCallback
|
|
6
|
+
from scrapli.netconf import Netconf
|
|
7
|
+
from scrapli.netconf import Options as NetconfOptions
|
|
8
|
+
from scrapli.session import Options as SessionOptions
|
|
9
|
+
from scrapli.transport import BinOptions as TransportBinOptions
|
|
10
|
+
from scrapli.transport import Ssh2Options as TransportSsh2Options
|
|
11
|
+
from scrapli.transport import TelnetOptions as TransportTelnetOptions
|
|
12
|
+
from scrapli.transport import TestOptions as TransportTestOptions
|
|
13
|
+
|
|
14
|
+
# set in ci on release
|
|
15
|
+
__version__ = "2.0.0"
|
|
16
|
+
__semver_version__ = "2.0.0"
|
|
17
|
+
__calendar_version__ = "2026.10.03"
|
|
18
|
+
|
|
19
|
+
__definitions_version__ = "0.0.5"
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
"AuthOptions",
|
|
23
|
+
"Cli",
|
|
24
|
+
"LookupKeyValue",
|
|
25
|
+
"Netconf",
|
|
26
|
+
"NetconfOptions",
|
|
27
|
+
"ReadCallback",
|
|
28
|
+
"SessionOptions",
|
|
29
|
+
"TransportBinOptions",
|
|
30
|
+
"TransportSsh2Options",
|
|
31
|
+
"TransportTelnetOptions",
|
|
32
|
+
"TransportTestOptions",
|
|
33
|
+
)
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""scrapli.auth"""
|
|
2
|
+
|
|
3
|
+
from ctypes import Array, c_bool, c_char_p, c_size_t, c_uint16, pointer
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from scrapli.ffi_options import DriverOptionsPointer
|
|
7
|
+
from scrapli.ffi_types import to_c_string
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class LookupKeyValue:
|
|
12
|
+
"""
|
|
13
|
+
Options a "lookup" key/value pair.
|
|
14
|
+
|
|
15
|
+
Used in conjuection with platform definition templating like `__lookup::enable` where "enable"
|
|
16
|
+
is the key to "lookup" in the list of lookup key/values.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
key: the name of the lookup key
|
|
20
|
+
value: the value of the lookup key
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
None
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
N/A
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
key: str
|
|
31
|
+
value: str
|
|
32
|
+
|
|
33
|
+
_key: c_char_p | None = field(init=False, default=None, repr=False)
|
|
34
|
+
_value: c_char_p | None = field(init=False, default=None, repr=False)
|
|
35
|
+
|
|
36
|
+
def _get_c_strings(self) -> tuple[c_char_p, c_char_p]:
|
|
37
|
+
self._key = to_c_string(self.key)
|
|
38
|
+
self._value = to_c_string(self.value)
|
|
39
|
+
|
|
40
|
+
return self._key, self._value
|
|
41
|
+
|
|
42
|
+
def __repr__(self) -> str:
|
|
43
|
+
return f"{self.__class__.__name__}(key='{self.key}', value='REDACTED')"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class Options:
|
|
48
|
+
"""
|
|
49
|
+
Options holds auth related options to pass to the ffi layer.
|
|
50
|
+
|
|
51
|
+
All arguments are optional, though you will almost certainly need to provide *some* -- for
|
|
52
|
+
example a username and password or key.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
username: the username to use for the connection
|
|
56
|
+
password: the password to use for the connection
|
|
57
|
+
private_key_path: filepath to the ssh private key to use
|
|
58
|
+
private_key_passphrase: the private key passphrase if set
|
|
59
|
+
private_key_content: the private key content to use -- only supported w/ ssh2 transport.
|
|
60
|
+
lookups: a list of key/values that can be "looked up" from a connection -- used in
|
|
61
|
+
conjunction with platform definition templating like `__lookup::enable` where "enable"
|
|
62
|
+
is the key to "lookup" in the list of lookup key/values.
|
|
63
|
+
force_in_session_auth: unconditionally force the in session auth process.
|
|
64
|
+
bypass_in_session_auth: skip in session auth even if transport (telnet/bin) expect it.
|
|
65
|
+
username_pattern: the regex pattern to use to look for a username prompt
|
|
66
|
+
password_pattern: the regex pattern to use to look for a password prompt
|
|
67
|
+
private_key_passphrase_pattern: the regex pattern to use to look for a passphrase prompt
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
None
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
N/A
|
|
74
|
+
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
username: str | None = None
|
|
78
|
+
password: str | None = None
|
|
79
|
+
private_key_path: str | None = None
|
|
80
|
+
private_key_passphrase: str | None = None
|
|
81
|
+
private_key_content: str | None = None
|
|
82
|
+
lookups: list[LookupKeyValue] | None = None
|
|
83
|
+
force_in_session_auth: bool | None = None
|
|
84
|
+
bypass_in_session_auth: bool | None = None
|
|
85
|
+
username_pattern: str | None = None
|
|
86
|
+
password_pattern: str | None = None
|
|
87
|
+
private_key_passphrase_pattern: str | None = None
|
|
88
|
+
|
|
89
|
+
_username: c_char_p | None = field(init=False, default=None, repr=False)
|
|
90
|
+
_password: c_char_p | None = field(init=False, default=None, repr=False)
|
|
91
|
+
_private_key_path: c_char_p | None = field(init=False, default=None, repr=False)
|
|
92
|
+
_private_key_passphrase: c_char_p | None = field(init=False, default=None, repr=False)
|
|
93
|
+
_private_key_content: c_char_p | None = field(init=False, default=None, repr=False)
|
|
94
|
+
_username_pattern: c_char_p | None = field(init=False, default=None, repr=False)
|
|
95
|
+
_password_pattern: c_char_p | None = field(init=False, default=None, repr=False)
|
|
96
|
+
_private_key_passphrase_pattern: c_char_p | None = field(init=False, default=None, repr=False)
|
|
97
|
+
|
|
98
|
+
_lookup_map_keys: Array[c_char_p] | None = field(init=False, default=None, repr=False)
|
|
99
|
+
_lookup_map_key_lens: Array[c_uint16] | None = field(init=False, default=None, repr=False)
|
|
100
|
+
_lookup_map_vals: Array[c_char_p] | None = field(init=False, default=None, repr=False)
|
|
101
|
+
_lookup_map_val_lens: Array[c_uint16] | None = field(init=False, default=None, repr=False)
|
|
102
|
+
|
|
103
|
+
def apply(self, *, options: DriverOptionsPointer) -> None: # noqa: C901,PLR0915
|
|
104
|
+
"""
|
|
105
|
+
Applies the options to the given options struct.
|
|
106
|
+
|
|
107
|
+
Should not be called directly/by users.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
options: the options struct to write set options to
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
None
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
N/A
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
if self.username is not None:
|
|
120
|
+
self._username = to_c_string(self.username)
|
|
121
|
+
|
|
122
|
+
options.contents.auth.username = self._username
|
|
123
|
+
options.contents.auth.username_len = c_size_t(len(self.username))
|
|
124
|
+
|
|
125
|
+
if self.password is not None:
|
|
126
|
+
self._password = to_c_string(self.password)
|
|
127
|
+
|
|
128
|
+
options.contents.auth.password = self._password
|
|
129
|
+
options.contents.auth.password_len = c_size_t(len(self.password))
|
|
130
|
+
|
|
131
|
+
if self.private_key_path is not None:
|
|
132
|
+
self._private_key_path = to_c_string(self.private_key_path)
|
|
133
|
+
|
|
134
|
+
options.contents.auth.private_key_path = self._private_key_path
|
|
135
|
+
options.contents.auth.private_key_path_len = c_size_t(len(self.private_key_path))
|
|
136
|
+
|
|
137
|
+
if self.private_key_passphrase is not None:
|
|
138
|
+
self._private_key_passphrase = to_c_string(self.private_key_passphrase)
|
|
139
|
+
|
|
140
|
+
options.contents.auth.private_key_passphrase = self._private_key_passphrase
|
|
141
|
+
options.contents.auth.private_key_passphrase_len = c_size_t(
|
|
142
|
+
len(self.private_key_passphrase)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if self.private_key_content is not None:
|
|
146
|
+
self._private_key_content = to_c_string(self.private_key_content)
|
|
147
|
+
|
|
148
|
+
options.contents.auth.private_key_content = self._private_key_content
|
|
149
|
+
options.contents.auth.private_key_content_len = c_size_t(len(self.private_key_content))
|
|
150
|
+
|
|
151
|
+
if self.lookups is not None:
|
|
152
|
+
count = len(self.lookups)
|
|
153
|
+
|
|
154
|
+
self._lookup_map_keys = (c_char_p * count)()
|
|
155
|
+
self._lookup_map_key_lens = (c_uint16 * count)()
|
|
156
|
+
self._lookup_map_vals = (c_char_p * count)()
|
|
157
|
+
self._lookup_map_val_lens = (c_uint16 * count)()
|
|
158
|
+
|
|
159
|
+
for i, lookup in enumerate(self.lookups):
|
|
160
|
+
k, v = lookup._get_c_strings()
|
|
161
|
+
|
|
162
|
+
self._lookup_map_keys[i] = k
|
|
163
|
+
self._lookup_map_key_lens[i] = c_uint16(len(lookup.key))
|
|
164
|
+
|
|
165
|
+
self._lookup_map_vals[i] = v
|
|
166
|
+
self._lookup_map_val_lens[i] = c_uint16(len(lookup.value))
|
|
167
|
+
|
|
168
|
+
options.contents.auth.lookups.keys = self._lookup_map_keys
|
|
169
|
+
options.contents.auth.lookups.key_lens = self._lookup_map_key_lens
|
|
170
|
+
|
|
171
|
+
options.contents.auth.lookups.vals = self._lookup_map_vals
|
|
172
|
+
options.contents.auth.lookups.val_lens = self._lookup_map_val_lens
|
|
173
|
+
|
|
174
|
+
options.contents.auth.lookups.count = c_size_t(len(self.lookups))
|
|
175
|
+
|
|
176
|
+
if self.force_in_session_auth is not None:
|
|
177
|
+
options.contents.auth.force_in_session_auth = pointer(
|
|
178
|
+
c_bool(self.force_in_session_auth)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if self.bypass_in_session_auth is not None:
|
|
182
|
+
options.contents.auth.bypass_in_session_auth = pointer(
|
|
183
|
+
c_bool(self.bypass_in_session_auth)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if self.username_pattern is not None:
|
|
187
|
+
self._username_pattern = to_c_string(self.username_pattern)
|
|
188
|
+
|
|
189
|
+
options.contents.auth.username_pattern = self._username_pattern
|
|
190
|
+
options.contents.auth.username_pattern_len = c_size_t(len(self.username_pattern))
|
|
191
|
+
|
|
192
|
+
if self.password_pattern is not None:
|
|
193
|
+
self._password_pattern = to_c_string(self.password_pattern)
|
|
194
|
+
|
|
195
|
+
options.contents.auth.password_pattern = self._password_pattern
|
|
196
|
+
options.contents.auth.password_pattern_len = c_size_t(len(self.password_pattern))
|
|
197
|
+
|
|
198
|
+
if self.private_key_passphrase_pattern is not None:
|
|
199
|
+
self._private_key_passphrase_pattern = to_c_string(self.private_key_passphrase_pattern)
|
|
200
|
+
|
|
201
|
+
options.contents.auth.private_key_passphrase_pattern = (
|
|
202
|
+
self._private_key_passphrase_pattern
|
|
203
|
+
)
|
|
204
|
+
options.contents.auth.private_key_passphrase_pattern_len = c_size_t(
|
|
205
|
+
len(self.private_key_passphrase_pattern)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def __repr__(self) -> str:
|
|
209
|
+
"""
|
|
210
|
+
Magic repr method for Options object
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
N/A
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
str: repr for Options object
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
N/A
|
|
220
|
+
|
|
221
|
+
"""
|
|
222
|
+
return (
|
|
223
|
+
# it will probably be "canonical" to import Options as AuthOptions, so we'll make
|
|
224
|
+
# the repr do that too
|
|
225
|
+
f"Auth{self.__class__.__name__}("
|
|
226
|
+
f"username={self.username!r}, "
|
|
227
|
+
f"password={None if not self.password else 'REDACTED'}, "
|
|
228
|
+
f"private_key_path={self.private_key_path!r}, "
|
|
229
|
+
f"private_key_passphrase={None if not self.private_key_passphrase else 'REDACTED'}, "
|
|
230
|
+
f"private_key_content={None if not self.private_key_content else 'REDACTED'}, "
|
|
231
|
+
f"lookups={self.lookups!r}, "
|
|
232
|
+
f"force_in_session_auth={self.force_in_session_auth!r}, "
|
|
233
|
+
f"bypass_in_session_auth={self.bypass_in_session_auth!r}, "
|
|
234
|
+
f"username_pattern={self.username_pattern!r}, "
|
|
235
|
+
f"password_pattern={self.password_pattern!r}, "
|
|
236
|
+
f"private_key_passphrase_pattern={self.private_key_passphrase_pattern!r})"
|
|
237
|
+
)
|