1 | # py312 |
2 | # requirements: |
3 | # pyeapi==1.0.4 |
4 |
|
5 | import ssl |
6 | import time |
7 | from typing import Literal, TypedDict |
8 | from urllib.parse import urlsplit |
9 |
|
10 | import pyeapi |
11 |
|
12 |
|
13 | class arista_eos(TypedDict): |
14 | host: str |
15 | username: str |
16 | password: str |
17 | port: int |
18 | verify_tls: bool |
19 |
|
20 |
|
21 | def main( |
22 | auth: arista_eos, |
23 | lines: list[str], |
24 | mode: Literal["diff", "commit", "commit_timer"] = "diff", |
25 | session_name: str = "", |
26 | timer_minutes: int = 10, |
27 | ): |
28 | """ |
29 | Push Config |
30 | Apply configuration lines in an EOS config session and return the diff. mode diff only shows the diff and aborts (dry run); commit applies it; commit_timer applies it and rolls it back by itself unless Confirm Commit runs within timer_minutes. Nothing is saved to startup-config: run Save Config (or Confirm Commit) for that. |
31 | """ |
32 | session = session_name or f"windmill-{int(time.time() * 1000)}" |
33 | # A session survives across eAPI calls by name: stage the lines, read the diff, then commit or abort it. |
34 | eos = node(auth) |
35 | eos.run_commands([f"configure session {session}", *lines, "end"]) |
36 | [diff] = eos.run_commands([f"show session-config named {session} diffs"], "text") |
37 | if mode == "commit": |
38 | eos.run_commands([f"configure session {session} commit"]) |
39 | elif mode == "commit_timer": |
40 | m = timer_minutes or 10 |
41 | eos.run_commands( |
42 | [f"configure session {session} commit timer {m // 60:02d}:{m % 60:02d}:00"], |
43 | ) |
44 | else: |
45 | eos.run_commands([f"configure session {session} abort"]) |
46 | return { |
47 | "session": session, |
48 | "mode": mode or "diff", |
49 | "diff": diff["output"], |
50 | "committed": mode in ("commit", "commit_timer"), |
51 | } |
52 |
|
53 |
|
54 | # host is an address or a URL (https: |
55 | # so one is always passed: verifying, or not when the resource unticks verify_tls (EOS ships a self-signed cert). |
56 | def node(auth: arista_eos, timeout: float = 60): |
57 | url = urlsplit(auth["host"] if "://" in auth["host"] else f"https://{auth['host']}") |
58 | context = ssl.create_default_context() |
59 | if auth.get("verify_tls") is False: |
60 | context.check_hostname = False |
61 | context.verify_mode = ssl.CERT_NONE |
62 | return pyeapi.connect( |
63 | transport=url.scheme, |
64 | host=url.hostname, |
65 | port=url.port or auth.get("port") or None, |
66 | username=auth["username"], |
67 | password=auth["password"], |
68 | context=context, |
69 | timeout=timeout, |
70 | return_node=True, |
71 | ) |
72 |
|