maxconn 0.1.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.
- maxconn-0.1.0/.github/workflows/ci.yml +23 -0
- maxconn-0.1.0/.github/workflows/publish.yml +24 -0
- maxconn-0.1.0/.gitignore +13 -0
- maxconn-0.1.0/PKG-INFO +425 -0
- maxconn-0.1.0/README.md +391 -0
- maxconn-0.1.0/pyproject.toml +74 -0
- maxconn-0.1.0/src/maxconn/__init__.py +68 -0
- maxconn-0.1.0/src/maxconn/automation/__init__.py +5 -0
- maxconn-0.1.0/src/maxconn/automation/expect.py +84 -0
- maxconn-0.1.0/src/maxconn/exceptions.py +18 -0
- maxconn-0.1.0/src/maxconn/transport/__init__.py +0 -0
- maxconn-0.1.0/src/maxconn/transport/base.py +162 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/__init__.py +0 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/auth.py +104 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/channel.py +131 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/diffie_hellman.py +105 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/handshake.py +25 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/hostkey.py +45 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/kex.py +109 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/keys.py +49 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/messages.py +35 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/negotiate.py +94 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/packet.py +60 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/session.py +85 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/socket_reader.py +40 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/transport.py +87 -0
- maxconn-0.1.0/src/maxconn/transport/ssh/wire.py +75 -0
- maxconn-0.1.0/src/maxconn/transport/telnet/__init__.py +0 -0
- maxconn-0.1.0/src/maxconn/transport/telnet/negotiation.py +94 -0
- maxconn-0.1.0/src/maxconn/transport/telnet/transport.py +85 -0
- maxconn-0.1.0/tests/__init__.py +1 -0
- maxconn-0.1.0/tests/integration/__init__.py +0 -0
- maxconn-0.1.0/tests/integration/conftest.py +18 -0
- maxconn-0.1.0/tests/integration/ssh_server.py +148 -0
- maxconn-0.1.0/tests/integration/telnet_server.py +107 -0
- maxconn-0.1.0/tests/integration/test_automation_expect.py +19 -0
- maxconn-0.1.0/tests/integration/test_connection.py +23 -0
- maxconn-0.1.0/tests/integration/test_public_api.py +41 -0
- maxconn-0.1.0/tests/integration/test_ssh_auth_password.py +28 -0
- maxconn-0.1.0/tests/integration/test_ssh_auth_publickey.py +33 -0
- maxconn-0.1.0/tests/integration/test_ssh_channel.py +73 -0
- maxconn-0.1.0/tests/integration/test_ssh_diffie_hellman.py +36 -0
- maxconn-0.1.0/tests/integration/test_ssh_handshake.py +16 -0
- maxconn-0.1.0/tests/integration/test_ssh_hostkey.py +37 -0
- maxconn-0.1.0/tests/integration/test_ssh_negotiate.py +29 -0
- maxconn-0.1.0/tests/integration/test_ssh_transport.py +75 -0
- maxconn-0.1.0/tests/integration/test_telnet_transport.py +59 -0
- maxconn-0.1.0/tests/unit/__init__.py +0 -0
- maxconn-0.1.0/tests/unit/test_automation_expect.py +56 -0
- maxconn-0.1.0/tests/unit/test_connection_run.py +109 -0
- maxconn-0.1.0/tests/unit/test_packaging.py +17 -0
- maxconn-0.1.0/tests/unit/test_ssh_diffie_hellman.py +65 -0
- maxconn-0.1.0/tests/unit/test_ssh_handshake.py +45 -0
- maxconn-0.1.0/tests/unit/test_ssh_hostkey.py +87 -0
- maxconn-0.1.0/tests/unit/test_ssh_kex.py +54 -0
- maxconn-0.1.0/tests/unit/test_ssh_keys.py +46 -0
- maxconn-0.1.0/tests/unit/test_ssh_packet.py +60 -0
- maxconn-0.1.0/tests/unit/test_ssh_session.py +85 -0
- maxconn-0.1.0/tests/unit/test_ssh_wire.py +90 -0
- maxconn-0.1.0/tests/unit/test_telnet_negotiation.py +87 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: pip install -e ".[dev]"
|
|
20
|
+
- run: ruff check src tests
|
|
21
|
+
- run: pytest -v
|
|
22
|
+
- run: python -m build
|
|
23
|
+
- run: twine check dist/*
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment: pypi
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- run: python -m pip install --upgrade build twine
|
|
22
|
+
- run: python -m build
|
|
23
|
+
- run: twine check dist/*
|
|
24
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
maxconn-0.1.0/.gitignore
ADDED
maxconn-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: maxconn
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python toolkit for network and infrastructure automation, starting with raw-socket SSH and Telnet.
|
|
5
|
+
Project-URL: Homepage, https://github.com/mmaxjr/maxconn
|
|
6
|
+
Project-URL: Repository, https://github.com/mmaxjr/maxconn
|
|
7
|
+
Project-URL: Issues, https://github.com/mmaxjr/maxconn/issues
|
|
8
|
+
Author: Marcos Max
|
|
9
|
+
Keywords: automation,devops,infrastructure,network,ssh,telnet
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Internet
|
|
19
|
+
Classifier: Topic :: System :: Networking
|
|
20
|
+
Classifier: Topic :: System :: Systems Administration
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
25
|
+
Requires-Dist: cryptography>=42; extra == 'dev'
|
|
26
|
+
Requires-Dist: paramiko>=3.4; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
29
|
+
Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'dev'
|
|
30
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
31
|
+
Provides-Extra: ssh
|
|
32
|
+
Requires-Dist: cryptography>=42; extra == 'ssh'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# MAXCONN
|
|
36
|
+
|
|
37
|
+
## Português
|
|
38
|
+
|
|
39
|
+
Projeto criado por Marcos Max para ser uma biblioteca Python voltada a redes e
|
|
40
|
+
infraestrutura.
|
|
41
|
+
|
|
42
|
+
A ideia do MAXCONN é juntar, aos poucos, as ferramentas que um engenheiro de
|
|
43
|
+
redes ou DevOps usa no dia a dia para automatizar tarefas de rede: conexão em
|
|
44
|
+
equipamentos, execução de comandos, leitura de saída, coleta de dados,
|
|
45
|
+
validação, inventário e, mais adiante, módulos específicos para fornecedores.
|
|
46
|
+
|
|
47
|
+
O início do projeto é a camada de conexão. Hoje o MAXCONN já tem cliente SSH e
|
|
48
|
+
Telnet feitos sobre sockets, sem usar Paramiko, Netmiko, Scrapli ou Telnetlib
|
|
49
|
+
como cliente em runtime.
|
|
50
|
+
|
|
51
|
+
Exemplo:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import maxconn
|
|
55
|
+
|
|
56
|
+
with maxconn.connect(
|
|
57
|
+
"192.0.2.10",
|
|
58
|
+
protocol="ssh",
|
|
59
|
+
username="admin",
|
|
60
|
+
password="secret",
|
|
61
|
+
) as conn:
|
|
62
|
+
result = conn.run("display version", prompt_markers=(">", "#"))
|
|
63
|
+
print(result.text)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Instalação
|
|
67
|
+
|
|
68
|
+
Instalação para desenvolvimento:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/mmaxjr/maxconn
|
|
72
|
+
cd maxconn
|
|
73
|
+
pip install -e ".[dev]"
|
|
74
|
+
pytest -v
|
|
75
|
+
ruff check src tests
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Instalação futura para uso normal:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install maxconn
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Para usar SSH:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install "maxconn[ssh]"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Telnet não puxa dependências extras. SSH usa `cryptography` pelo extra `ssh`.
|
|
91
|
+
Paramiko fica só nos testes, para subir um servidor SSH local e validar o
|
|
92
|
+
cliente do MAXCONN contra uma implementação independente.
|
|
93
|
+
|
|
94
|
+
### Uso Básico
|
|
95
|
+
|
|
96
|
+
Telnet:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import maxconn
|
|
100
|
+
|
|
101
|
+
with maxconn.connect(
|
|
102
|
+
"192.0.2.20",
|
|
103
|
+
protocol="telnet",
|
|
104
|
+
username="admin",
|
|
105
|
+
password="secret",
|
|
106
|
+
) as conn:
|
|
107
|
+
result = conn.run("show status", prompt_markers=(">", "#"))
|
|
108
|
+
print(result.text)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
SSH:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import maxconn
|
|
115
|
+
|
|
116
|
+
with maxconn.connect(
|
|
117
|
+
"192.0.2.30",
|
|
118
|
+
protocol="ssh",
|
|
119
|
+
username="admin",
|
|
120
|
+
password="secret",
|
|
121
|
+
) as conn:
|
|
122
|
+
result = conn.run("show version", prompt_markers=(">", "#"))
|
|
123
|
+
print(result.text)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Para uso mais direto, `Connection.send()`, `Connection.recv()`,
|
|
127
|
+
`Connection.read_until()` e `Connection.send_command()` continuam disponíveis.
|
|
128
|
+
|
|
129
|
+
### Resultado de Comando
|
|
130
|
+
|
|
131
|
+
`Connection.run()` retorna um resultado com campos úteis:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
result = conn.run("display version", prompt_markers=(">", "#"))
|
|
135
|
+
|
|
136
|
+
print(result.command)
|
|
137
|
+
print(result.text)
|
|
138
|
+
print(result.bytes)
|
|
139
|
+
print(result.elapsed)
|
|
140
|
+
print(result.exit_status)
|
|
141
|
+
print(result.ok)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`result.ok` é verdadeiro quando `exit_status` é `None` ou `0`. Em sessões CLI
|
|
145
|
+
interativas, como Telnet e shell SSH, geralmente não existe status de saída,
|
|
146
|
+
então `None` é esperado.
|
|
147
|
+
|
|
148
|
+
### Expect
|
|
149
|
+
|
|
150
|
+
Para automação guiada por prompt, use `ExpectSession` diretamente:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from maxconn.automation import ExpectSession, PromptProfile
|
|
154
|
+
|
|
155
|
+
expect = ExpectSession(conn, prompt_markers=PromptProfile.CISCO)
|
|
156
|
+
output = expect.run("show running-config", timeout=20.0)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`ExpectSession` faz o básico que uma CLI de equipamento costuma precisar:
|
|
160
|
+
|
|
161
|
+
- espera por prompts
|
|
162
|
+
- remove eco do comando
|
|
163
|
+
- responde paginação simples, como `--More--`
|
|
164
|
+
- inclui a saída parcial quando ocorre timeout
|
|
165
|
+
|
|
166
|
+
### Timeouts
|
|
167
|
+
|
|
168
|
+
`connect()` aceita timeouts separados:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
conn = maxconn.connect(
|
|
172
|
+
"192.0.2.30",
|
|
173
|
+
protocol="ssh",
|
|
174
|
+
username="admin",
|
|
175
|
+
password="secret",
|
|
176
|
+
connect_timeout=5.0,
|
|
177
|
+
auth_timeout=10.0,
|
|
178
|
+
command_timeout=5.0,
|
|
179
|
+
prompt_timeout=10.0,
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
O argumento antigo `timeout=` continua funcionando. Quando `connect_timeout` ou
|
|
184
|
+
`auth_timeout` não são informados, `timeout=` é usado como padrão.
|
|
185
|
+
|
|
186
|
+
### Logging
|
|
187
|
+
|
|
188
|
+
A execução de comandos registra eventos pelo logger `maxconn.audit`:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
import logging
|
|
192
|
+
|
|
193
|
+
logging.basicConfig(level=logging.INFO)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Trechos sensíveis com palavras como `password`, `secret`, `token` ou `key` são
|
|
197
|
+
redigidos antes de ir para o log.
|
|
198
|
+
|
|
199
|
+
### Erros
|
|
200
|
+
|
|
201
|
+
Use a hierarquia de exceções do projeto:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
import maxconn
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
with maxconn.connect(
|
|
208
|
+
"192.0.2.30",
|
|
209
|
+
protocol="ssh",
|
|
210
|
+
username="admin",
|
|
211
|
+
password="bad-password",
|
|
212
|
+
) as conn:
|
|
213
|
+
print(conn.run("show status", prompt_markers=(">", "#")).text)
|
|
214
|
+
except maxconn.AuthenticationError:
|
|
215
|
+
print("Login failed")
|
|
216
|
+
except maxconn.ConnectionTimeoutError:
|
|
217
|
+
print("Connection timed out")
|
|
218
|
+
except maxconn.ProtocolError as exc:
|
|
219
|
+
print(f"Protocol problem: {exc}")
|
|
220
|
+
except maxconn.MaxConnError as exc:
|
|
221
|
+
print(f"maxconn error: {exc}")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Direção do Projeto
|
|
225
|
+
|
|
226
|
+
- Não transformar o projeto em wrapper de Paramiko, Netmiko, Scrapli ou Telnetlib.
|
|
227
|
+
- Manter dependências opcionais atrás de extras.
|
|
228
|
+
- Deixar bytes crus disponíveis para quem precisa.
|
|
229
|
+
- Dar uma API simples para o caso comum.
|
|
230
|
+
- Testar com servidores locais de Telnet e SSH sempre que fizer sentido.
|
|
231
|
+
|
|
232
|
+
## English
|
|
233
|
+
|
|
234
|
+
Project created by Marcos Max as a Python library for networking and
|
|
235
|
+
infrastructure work.
|
|
236
|
+
|
|
237
|
+
MAXCONN is meant to grow into a practical toolkit for network engineers and
|
|
238
|
+
DevOps engineers who automate network tasks: connecting to devices, running
|
|
239
|
+
commands, reading output, collecting data, validating state, building inventory,
|
|
240
|
+
and later adding vendor-specific modules.
|
|
241
|
+
|
|
242
|
+
The project starts with the connection layer. Today MAXCONN has SSH and Telnet
|
|
243
|
+
clients built on top of sockets, without using Paramiko, Netmiko, Scrapli, or
|
|
244
|
+
Telnetlib as runtime clients.
|
|
245
|
+
|
|
246
|
+
Example:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
import maxconn
|
|
250
|
+
|
|
251
|
+
with maxconn.connect(
|
|
252
|
+
"192.0.2.10",
|
|
253
|
+
protocol="ssh",
|
|
254
|
+
username="admin",
|
|
255
|
+
password="secret",
|
|
256
|
+
) as conn:
|
|
257
|
+
result = conn.run("display version", prompt_markers=(">", "#"))
|
|
258
|
+
print(result.text)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Installation
|
|
262
|
+
|
|
263
|
+
Development install:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
git clone https://github.com/mmaxjr/maxconn
|
|
267
|
+
cd maxconn
|
|
268
|
+
pip install -e ".[dev]"
|
|
269
|
+
pytest -v
|
|
270
|
+
ruff check src tests
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Future regular install:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
pip install maxconn
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
For SSH:
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
pip install "maxconn[ssh]"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Telnet does not pull extra runtime dependencies. SSH uses `cryptography` through
|
|
286
|
+
the `ssh` extra. Paramiko is test-only and is used to run a local SSH server for
|
|
287
|
+
integration tests.
|
|
288
|
+
|
|
289
|
+
### Basic Usage
|
|
290
|
+
|
|
291
|
+
Telnet:
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
import maxconn
|
|
295
|
+
|
|
296
|
+
with maxconn.connect(
|
|
297
|
+
"192.0.2.20",
|
|
298
|
+
protocol="telnet",
|
|
299
|
+
username="admin",
|
|
300
|
+
password="secret",
|
|
301
|
+
) as conn:
|
|
302
|
+
result = conn.run("show status", prompt_markers=(">", "#"))
|
|
303
|
+
print(result.text)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
SSH:
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
import maxconn
|
|
310
|
+
|
|
311
|
+
with maxconn.connect(
|
|
312
|
+
"192.0.2.30",
|
|
313
|
+
protocol="ssh",
|
|
314
|
+
username="admin",
|
|
315
|
+
password="secret",
|
|
316
|
+
) as conn:
|
|
317
|
+
result = conn.run("show version", prompt_markers=(">", "#"))
|
|
318
|
+
print(result.text)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
For lower-level use, `Connection.send()`, `Connection.recv()`,
|
|
322
|
+
`Connection.read_until()`, and `Connection.send_command()` are still available.
|
|
323
|
+
|
|
324
|
+
### Command Result
|
|
325
|
+
|
|
326
|
+
`Connection.run()` returns a result object:
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
result = conn.run("display version", prompt_markers=(">", "#"))
|
|
330
|
+
|
|
331
|
+
print(result.command)
|
|
332
|
+
print(result.text)
|
|
333
|
+
print(result.bytes)
|
|
334
|
+
print(result.elapsed)
|
|
335
|
+
print(result.exit_status)
|
|
336
|
+
print(result.ok)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
`result.ok` is true when `exit_status` is `None` or `0`. Interactive CLI
|
|
340
|
+
sessions, such as Telnet and shell-style SSH, usually do not provide an exit
|
|
341
|
+
status, so `None` is expected.
|
|
342
|
+
|
|
343
|
+
### Expect
|
|
344
|
+
|
|
345
|
+
For prompt-based automation, use `ExpectSession` directly:
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
from maxconn.automation import ExpectSession, PromptProfile
|
|
349
|
+
|
|
350
|
+
expect = ExpectSession(conn, prompt_markers=PromptProfile.CISCO)
|
|
351
|
+
output = expect.run("show running-config", timeout=20.0)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`ExpectSession` handles the common parts of a network device CLI:
|
|
355
|
+
|
|
356
|
+
- waits for prompts
|
|
357
|
+
- strips command echo
|
|
358
|
+
- answers simple pagination markers such as `--More--`
|
|
359
|
+
- includes partial output in timeout errors
|
|
360
|
+
|
|
361
|
+
### Timeouts
|
|
362
|
+
|
|
363
|
+
`connect()` accepts separate timeouts:
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
conn = maxconn.connect(
|
|
367
|
+
"192.0.2.30",
|
|
368
|
+
protocol="ssh",
|
|
369
|
+
username="admin",
|
|
370
|
+
password="secret",
|
|
371
|
+
connect_timeout=5.0,
|
|
372
|
+
auth_timeout=10.0,
|
|
373
|
+
command_timeout=5.0,
|
|
374
|
+
prompt_timeout=10.0,
|
|
375
|
+
)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
The older `timeout=` argument still works. When `connect_timeout` or
|
|
379
|
+
`auth_timeout` is not provided, `timeout=` is used as the default.
|
|
380
|
+
|
|
381
|
+
### Logging
|
|
382
|
+
|
|
383
|
+
Command execution writes audit events through the `maxconn.audit` logger:
|
|
384
|
+
|
|
385
|
+
```python
|
|
386
|
+
import logging
|
|
387
|
+
|
|
388
|
+
logging.basicConfig(level=logging.INFO)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Command fragments with words such as `password`, `secret`, `token`, or `key`
|
|
392
|
+
are redacted before logging.
|
|
393
|
+
|
|
394
|
+
### Errors
|
|
395
|
+
|
|
396
|
+
Use the project exception hierarchy:
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
import maxconn
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
with maxconn.connect(
|
|
403
|
+
"192.0.2.30",
|
|
404
|
+
protocol="ssh",
|
|
405
|
+
username="admin",
|
|
406
|
+
password="bad-password",
|
|
407
|
+
) as conn:
|
|
408
|
+
print(conn.run("show status", prompt_markers=(">", "#")).text)
|
|
409
|
+
except maxconn.AuthenticationError:
|
|
410
|
+
print("Login failed")
|
|
411
|
+
except maxconn.ConnectionTimeoutError:
|
|
412
|
+
print("Connection timed out")
|
|
413
|
+
except maxconn.ProtocolError as exc:
|
|
414
|
+
print(f"Protocol problem: {exc}")
|
|
415
|
+
except maxconn.MaxConnError as exc:
|
|
416
|
+
print(f"maxconn error: {exc}")
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Project Direction
|
|
420
|
+
|
|
421
|
+
- Do not turn the project into a wrapper around Paramiko, Netmiko, Scrapli, or Telnetlib.
|
|
422
|
+
- Keep optional dependencies behind extras.
|
|
423
|
+
- Keep raw bytes available for code that needs them.
|
|
424
|
+
- Keep the common API simple.
|
|
425
|
+
- Test against local Telnet and SSH servers when it makes sense.
|