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.
Files changed (60) hide show
  1. maxconn-0.1.0/.github/workflows/ci.yml +23 -0
  2. maxconn-0.1.0/.github/workflows/publish.yml +24 -0
  3. maxconn-0.1.0/.gitignore +13 -0
  4. maxconn-0.1.0/PKG-INFO +425 -0
  5. maxconn-0.1.0/README.md +391 -0
  6. maxconn-0.1.0/pyproject.toml +74 -0
  7. maxconn-0.1.0/src/maxconn/__init__.py +68 -0
  8. maxconn-0.1.0/src/maxconn/automation/__init__.py +5 -0
  9. maxconn-0.1.0/src/maxconn/automation/expect.py +84 -0
  10. maxconn-0.1.0/src/maxconn/exceptions.py +18 -0
  11. maxconn-0.1.0/src/maxconn/transport/__init__.py +0 -0
  12. maxconn-0.1.0/src/maxconn/transport/base.py +162 -0
  13. maxconn-0.1.0/src/maxconn/transport/ssh/__init__.py +0 -0
  14. maxconn-0.1.0/src/maxconn/transport/ssh/auth.py +104 -0
  15. maxconn-0.1.0/src/maxconn/transport/ssh/channel.py +131 -0
  16. maxconn-0.1.0/src/maxconn/transport/ssh/diffie_hellman.py +105 -0
  17. maxconn-0.1.0/src/maxconn/transport/ssh/handshake.py +25 -0
  18. maxconn-0.1.0/src/maxconn/transport/ssh/hostkey.py +45 -0
  19. maxconn-0.1.0/src/maxconn/transport/ssh/kex.py +109 -0
  20. maxconn-0.1.0/src/maxconn/transport/ssh/keys.py +49 -0
  21. maxconn-0.1.0/src/maxconn/transport/ssh/messages.py +35 -0
  22. maxconn-0.1.0/src/maxconn/transport/ssh/negotiate.py +94 -0
  23. maxconn-0.1.0/src/maxconn/transport/ssh/packet.py +60 -0
  24. maxconn-0.1.0/src/maxconn/transport/ssh/session.py +85 -0
  25. maxconn-0.1.0/src/maxconn/transport/ssh/socket_reader.py +40 -0
  26. maxconn-0.1.0/src/maxconn/transport/ssh/transport.py +87 -0
  27. maxconn-0.1.0/src/maxconn/transport/ssh/wire.py +75 -0
  28. maxconn-0.1.0/src/maxconn/transport/telnet/__init__.py +0 -0
  29. maxconn-0.1.0/src/maxconn/transport/telnet/negotiation.py +94 -0
  30. maxconn-0.1.0/src/maxconn/transport/telnet/transport.py +85 -0
  31. maxconn-0.1.0/tests/__init__.py +1 -0
  32. maxconn-0.1.0/tests/integration/__init__.py +0 -0
  33. maxconn-0.1.0/tests/integration/conftest.py +18 -0
  34. maxconn-0.1.0/tests/integration/ssh_server.py +148 -0
  35. maxconn-0.1.0/tests/integration/telnet_server.py +107 -0
  36. maxconn-0.1.0/tests/integration/test_automation_expect.py +19 -0
  37. maxconn-0.1.0/tests/integration/test_connection.py +23 -0
  38. maxconn-0.1.0/tests/integration/test_public_api.py +41 -0
  39. maxconn-0.1.0/tests/integration/test_ssh_auth_password.py +28 -0
  40. maxconn-0.1.0/tests/integration/test_ssh_auth_publickey.py +33 -0
  41. maxconn-0.1.0/tests/integration/test_ssh_channel.py +73 -0
  42. maxconn-0.1.0/tests/integration/test_ssh_diffie_hellman.py +36 -0
  43. maxconn-0.1.0/tests/integration/test_ssh_handshake.py +16 -0
  44. maxconn-0.1.0/tests/integration/test_ssh_hostkey.py +37 -0
  45. maxconn-0.1.0/tests/integration/test_ssh_negotiate.py +29 -0
  46. maxconn-0.1.0/tests/integration/test_ssh_transport.py +75 -0
  47. maxconn-0.1.0/tests/integration/test_telnet_transport.py +59 -0
  48. maxconn-0.1.0/tests/unit/__init__.py +0 -0
  49. maxconn-0.1.0/tests/unit/test_automation_expect.py +56 -0
  50. maxconn-0.1.0/tests/unit/test_connection_run.py +109 -0
  51. maxconn-0.1.0/tests/unit/test_packaging.py +17 -0
  52. maxconn-0.1.0/tests/unit/test_ssh_diffie_hellman.py +65 -0
  53. maxconn-0.1.0/tests/unit/test_ssh_handshake.py +45 -0
  54. maxconn-0.1.0/tests/unit/test_ssh_hostkey.py +87 -0
  55. maxconn-0.1.0/tests/unit/test_ssh_kex.py +54 -0
  56. maxconn-0.1.0/tests/unit/test_ssh_keys.py +46 -0
  57. maxconn-0.1.0/tests/unit/test_ssh_packet.py +60 -0
  58. maxconn-0.1.0/tests/unit/test_ssh_session.py +85 -0
  59. maxconn-0.1.0/tests/unit/test_ssh_wire.py +90 -0
  60. 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
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ htmlcov/
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.