bytesim 0.1.0__py3-none-any.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.
- bytesim/__init__.py +14 -0
- bytesim/__main__.py +3 -0
- bytesim/cli.py +45 -0
- bytesim/constants.py +48 -0
- bytesim/demos.py +74 -0
- bytesim/model/__init__.py +5 -0
- bytesim/model/addressing.py +160 -0
- bytesim/model/base.py +57 -0
- bytesim/model/cloning.py +249 -0
- bytesim/model/equivalence.py +86 -0
- bytesim/model/groups.py +133 -0
- bytesim/model/ha.py +93 -0
- bytesim/model/layer2.py +106 -0
- bytesim/model/layout.py +203 -0
- bytesim/model/naming.py +114 -0
- bytesim/model/network.py +138 -0
- bytesim/model/routing.py +117 -0
- bytesim/model/stp.py +315 -0
- bytesim/model/types.py +107 -0
- bytesim/py.typed +0 -0
- bytesim/scripting.py +108 -0
- bytesim/synthesis.py +644 -0
- bytesim/topology.py +517 -0
- bytesim/tui/__init__.py +4 -0
- bytesim/tui/app.py +161 -0
- bytesim/tui/cables.py +58 -0
- bytesim/tui/canvas.py +489 -0
- bytesim/tui/command_input.py +29 -0
- bytesim/tui/commands/__init__.py +1 -0
- bytesim/tui/commands/addressing.py +235 -0
- bytesim/tui/commands/base.py +43 -0
- bytesim/tui/commands/groups.py +215 -0
- bytesim/tui/commands/history.py +134 -0
- bytesim/tui/commands/interpreter.py +85 -0
- bytesim/tui/commands/naming.py +68 -0
- bytesim/tui/commands/registry.py +28 -0
- bytesim/tui/commands/scripting.py +54 -0
- bytesim/tui/commands/switching.py +192 -0
- bytesim/tui/commands/topology.py +255 -0
- bytesim/tui/commands/traffic.py +312 -0
- bytesim/tui/help.py +50 -0
- bytesim/tui/inspector.py +115 -0
- bytesim/tui/theme.py +12 -0
- bytesim-0.1.0.dist-info/METADATA +178 -0
- bytesim-0.1.0.dist-info/RECORD +48 -0
- bytesim-0.1.0.dist-info/WHEEL +4 -0
- bytesim-0.1.0.dist-info/entry_points.txt +2 -0
- bytesim-0.1.0.dist-info/licenses/LICENSE +21 -0
bytesim/tui/inspector.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""The inspector panel: a text summary of the selection (Rich markup)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ..constants import KINDS, VIP_HOST
|
|
5
|
+
from ..model import Iface, Network, fmt
|
|
6
|
+
from .theme import HA_COLOR, ROLE_COLOR, group_color
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def mode_text(ifc: Iface) -> str:
|
|
10
|
+
"""How an interface got its address: dhcp, adhoc, static, auto, or dhcp→adhoc."""
|
|
11
|
+
if ifc.mode == "dhcp" and ifc.via == "adhoc":
|
|
12
|
+
return "dhcp→adhoc"
|
|
13
|
+
return ifc.via or ifc.mode
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def render_inspector(net: Network, sel: str | None, selected_group: str | None,
|
|
17
|
+
graph: bool, auto: bool, naming: str = "off") -> str:
|
|
18
|
+
out = [f"mode: [b]{'graph' if graph else 'addr'}[/] · auto-arrange: "
|
|
19
|
+
f"{'[green]on[/]' if auto else '[yellow]off[/]'}",
|
|
20
|
+
f"auto-renumber: {'[dim]off[/]' if naming == 'off' else f'[cyan]{naming}[/]'}",
|
|
21
|
+
f"{len(net.devices)} devices · {len(net.links)} cables", ""]
|
|
22
|
+
if net.storming:
|
|
23
|
+
out[-1:] = [f"[b red]⚡ broadcast storm:[/] [red]{' '.join(sorted(net.storming, key=net.bridge_id))}[/]", ""]
|
|
24
|
+
for k, (name, _, color) in KINDS.items():
|
|
25
|
+
ids = [d.id for d in net.sorted_devices() if d.kind == k]
|
|
26
|
+
out.append(f"[{color}]■ {name}{'es' if name.endswith('h') else 's'}[/] " + (" ".join(ids) if ids else "[dim]—[/]"))
|
|
27
|
+
def nest(g: str) -> str:
|
|
28
|
+
subs = net.subgroups(g)
|
|
29
|
+
return f"[{group_color(g)}]{g}[/]" + (" ‹" + " ".join(nest(x) for x in subs) + "›" if subs else "")
|
|
30
|
+
|
|
31
|
+
tops = [g for g in net.groups if net.parent_of(g) is None]
|
|
32
|
+
out.append("[b]┆ groups[/] " + (" ".join(nest(g) for g in tops) if tops else "[dim]—[/]"))
|
|
33
|
+
out.append("")
|
|
34
|
+
gsel = selected_group
|
|
35
|
+
if gsel and gsel in net.groups:
|
|
36
|
+
members = net.devices_in(gsel)
|
|
37
|
+
inside = sum(1 for l in net.links if l.a in members and l.b in members)
|
|
38
|
+
outside = sum(1 for l in net.links if (l.a in members) != (l.b in members))
|
|
39
|
+
out.append(f"[b reverse {group_color(gsel)}] {gsel} [/] [b]group[/] {len(members)} devices")
|
|
40
|
+
if (chain := net.chain(gsel)):
|
|
41
|
+
out.append(f"[dim]inside {' › '.join(chain)}[/]")
|
|
42
|
+
for sub in net.subgroups(gsel):
|
|
43
|
+
out.append(f" [{group_color(sub)}]▣ {sub:<10}[/] {len(net.devices_in(sub))} devices")
|
|
44
|
+
out.append(f"[dim]{inside} internal · {outside} external cables[/]")
|
|
45
|
+
if gsel in net.clusters and not graph:
|
|
46
|
+
st = net.ha_state(gsel)
|
|
47
|
+
out.append(f"[b]HA cluster[/] · policy {st['policy']}")
|
|
48
|
+
out.append(f"[{HA_COLOR[st['level']]}]{st['text']}[/]")
|
|
49
|
+
out.append(f"control links {st['ctrl_up']}/{st['ctrl']} up · "
|
|
50
|
+
f"{len(st['partitions'])} partition{'s' if len(st['partitions']) != 1 else ''}")
|
|
51
|
+
for netid, owners in sorted(st["owners"].items()):
|
|
52
|
+
out.append(f" {fmt((netid, VIP_HOST))} → {' + '.join(o[0] for o in owners)}")
|
|
53
|
+
for did in [m for m in net.groups[gsel] if m in net.devices]:
|
|
54
|
+
d = net.devices[did]
|
|
55
|
+
used = sum(1 for l in net.links if l.touches(did))
|
|
56
|
+
out.append(f" [{d.color}]■ {did:<5}[/] {d.name:<8} {used}/{d.nports}"
|
|
57
|
+
+ (" [red]✕ down[/]" if not d.up else ""))
|
|
58
|
+
elif sel and sel in net.devices:
|
|
59
|
+
d = net.devices[sel]
|
|
60
|
+
used = sum(1 for l in net.links if l.touches(d.id))
|
|
61
|
+
kind = "NICs" if d.kind == "c" else "ports"
|
|
62
|
+
chain = net.chain(d.id)
|
|
63
|
+
g = chain[-1] if chain else None
|
|
64
|
+
out.append(f"[b reverse {d.color}] {d.id} [/] [b]{d.name}[/] {used}/{d.nports} {kind}"
|
|
65
|
+
+ (" [b red]✕ DOWN[/]" if not d.up else ""))
|
|
66
|
+
out.append(f"[dim]position {d.x},{d.y}[/]"
|
|
67
|
+
+ (f" [{group_color(g)}]{' › '.join(chain)}[/]" if g else ""))
|
|
68
|
+
if d.kind in "rg" and not graph:
|
|
69
|
+
out.append("DHCP server " + ("[red]off[/]" if d.id in net.dhcp_off else "[green]on[/]"))
|
|
70
|
+
if d.kind == "g":
|
|
71
|
+
out.append("[magenta]☁ uplink to the internet[/]")
|
|
72
|
+
for p in range(1, d.nports + 1):
|
|
73
|
+
l = net.link_on(d.id, p)
|
|
74
|
+
if l:
|
|
75
|
+
o, op = l.other(d.id)
|
|
76
|
+
od = net.devices[o]
|
|
77
|
+
tag = f" [{ROLE_COLOR[l.role]}]{l.role}[/]" if l.role != "data" else ""
|
|
78
|
+
mark = "[b red]✕[/]" if not l.up else "[b]●[/]"
|
|
79
|
+
if d.kind == "s" and (role := net.port_role(d.id, p)):
|
|
80
|
+
tag += {"blocking": " [b yellow]⊘ blocking[/]", "root": " [cyan]root[/]",
|
|
81
|
+
"designated": " [dim]desig[/]", "edge": " [dim]edge[/]"}[role]
|
|
82
|
+
if (d.id, p) in net.stp_blocked:
|
|
83
|
+
mark = "[b yellow]⊘[/]"
|
|
84
|
+
out.append(f" {mark} {d.port_name(p):<5} → [{od.color}]{o}[/]:{od.port_name(op)}{tag}")
|
|
85
|
+
else:
|
|
86
|
+
out.append(f" [grey42]○ {d.port_name(p):<5} free[/]")
|
|
87
|
+
ifc = net.ifaces.get(f"{d.id}:{p}")
|
|
88
|
+
line = f" [dim]{d.mac(p):<6}[/]"
|
|
89
|
+
if d.kind != "s" and ifc and not graph and (ifc.addr or ifc.mode in ("static", "none")):
|
|
90
|
+
mode = mode_text(ifc)
|
|
91
|
+
color = {"dhcp": "gold1", "static": "cyan", "auto": "grey62", "none": "red"}.get(mode, "yellow")
|
|
92
|
+
line += f" {fmt(ifc.addr)} [{color}]{mode}[/]"
|
|
93
|
+
if ifc.gw is not None:
|
|
94
|
+
line += f" [dim]gw {ifc.gw}[/]"
|
|
95
|
+
out.append(line)
|
|
96
|
+
if d.kind == "s":
|
|
97
|
+
stp = (f"STP [green]on[/] · root bridge [cyan]{net.stp_roots.get(d.id, d.id)}[/]"
|
|
98
|
+
if net.stp_enabled(d.id) else "STP [yellow]off[/]")
|
|
99
|
+
prio = net.stp_priority.get(d.id)
|
|
100
|
+
out.append(stp + (f" · priority {prio}" if prio is not None else ""))
|
|
101
|
+
if d.id in net.storming:
|
|
102
|
+
out.append("[b red]⚡ STORMING: every port saturated[/]")
|
|
103
|
+
for mac, ports in sorted(net.mac_flaps.get(d.id, {}).items()):
|
|
104
|
+
out.append(f"[orange1]MAC {mac} flapping {'⇄'.join(f'p{q}' for q in sorted(ports))}[/]")
|
|
105
|
+
table = net.mac_tables.get(d.id, {})
|
|
106
|
+
out.append("")
|
|
107
|
+
out.append(f"[b]MAC table[/] [dim]({len(table)} learned)[/]")
|
|
108
|
+
for mac, port in sorted(table.items(), key=lambda t: (t[1], t[0])):
|
|
109
|
+
out.append(f" {d.port_name(port):<4} {mac}")
|
|
110
|
+
if not table:
|
|
111
|
+
out.append(" [dim]empty: send some traffic[/]")
|
|
112
|
+
else:
|
|
113
|
+
out.append("[dim]nothing selected[/]")
|
|
114
|
+
out.append("[dim]tab / click / type an id or group[/]")
|
|
115
|
+
return "\n".join(out)
|
bytesim/tui/theme.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Colours for cable roles, cluster health and groups."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
GROUP_COLORS = ["orange1", "orchid", "spring_green2", "gold1", "deep_sky_blue1", "salmon1"]
|
|
5
|
+
|
|
6
|
+
ROLE_COLOR = {"data": "grey50", "control": "orchid", "control-backup": "orchid", "fabric": "dark_cyan"}
|
|
7
|
+
|
|
8
|
+
HA_COLOR = {"ok": "green", "degraded": "orange1", "split": "bold red", "down": "red"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def group_color(name: str) -> str:
|
|
12
|
+
return GROUP_COLORS[sum(map(ord, name)) % len(GROUP_COLORS)]
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bytesim
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A text-mode network simulator: cable up gateways, routers, switches and computers, then watch DHCP, routing, multicast and HA failover in your terminal.
|
|
5
|
+
Project-URL: Homepage, https://github.com/BytesAndCoffee/bytesim
|
|
6
|
+
Project-URL: Documentation, https://github.com/BytesAndCoffee/bytesim/tree/main/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/BytesAndCoffee/bytesim
|
|
8
|
+
Project-URL: Issues, https://github.com/BytesAndCoffee/bytesim/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/BytesAndCoffee/bytesim/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Michael Yazdani
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: dhcp,education,network,networking,routing,simulator,textual,tui
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Education
|
|
18
|
+
Classifier: Intended Audience :: System Administrators
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
27
|
+
Classifier: Topic :: Education
|
|
28
|
+
Classifier: Topic :: System :: Networking
|
|
29
|
+
Classifier: Typing :: Typed
|
|
30
|
+
Requires-Python: >=3.10
|
|
31
|
+
Requires-Dist: pyyaml>=6.0
|
|
32
|
+
Requires-Dist: textual>=6.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: build; extra == 'dev'
|
|
35
|
+
Requires-Dist: pyflakes; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
37
|
+
Requires-Dist: twine; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# ByteSim
|
|
41
|
+
|
|
42
|
+
[](https://github.com/BytesAndCoffee/bytesim/actions/workflows/ci.yml)
|
|
43
|
+
[](https://pypi.org/project/bytesim/)
|
|
44
|
+
[](https://pypi.org/project/bytesim/)
|
|
45
|
+
|
|
46
|
+
**A network simulator that runs in your terminal.** Add gateways, routers,
|
|
47
|
+
switches and computers, cable them together by typing device ids, and watch
|
|
48
|
+
DHCP, routing, multicast and router failover happen packet by packet.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
╭┄ rfab1 · HA ┄┄┄┄┄┄┄┄┄┄┄┄╮
|
|
52
|
+
┆ ┌────────┐ ┌────────┐ ┆
|
|
53
|
+
┆ │ r1 │ │ r2 │ ┆
|
|
54
|
+
┆ │ ●●○●●● │ │ ●●○●●● │ ┆
|
|
55
|
+
┆ └─┬┬─┬┬┬─┘ └─┬┬─┬┬┬─┘ ┆
|
|
56
|
+
╰┄┄┄││┄┃║║┄┄┄┄┄┄┄┄││┄┃║║┄┄┄╯
|
|
57
|
+
┌──────────────┘└─┴┴┼──────────┼┼─┴┤║
|
|
58
|
+
│ ┌─────┴──────────┴┴──┼┴────────────┐
|
|
59
|
+
╭┄┄┄┄│ fab1 ┄┄┄┄┄┄┄│┄┄┄┄┄┄┄╮ ╭┄┄┄┄│ fab2 ┄┄┄┄┄┄┄│┄┄┄┄┄┄┄╮
|
|
60
|
+
┆ ┌──┴─────┐ ┌──┴─────┐ ┆ ┆ ┌──┴─────┐ ┌──┴─────┐ ┆
|
|
61
|
+
┆ │ s1 │ │ s2 │ ┆ ┆ │ s3 │ │ s4 │ ┆
|
|
62
|
+
┆ │ ●●●●○○ │ │ ●●●○○○ │ ┆ ┆ │ ●●●●○○ │ │ ●●●○○○ │ ┆
|
|
63
|
+
┆ └─┬─┬┬───┘ └─┬─┬────┘ ┆ ┆ └─┬─┬┬───┘ └─┬─┬────┘ ┆
|
|
64
|
+
╰┄┄┄│┄││┄┄┄┄┄┄┄┄┄┄│┄│┄┄┄┄┄┄╯ ╰┄┄┄│┄││┄┄┄┄┄┄┄┄┄┄│┄│┄┄┄┄┄┄╯
|
|
65
|
+
└─●•••─────┬─┘ │ │ ││ │ └─────┐
|
|
66
|
+
┌─┴──┐ ┌┴───┐ ┌────┐ ┌─┴──┐ ┌────┐ ┌────┐
|
|
67
|
+
│ c1 │ │ c2 │ │ c3 │ │ c4 │ │ c5 │ │ c6 │
|
|
68
|
+
└────┘ └────┘ └────┘ └────┘ └────┘ └────┘
|
|
69
|
+
(1,10) (1,11) (1,12) (2,10) (2,11) (2,12)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
ByteSim keeps what matters about networking (graphs, broadcast domains,
|
|
73
|
+
forwarding, failure) and leaves out the detail that gets in the way:
|
|
74
|
+
|
|
75
|
+
- **Devices with real ports.** Routers and switches can have any number of
|
|
76
|
+
ports, and computers can have several network cards. Every port shows on
|
|
77
|
+
the device as `○` (free) or `●` (in use).
|
|
78
|
+
- **Cabling by typing.** `r1 s2` connects two devices using their first free
|
|
79
|
+
ports. `r1:3 s2:8` picks the ports.
|
|
80
|
+
- **Automatic layout**, with arrow keys or the mouse to move things yourself.
|
|
81
|
+
- **Simple addressing.** An address is a pair: a network id and an 8-bit host
|
|
82
|
+
number, like `(2,14)`. MAC addresses name the physical port: `R1A` is port 1
|
|
83
|
+
of router r1.
|
|
84
|
+
- **DHCP and ad-hoc addressing**, animated as broadcast-then-reply.
|
|
85
|
+
- **Routing** through gateways and between routers, with ARP shown for every hop.
|
|
86
|
+
- **Unicast, ping and multicast.** With multicast you can see the packet copied
|
|
87
|
+
wherever the paths split.
|
|
88
|
+
- **Cable roles** (data, control, fabric) and failures you can trigger by hand.
|
|
89
|
+
- **Switching loops**: spanning tree with root bridges and blocked ports, and
|
|
90
|
+
real broadcast storms (frames multiplying, MAC tables flapping) when STP is off.
|
|
91
|
+
- **HA router clusters** with a shared virtual gateway, failover, split-brain,
|
|
92
|
+
and quorum or witness policies.
|
|
93
|
+
- **Nested groups** drawn as labelled frames, which you can **clone** along with
|
|
94
|
+
their internal cables.
|
|
95
|
+
- **BFS/DFS renumbering**, once or automatically after every change, so ids
|
|
96
|
+
follow the network's shape.
|
|
97
|
+
- **Graph mode**, which ignores addressing so you can explore pure connectivity.
|
|
98
|
+
- **Topology files.** Describe networks in YAML with reusable templates and link
|
|
99
|
+
macros (`mesh: [routers, switches]`), then load or export them.
|
|
100
|
+
- **Construction scripts.** `export script` writes the console commands that
|
|
101
|
+
rebuild your network, reusing repeated parts with `clone` and `copy`. Each
|
|
102
|
+
script is checked by replaying it before you get it, and `run` replays it.
|
|
103
|
+
|
|
104
|
+
## Install
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
pip install bytesim
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Requires Python 3.10+ and a terminal with Unicode and colour support.
|
|
111
|
+
|
|
112
|
+
## Quick start
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
bytesim --demo basic # a small routed network
|
|
116
|
+
bytesim --demo ha # a clustered router pair between two fabrics
|
|
117
|
+
bytesim --demo loop # a switch loop with STP off: set off a storm
|
|
118
|
+
bytesim mynet.json # open a saved network
|
|
119
|
+
bytesim campus.yaml # build one from a topology file
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Then, at the command line at the bottom of the screen:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
add r 4 add a router with 4 ports → r1
|
|
126
|
+
add s 8 add an 8-port switch → s1
|
|
127
|
+
add c 1 3 add three computers with 1 NIC → c1 c2 c3
|
|
128
|
+
r1 s1 cable r1 to s1
|
|
129
|
+
s1 c1 cable s1 to c1 (c1 gets an address by DHCP)
|
|
130
|
+
ping c1 c2 watch the echo request and reply
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Type `help` for every command. Press `Esc` to switch from the command line to
|
|
134
|
+
the canvas, where the arrow keys move the selected device and `Tab` cycles
|
|
135
|
+
through devices.
|
|
136
|
+
|
|
137
|
+
## Documentation
|
|
138
|
+
|
|
139
|
+
- [User guide](docs/guide.md): concepts, every command, keys, worked examples
|
|
140
|
+
- [Architecture](docs/architecture.md): how the code is organised, and how to add a command
|
|
141
|
+
- [Topology format](docs/topology-format.md): templates and link macros in YAML
|
|
142
|
+
- [Construction scripts](docs/scripts.md): `export script` and `run`, with a tutorial
|
|
143
|
+
- [Save file format](docs/file-format.md)
|
|
144
|
+
- [Changelog](CHANGELOG.md)
|
|
145
|
+
|
|
146
|
+
## Using the model from Python
|
|
147
|
+
|
|
148
|
+
The simulation is a plain Python model with no UI dependency:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from bytesim import Network
|
|
152
|
+
|
|
153
|
+
net = Network()
|
|
154
|
+
net.add("r"); net.add("s"); net.add("c"); net.add("c")
|
|
155
|
+
net.connect("r1", None, "s1", None)
|
|
156
|
+
net.connect("s1", None, "c1", None)
|
|
157
|
+
net.connect("s1", None, "c2", None)
|
|
158
|
+
net.reconcile() # router autoconfig + DHCP
|
|
159
|
+
|
|
160
|
+
print(net.iface("c1", 1).addr) # (1, 10)
|
|
161
|
+
for src, dst, frames in net.route("c1", ("c2", 1)):
|
|
162
|
+
print(src, "→", dst, [d for d, _ in frames])
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Development
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
git clone https://github.com/BytesAndCoffee/bytesim
|
|
169
|
+
cd bytesim
|
|
170
|
+
python -m venv .venv && . .venv/bin/activate
|
|
171
|
+
pip install -e ".[dev]"
|
|
172
|
+
pytest
|
|
173
|
+
python -m build && twine check dist/*
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT © Michael Yazdani. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
bytesim/__init__.py,sha256=Ewcp6sGbLRMp8bLmlBrcXTSYrWs1YV4UPhXa7XL6Eu4,399
|
|
2
|
+
bytesim/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
|
|
3
|
+
bytesim/cli.py,sha256=QFSKs828d-ymEjIKIIduYOq9-f6xIhtXcaNV1hxW1iY,1836
|
|
4
|
+
bytesim/constants.py,sha256=8YIsJ-NK3kPJlL3ivFlS0YDNUEYOtNuGv-Z4CKjdoUA,1513
|
|
5
|
+
bytesim/demos.py,sha256=mAIVbhGytT_99Tx9-7sydvVPk5_309PsBIAkgzm7nN0,3252
|
|
6
|
+
bytesim/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
bytesim/scripting.py,sha256=3qMfukdImjbHqYJOAmJKm0sm5MQO9N9oLSiCs6CImlU,3776
|
|
8
|
+
bytesim/synthesis.py,sha256=SyHIREMxyoIHxD28NmHDumFPSYmnVA8qwq3vzhrs7OM,27960
|
|
9
|
+
bytesim/topology.py,sha256=wuxqPGF4xLC34gD1eEt3dFEEitskg1uHqI6NRtC_DfQ,24676
|
|
10
|
+
bytesim/model/__init__.py,sha256=GfNW68cYmWQvteIJvTnBJEJrpk-CsMWTp-2wajNgCmU,254
|
|
11
|
+
bytesim/model/addressing.py,sha256=ugs7nbObzz4tDgXJK-ylDNdNXBGRzvrXpe1h2ZGw96M,7471
|
|
12
|
+
bytesim/model/base.py,sha256=v9KZyE_TY-bw3VxOsGBa3B7DHO9h_Qb7YcmWuoU_C_0,2714
|
|
13
|
+
bytesim/model/cloning.py,sha256=y4OzZKBTa5fRiHeqjWKdWzqcWiPRpzK7E6VQ3J2P8Mg,12480
|
|
14
|
+
bytesim/model/equivalence.py,sha256=njKmkvPnpl5kJqXwZ4o4vWWYNMinsLTf9rw0JBvxE1c,3668
|
|
15
|
+
bytesim/model/groups.py,sha256=Huk3BeVoRtNydnRJBJ27kfFzPGH84-W2NjUh2qxf058,5928
|
|
16
|
+
bytesim/model/ha.py,sha256=56FGlLgFroB7AK3z95atcW6fi71xM0DskGVIZ4D8AkA,4643
|
|
17
|
+
bytesim/model/layer2.py,sha256=4f9PuTg4jaIlXB4hPc6PgrnMSRm3t-eDFFvhXmLIXAc,4404
|
|
18
|
+
bytesim/model/layout.py,sha256=DMMRU9DVZCdaDN_ycO-4OfP71nxefOX6wStt0Tt0B2w,9064
|
|
19
|
+
bytesim/model/naming.py,sha256=tIc9nqLitoMdVdBvmAALI4437WCZ2Iv_47G1onV_IMk,4928
|
|
20
|
+
bytesim/model/network.py,sha256=TYU552egQtsLbasw9WUkI8Kn6x_ia25lK0ZKVbHC3FY,5818
|
|
21
|
+
bytesim/model/routing.py,sha256=eh5vTFl7SWlokia8vBdM1q_KjZG3_RGc0Ki84uQ8ScE,5382
|
|
22
|
+
bytesim/model/stp.py,sha256=p9RLiCBvJSn5oeD4sl_2blff7m4i3GX9ucUjIaSVCzw,14393
|
|
23
|
+
bytesim/model/types.py,sha256=NoYyF5ZfWa-URe30wzqe4VlfTdxcXuGOv6-s2bZS4o0,2781
|
|
24
|
+
bytesim/tui/__init__.py,sha256=OIBGeSC0sJ0ybcCzFky9U8slgG03-6Lo92UZI7H6ddM,88
|
|
25
|
+
bytesim/tui/app.py,sha256=czaWUrz8jy5slmQqsgfpKJQNQuosuoF6bqJvs2P8RbA,6589
|
|
26
|
+
bytesim/tui/cables.py,sha256=zf7LnSu8Xb2BR0JuPfr4SS-YEbWOOuhFznZTLq9ntyE,2488
|
|
27
|
+
bytesim/tui/canvas.py,sha256=JxtJrlJdLBZv2FZmco6gS-A-2oOSomIYEo8WIBw2Wt8,20715
|
|
28
|
+
bytesim/tui/command_input.py,sha256=yLJkvVHZuLIpX0cD7MYTUgZ0PKiFX34Lewt2L3dMePM,993
|
|
29
|
+
bytesim/tui/help.py,sha256=k_FQ0I5K4Rf6llMOp33qL55_z1UAVVoqI1W-sHCoj1s,4652
|
|
30
|
+
bytesim/tui/inspector.py,sha256=CEg1e58-pFjBwW2Qc8ZZ-TOs-ueHMETREKuydU_e3qg,6572
|
|
31
|
+
bytesim/tui/theme.py,sha256=Q0N-G6kJqk57oPqTaYlddxGNhNpYumW-WxKjHmSiYlo,480
|
|
32
|
+
bytesim/tui/commands/__init__.py,sha256=fcmi2KBXO6wQy0dfLkM5Ya5PRqkUbHLvn16h63xbUdI,84
|
|
33
|
+
bytesim/tui/commands/addressing.py,sha256=-vs3Rn16EWi34_r9sVxSZyNOBnM7zchnI0A7mctGUKE,10622
|
|
34
|
+
bytesim/tui/commands/base.py,sha256=eXPZEUQ0SWKdQBXdPJq0dql6-3rqbzDSBD4ccYhFUNc,1277
|
|
35
|
+
bytesim/tui/commands/groups.py,sha256=d7nv39ouQscAsw6jgfy96oTD_l7F8Mb_5WuItOoSFk8,10825
|
|
36
|
+
bytesim/tui/commands/history.py,sha256=msSHRVySMBIMH8_Jr06pjbuN6kmtN6xwLgF_NaowbXY,4942
|
|
37
|
+
bytesim/tui/commands/interpreter.py,sha256=xHvnzJ9gwRDg7obsS3cJ2owTP55zdEl9pMC9KE5Qvss,3251
|
|
38
|
+
bytesim/tui/commands/naming.py,sha256=wO-9CM8YRDcbs957e4A0oLhD_7Ype4h9SufhRU2ph7o,3122
|
|
39
|
+
bytesim/tui/commands/registry.py,sha256=Gas3rglMrz2rD5e3myNzL4oGkE2bkUWDvgWYoKBJ3aQ,951
|
|
40
|
+
bytesim/tui/commands/scripting.py,sha256=LQ6Zz5WyOCKlTVQ-3z8RjCceufeN7to4koP8uCIyI0I,2633
|
|
41
|
+
bytesim/tui/commands/switching.py,sha256=E9WE6qSCmsiFr1qxnmMaNOW-PBdfC5y7vT8o1NrP8sc,8958
|
|
42
|
+
bytesim/tui/commands/topology.py,sha256=nJ2v6a5Deu-oKbPtPd9GG4_xdFQB3zMYJXONSOGV-Yk,11828
|
|
43
|
+
bytesim/tui/commands/traffic.py,sha256=2Vaxosg1xZ21xnJDqTp5KQ2zd9GJofPnbLMlTu9gIeM,13912
|
|
44
|
+
bytesim-0.1.0.dist-info/METADATA,sha256=HHVbTM__4sjSZt34WHYGVAw-jXB4ZCNZAuACsYAx6uY,8802
|
|
45
|
+
bytesim-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
46
|
+
bytesim-0.1.0.dist-info/entry_points.txt,sha256=0TVPSg_gO-nuL7VmnswUn16V6hEjIl3baj9_cKS0D-M,45
|
|
47
|
+
bytesim-0.1.0.dist-info/licenses/LICENSE,sha256=tCjN4t4dQq1IAFqmZrk1GXnbkBlNPIjUNn06h5gpAQY,1072
|
|
48
|
+
bytesim-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Yazdani
|
|
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.
|