testprotocols 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.
Files changed (119) hide show
  1. testprotocols/__init__.py +217 -0
  2. testprotocols/aftr_gateway.py +22 -0
  3. testprotocols/appliance_nat.py +52 -0
  4. testprotocols/appliance_uplinks.py +32 -0
  5. testprotocols/appliance_vlans.py +50 -0
  6. testprotocols/arp_client.py +26 -0
  7. testprotocols/bgp.py +55 -0
  8. testprotocols/conntrack.py +147 -0
  9. testprotocols/content_filtering.py +47 -0
  10. testprotocols/device_lifecycle.py +49 -0
  11. testprotocols/device_management.py +50 -0
  12. testprotocols/devices/__init__.py +46 -0
  13. testprotocols/devices/base.py +40 -0
  14. testprotocols/devices/client.py +133 -0
  15. testprotocols/devices/cpe.py +66 -0
  16. testprotocols/devices/infra.py +62 -0
  17. testprotocols/devices/sdwan.py +97 -0
  18. testprotocols/devices/switch.py +115 -0
  19. testprotocols/devices/traffic.py +53 -0
  20. testprotocols/devices/voice.py +69 -0
  21. testprotocols/devices/wan.py +60 -0
  22. testprotocols/dhcp_client.py +30 -0
  23. testprotocols/dhcp_server.py +23 -0
  24. testprotocols/discovery.py +20 -0
  25. testprotocols/dns_client.py +23 -0
  26. testprotocols/file_transfer.py +22 -0
  27. testprotocols/firewall.py +121 -0
  28. testprotocols/firewall_zones.py +133 -0
  29. testprotocols/first_hop_security.py +52 -0
  30. testprotocols/gateway_redundancy.py +29 -0
  31. testprotocols/http_client.py +36 -0
  32. testprotocols/http_server.py +22 -0
  33. testprotocols/hw_console.py +48 -0
  34. testprotocols/infra_controller.py +28 -0
  35. testprotocols/interface_dhcp.py +30 -0
  36. testprotocols/ip_interface.py +62 -0
  37. testprotocols/ip_routing.py +57 -0
  38. testprotocols/iperf_client.py +47 -0
  39. testprotocols/iperf_generator.py +42 -0
  40. testprotocols/iperf_server.py +41 -0
  41. testprotocols/l3_firewall.py +74 -0
  42. testprotocols/l7_firewall.py +32 -0
  43. testprotocols/link_aggregation.py +24 -0
  44. testprotocols/mac_table.py +20 -0
  45. testprotocols/models/__init__.py +304 -0
  46. testprotocols/models/dhcp.py +28 -0
  47. testprotocols/models/firewall.py +197 -0
  48. testprotocols/models/impairment.py +18 -0
  49. testprotocols/models/l2_common.py +53 -0
  50. testprotocols/models/multicast.py +22 -0
  51. testprotocols/models/networking.py +50 -0
  52. testprotocols/models/packets.py +21 -0
  53. testprotocols/models/qoe.py +31 -0
  54. testprotocols/models/radius.py +63 -0
  55. testprotocols/models/sdwan_appliance.py +637 -0
  56. testprotocols/models/switch.py +297 -0
  57. testprotocols/models/switch_routing.py +122 -0
  58. testprotocols/models/tr069.py +35 -0
  59. testprotocols/models/traffic.py +29 -0
  60. testprotocols/models/wan_edge.py +116 -0
  61. testprotocols/models/wifi.py +183 -0
  62. testprotocols/multicast_client.py +20 -0
  63. testprotocols/nat.py +87 -0
  64. testprotocols/netem_controller.py +42 -0
  65. testprotocols/network_endpoint.py +32 -0
  66. testprotocols/network_probe.py +27 -0
  67. testprotocols/nmap_scanner.py +27 -0
  68. testprotocols/ntp_client.py +26 -0
  69. testprotocols/ntp_config.py +25 -0
  70. testprotocols/ospf.py +24 -0
  71. testprotocols/packet_filter.py +144 -0
  72. testprotocols/pcap_capture.py +39 -0
  73. testprotocols/pdu_controller.py +26 -0
  74. testprotocols/port_poe.py +25 -0
  75. testprotocols/port_security.py +25 -0
  76. testprotocols/port_status.py +23 -0
  77. testprotocols/py.typed +0 -0
  78. testprotocols/qoe_browser.py +62 -0
  79. testprotocols/radius_client.py +78 -0
  80. testprotocols/radius_server.py +130 -0
  81. testprotocols/routed_interfaces.py +29 -0
  82. testprotocols/router.py +53 -0
  83. testprotocols/routing_read.py +22 -0
  84. testprotocols/sdwan_policy_manager.py +64 -0
  85. testprotocols/sip_phone.py +230 -0
  86. testprotocols/sip_server.py +205 -0
  87. testprotocols/site_to_site_vpn.py +61 -0
  88. testprotocols/snmp_client.py +17 -0
  89. testprotocols/spanning_tree.py +37 -0
  90. testprotocols/static_routes.py +47 -0
  91. testprotocols/storm_control.py +24 -0
  92. testprotocols/streaming_server.py +32 -0
  93. testprotocols/switch_acl.py +29 -0
  94. testprotocols/switch_ports.py +28 -0
  95. testprotocols/switch_qos.py +33 -0
  96. testprotocols/switch_vlans.py +34 -0
  97. testprotocols/syslog_config.py +31 -0
  98. testprotocols/tftp_server.py +22 -0
  99. testprotocols/threat_prevention.py +60 -0
  100. testprotocols/tr069_client.py +47 -0
  101. testprotocols/tr069_server.py +151 -0
  102. testprotocols/traffic_shaping.py +54 -0
  103. testprotocols/upnp_client.py +37 -0
  104. testprotocols/vlan_client.py +22 -0
  105. testprotocols/wan_link_admin.py +34 -0
  106. testprotocols/wifi_bss.py +197 -0
  107. testprotocols/wifi_client.py +72 -0
  108. testprotocols/wifi_mesh.py +259 -0
  109. testprotocols/wifi_onboarding.py +105 -0
  110. testprotocols/wifi_radio.py +153 -0
  111. testprotocols/wifi_rf.py +78 -0
  112. testprotocols/wifi_stations.py +59 -0
  113. testprotocols/wifi_transitions.py +112 -0
  114. testprotocols-0.1.0.dist-info/METADATA +29 -0
  115. testprotocols-0.1.0.dist-info/RECORD +119 -0
  116. testprotocols-0.1.0.dist-info/WHEEL +5 -0
  117. testprotocols-0.1.0.dist-info/licenses/LICENSE +201 -0
  118. testprotocols-0.1.0.dist-info/licenses/NOTICE +11 -0
  119. testprotocols-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,297 @@
1
+ """Data models for the vendor-neutral L2 (access) switch capabilities.
2
+
3
+ Field value-vocabularies are normalized ``StrEnum`` types owned here; records are
4
+ plain ``@dataclass``es composing them. Shared STP/FDB vocab lives in
5
+ ``models/l2_common.py``; rule action/protocol enums are reused from
6
+ ``models/sdwan_appliance.py``. Vendor neutrality is part of the contract — no
7
+ product name, vendor id, or ``native`` bucket appears in this module.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+ from enum import StrEnum
14
+
15
+ from testprotocols.models.l2_common import StpGuard
16
+ from testprotocols.models.sdwan_appliance import RuleAction, RuleProtocol
17
+
18
+
19
+ class PortMode(StrEnum):
20
+ """Switchport framing mode.
21
+
22
+ ``ACCESS`` and ``TRUNK`` are universal — all managed-switch drivers must support them.
23
+ ``ROUTED`` models a routed / "no-switchport" physical port and is only meaningful on
24
+ an L3 switch; an L2-only driver raises unsupported-capability if asked to set it.
25
+ """
26
+
27
+ ACCESS = "access"
28
+ TRUNK = "trunk"
29
+ ROUTED = "routed" # routed / no-switchport port; L3Switch only
30
+
31
+
32
+ class PortAdminState(StrEnum):
33
+ """Administrative enable/disable state of a port."""
34
+
35
+ ENABLED = "enabled"
36
+ DISABLED = "disabled"
37
+
38
+
39
+ class LinkState(StrEnum):
40
+ """Observed per-port link state."""
41
+
42
+ UP = "up"
43
+ DOWN = "down"
44
+ DISABLED = "disabled"
45
+
46
+
47
+ class Duplex(StrEnum):
48
+ """Port duplex mode (full, half, or auto-negotiated)."""
49
+
50
+ FULL = "full"
51
+ HALF = "half"
52
+ AUTO = "auto"
53
+
54
+
55
+ class AggregationMode(StrEnum):
56
+ """Link-aggregation negotiation mode (LACP or static)."""
57
+
58
+ LACP = "lacp"
59
+ STATIC = "static"
60
+
61
+
62
+ class PoeStatus(StrEnum):
63
+ """Observed Power-over-Ethernet delivery state for a port."""
64
+
65
+ DELIVERING = "delivering"
66
+ DISABLED = "disabled"
67
+ FAULT = "fault"
68
+ SEARCHING = "searching"
69
+ OFF = "off"
70
+
71
+
72
+ class PoePriority(StrEnum):
73
+ """PoE port priority used when total power budget is constrained."""
74
+
75
+ CRITICAL = "critical"
76
+ HIGH = "high"
77
+ LOW = "low"
78
+
79
+
80
+ class AccessPolicyType(StrEnum):
81
+ """Port-level admission-control policy (open, MAC-based, or 802.1X)."""
82
+
83
+ OPEN = "open"
84
+ MAC_ALLOW_LIST = "mac_allow_list"
85
+ STICKY_MAC = "sticky_mac"
86
+ DOT1X = "dot1x"
87
+
88
+
89
+ class AclDirection(StrEnum):
90
+ """Direction in which a port ACL is applied."""
91
+
92
+ INGRESS = "ingress"
93
+ EGRESS = "egress"
94
+
95
+
96
+ class DiscoveryProtocol(StrEnum):
97
+ """Link-layer discovery. Only the open IEEE 802.1AB standard is a member;
98
+ a vendor's proprietary discovery normalizes onto the same LLDP-shaped read."""
99
+
100
+ LLDP = "lldp"
101
+
102
+
103
+ class StormControlType(StrEnum):
104
+ """Traffic category that storm-control rate limiting is applied to."""
105
+
106
+ BROADCAST = "broadcast"
107
+ MULTICAST = "multicast"
108
+ UNKNOWN_UNICAST = "unknown_unicast"
109
+
110
+
111
+ class QosTrustMode(StrEnum):
112
+ """Which QoS marking field the port trusts for inbound classification."""
113
+
114
+ DSCP = "dscp"
115
+ COS = "cos"
116
+ UNTRUSTED = "untrusted"
117
+
118
+
119
+ class FhsTrustState(StrEnum):
120
+ """First-hop-security trust level assigned to a port."""
121
+
122
+ TRUSTED = "trusted"
123
+ UNTRUSTED = "untrusted"
124
+
125
+
126
+ class FhsScope(StrEnum):
127
+ """Scope at which a first-hop-security feature (e.g. DHCP snooping) operates."""
128
+
129
+ GLOBAL = "global"
130
+ PER_VLAN = "per_vlan"
131
+
132
+
133
+ class BindingSource(StrEnum):
134
+ """How a first-hop-security binding-table entry was learned."""
135
+
136
+ DYNAMIC_SNOOPING = "dynamic_snooping"
137
+ STATIC = "static"
138
+
139
+
140
+ @dataclass
141
+ class SwitchPort:
142
+ """A switchport — the first-class object of a switch."""
143
+
144
+ name: str
145
+ mode: PortMode
146
+ enabled: bool = True
147
+ native_vlan: int | None = None
148
+ allowed_vlans: list[int] = field(default_factory=list)
149
+ description: str = ""
150
+ voice_vlan: int | None = None
151
+ isolated: bool = False
152
+
153
+
154
+ @dataclass
155
+ class VlanDef:
156
+ """A VLAN id/name registry entry."""
157
+
158
+ vlan_id: int
159
+ name: str = ""
160
+
161
+
162
+ @dataclass
163
+ class StpPortConfig:
164
+ """Per-port spanning-tree configuration."""
165
+
166
+ port: str
167
+ guard: StpGuard = StpGuard.NONE
168
+ edge: bool = False
169
+ path_cost: int | None = None
170
+ priority: int | None = None
171
+
172
+
173
+ @dataclass
174
+ class LinkAggregationGroup:
175
+ """A link-aggregation group (LAG) by member ports + mode."""
176
+
177
+ name: str
178
+ member_ports: list[str]
179
+ mode: AggregationMode = AggregationMode.LACP
180
+
181
+
182
+ @dataclass
183
+ class PoePortStatus:
184
+ """Observed PoE state for a port."""
185
+
186
+ port: str
187
+ status: PoeStatus
188
+ draw_watts: float | None = None
189
+ priority: PoePriority | None = None
190
+
191
+
192
+ @dataclass
193
+ class AccessPolicy:
194
+ """Per-port access policy (802.1X / MAB / MAC limits)."""
195
+
196
+ port: str
197
+ policy_type: AccessPolicyType
198
+ allowed_macs: list[str] = field(default_factory=list)
199
+ max_macs: int | None = None
200
+ sticky: bool = False
201
+
202
+
203
+ @dataclass
204
+ class StormControlConfig:
205
+ """Per-port storm-control thresholds, keyed by traffic type.
206
+
207
+ Threshold units are driver-normalized (percent of line rate or pps); the
208
+ plugin maps the product's representation.
209
+ """
210
+
211
+ port: str
212
+ thresholds: dict[StormControlType, float] = field(default_factory=dict)
213
+
214
+
215
+ @dataclass
216
+ class SwitchAclRule:
217
+ """One ordered switch ACL rule — unified L2 + L3/L4 match.
218
+
219
+ Reuses ``RuleAction`` / ``RuleProtocol``; the L2 fields (``src_mac`` /
220
+ ``dst_mac`` / ``vlan``) and the IP 5-tuple are all optional, so the same
221
+ record serves both the L2 archetype and the L3 superset.
222
+ """
223
+
224
+ action: RuleAction
225
+ protocol: RuleProtocol = RuleProtocol.ANY
226
+ src_mac: str | None = None
227
+ dst_mac: str | None = None
228
+ vlan: int | None = None
229
+ src_cidr: str = "any"
230
+ dst_cidr: str = "any"
231
+ src_port: str = "any"
232
+ dst_port: str = "any"
233
+ comment: str = ""
234
+
235
+
236
+ @dataclass
237
+ class LldpNeighbor:
238
+ """A discovered link-layer neighbour (read-only)."""
239
+
240
+ local_port: str
241
+ remote_system: str
242
+ remote_port: str
243
+ protocol: DiscoveryProtocol = DiscoveryProtocol.LLDP
244
+ mgmt_address: str | None = None
245
+
246
+
247
+ @dataclass
248
+ class PortStatusEntry:
249
+ """Observed per-port link state and counters (read-only)."""
250
+
251
+ name: str
252
+ link_state: LinkState
253
+ speed_mbps: int | None = None
254
+ duplex: Duplex = Duplex.AUTO
255
+ rx_errors: int = 0
256
+ tx_errors: int = 0
257
+ rx_discards: int = 0
258
+ tx_discards: int = 0
259
+
260
+
261
+ @dataclass
262
+ class QosRule:
263
+ """A QoS classification rule (match -> DSCP/CoS marking).
264
+
265
+ ``match`` holds a vendor-neutral traffic-classifier expression (e.g. by
266
+ VLAN, protocol, or port) that the driver maps to its product's QoS
267
+ classifier; ``dscp`` and ``cos`` are the resulting mark values.
268
+ """
269
+
270
+ name: str
271
+ match: str
272
+ dscp: int | None = None
273
+ cos: int | None = None
274
+
275
+
276
+ @dataclass
277
+ class FhsBinding:
278
+ """A first-hop-security binding-table entry (DHCP snooping / DAI).
279
+
280
+ Note: a driver returning static bindings MUST set ``source=BindingSource.STATIC``
281
+ explicitly — static entries are immune to ageing and differ operationally from
282
+ snooped ones, so do not rely on the default for static tables.
283
+ """
284
+
285
+ mac: str
286
+ ip: str
287
+ vlan: int
288
+ port: str
289
+ source: BindingSource = BindingSource.DYNAMIC_SNOOPING
290
+
291
+
292
+ @dataclass
293
+ class NtpServer:
294
+ """An NTP server destination."""
295
+
296
+ host: str
297
+ prefer: bool = False
@@ -0,0 +1,122 @@
1
+ """Data models for the vendor-neutral L3 (distribution) switch capabilities.
2
+
3
+ Composed by ``devices.switch.L3Switch`` (a strict superset of ``L2Switch``).
4
+ Reuses ``DhcpMode`` / ``DhcpOption`` / ``DhcpReservation`` from
5
+ ``models/sdwan_appliance.py`` and ``RouteEntry`` / ``RouteOrigin`` from
6
+ ``models/wan_edge.py``. Scope is the **default VRF** — multi-VRF is deferred
7
+ (GAPS.md). Vendor neutrality is part of the contract: no product name, vendor id,
8
+ or ``native`` bucket appears here.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass, field
14
+ from enum import StrEnum
15
+
16
+ from testprotocols.models.sdwan_appliance import (
17
+ DhcpMode,
18
+ DhcpOption,
19
+ DhcpReservation,
20
+ )
21
+ from testprotocols.models.wan_edge import RouteOrigin # re-export
22
+
23
+ __all__ = [
24
+ "DhcpMode",
25
+ "InterfaceDhcpConfig",
26
+ "InterfaceMode",
27
+ "OspfConfig",
28
+ "OspfInterfaceSettings",
29
+ "OspfVersion",
30
+ "RedundancyGroup",
31
+ "RedundancyRole",
32
+ "RouteOrigin",
33
+ "RoutedInterface",
34
+ ]
35
+
36
+
37
+ class InterfaceMode(StrEnum):
38
+ """L3 interface kind."""
39
+
40
+ SVI = "svi"
41
+ ROUTED = "routed"
42
+ LOOPBACK = "loopback"
43
+
44
+
45
+ class OspfVersion(StrEnum):
46
+ """Seeded standardized OSPF versions. ``OspfNetworkType`` / ``OspfAreaType``
47
+ grow on evidence (open taxonomy)."""
48
+
49
+ V2 = "v2"
50
+ V3 = "v3"
51
+
52
+
53
+ class RedundancyRole(StrEnum):
54
+ """First-hop-redundancy role. ``ACTIVE_ACTIVE`` is a recorded candidate
55
+ (all-active gateways) — added on a driving test, not seeded."""
56
+
57
+ PRIMARY = "primary"
58
+ SPARE = "spare"
59
+
60
+
61
+ @dataclass
62
+ class RoutedInterface:
63
+ """An L3 interface — SVI, routed port, or loopback.
64
+
65
+ ``ip_address`` is the interface IP (the SVI IP for an SVI). Reuses the
66
+ appliance DHCP vocabulary via ``InterfaceDhcpConfig``; this record carries only the
67
+ L3 addressing identity.
68
+ """
69
+
70
+ name: str
71
+ mode: InterfaceMode
72
+ ip_address: str
73
+ subnet: str
74
+ vlan_id: int | None = None
75
+
76
+
77
+ @dataclass
78
+ class InterfaceDhcpConfig:
79
+ """Per-interface DHCP server/relay config (reuses appliance DHCP sub-models)."""
80
+
81
+ interface: str
82
+ mode: DhcpMode = DhcpMode.DISABLED
83
+ lease_seconds: int = 86400
84
+ dns_servers: list[str] = field(default_factory=list)
85
+ options: list[DhcpOption] = field(default_factory=list)
86
+ reservations: list[DhcpReservation] = field(default_factory=list)
87
+ reserved_ranges: list[tuple[str, str]] = field(default_factory=list)
88
+ relay_targets: list[str] = field(default_factory=list)
89
+
90
+
91
+ @dataclass
92
+ class OspfInterfaceSettings:
93
+ """Per-interface OSPF participation."""
94
+
95
+ interface: str
96
+ area: str
97
+ cost: int | None = None
98
+ passive: bool = False
99
+
100
+
101
+ @dataclass
102
+ class OspfConfig:
103
+ """Whole-config-replace OSPF configuration."""
104
+
105
+ enabled: bool
106
+ router_id: str
107
+ version: OspfVersion = OspfVersion.V2
108
+ areas: list[str] = field(default_factory=list)
109
+ interfaces: list[OspfInterfaceSettings] = field(default_factory=list)
110
+
111
+
112
+ @dataclass
113
+ class RedundancyGroup:
114
+ """A first-hop-redundancy group — virtual IP + role on an interface.
115
+
116
+ Behaviour is asserted via virtual-IP + role, not raw VRRP/HSRP internals.
117
+ """
118
+
119
+ group_id: int
120
+ virtual_ip: str
121
+ role: RedundancyRole
122
+ interface: str
@@ -0,0 +1,35 @@
1
+ """TR-069 ACS data models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import datetime
7
+
8
+
9
+ @dataclass
10
+ class CpeConnectionStatus:
11
+ """ACS-side view of a CPE record.
12
+
13
+ Reflects what the ACS believes about the CPE — not a live probe.
14
+ All fields remain populated when the CPE is offline (from the
15
+ last successful Inform). ``last_inform_time`` is ``None`` only
16
+ when the ACS has never received an Inform from this CPE.
17
+
18
+ ``last_boot_time`` is set to the timestamp of the most recent
19
+ Boot Inform (CWMP informEvent "1 BOOT") the ACS received from
20
+ this CPE. Use it to confirm a reboot has actually completed —
21
+ comparing successive ``last_boot_time`` values against a baseline
22
+ is the most reliable post-reboot re-registration signal because
23
+ the ACS keeps the previous CPE record indefinitely (a presence
24
+ check via list_cpes() would trivially pass against the stale
25
+ pre-reboot record).
26
+ """
27
+
28
+ online: bool
29
+ last_inform_time: datetime | None = None
30
+ last_boot_time: datetime | None = None
31
+ cached_manufacturer: str | None = None
32
+ cached_model: str | None = None
33
+ cached_serial_number: str | None = None
34
+ cached_hardware_version: str | None = None
35
+ cached_software_version: str | None = None
@@ -0,0 +1,29 @@
1
+ """Traffic generation specification and result data models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass
9
+ class TrafficSpec:
10
+ """Holds parameters for a traffic generation run (destination, bandwidth, protocol, etc.)."""
11
+
12
+ destination: str
13
+ bandwidth_mbps: float
14
+ protocol: str = "udp"
15
+ dscp: int = 0
16
+ duration_s: int = 30
17
+ parallel_streams: int = 1
18
+ port: int | None = None
19
+
20
+
21
+ @dataclass
22
+ class TrafficResult:
23
+ """Holds measured outcomes from a traffic generation run."""
24
+
25
+ sent_mbps: float = 0.0
26
+ received_mbps: float = 0.0
27
+ loss_percent: float = 0.0
28
+ jitter_ms: float | None = None
29
+ dscp_marking: int = 0
@@ -0,0 +1,116 @@
1
+ """WAN edge device data models for links, routes, SLA, flows, VPN, and shaping."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from enum import StrEnum
7
+ from typing import Any
8
+
9
+
10
+ @dataclass
11
+ class PathMetrics:
12
+ """Holds measured path quality metrics for a WAN link."""
13
+
14
+ latency_ms: float
15
+ jitter_ms: float
16
+ loss_percent: float
17
+ link_name: str
18
+ mos: float | None = None
19
+
20
+
21
+ @dataclass
22
+ class LinkStatus:
23
+ """Holds the current operational state and IP address of a WAN link."""
24
+
25
+ name: str
26
+ state: str # "up" | "down" | "degraded"
27
+ ip_address: str
28
+
29
+
30
+ class RouteOrigin(StrEnum):
31
+ """How a route was learned. Seeded with the standardized origins; ``ISIS`` /
32
+ ``RIP`` grow on evidence (see GAPS.md). ``UNKNOWN`` is the back-compat default
33
+ for callers that do not classify the route."""
34
+
35
+ UNKNOWN = "unknown"
36
+ STATIC = "static"
37
+ CONNECTED = "connected"
38
+ OSPF = "ospf"
39
+ BGP = "bgp"
40
+ LOCAL = "local"
41
+
42
+
43
+ @dataclass
44
+ class RouteEntry:
45
+ """A single routing-table entry: destination, gateway, interface, metric, origin.
46
+
47
+ ``origin`` is default-backed so the WAN-edge ``Router.get_routing_table`` and
48
+ ``Bgp.get_learned_routes`` producers stay source-compatible; the switch
49
+ ``RoutingRead`` populates it. See SPLITS.md.
50
+ """
51
+
52
+ destination: str
53
+ gateway: str
54
+ interface: str
55
+ metric: int
56
+ origin: RouteOrigin = RouteOrigin.UNKNOWN
57
+
58
+
59
+ @dataclass
60
+ class SLAPolicy:
61
+ """Holds SLA thresholds for latency, jitter, and packet loss."""
62
+
63
+ name: str
64
+ max_latency_ms: float = 150.0
65
+ max_jitter_ms: float = 30.0
66
+ max_loss_percent: float = 10.0
67
+
68
+
69
+ @dataclass
70
+ class LinkHealthReport:
71
+ """Holds a summary of link health including state, routing, metrics, and SLA compliance."""
72
+
73
+ state: str
74
+ route_installed: bool
75
+ avg_rtt_ms: float | None
76
+ jitter_ms: float | None
77
+ loss_percent: float
78
+ sla_compliant: bool
79
+ mos: float | None = None
80
+
81
+
82
+ @dataclass
83
+ class AppFlow:
84
+ """Holds per-application flow data observed on a WAN interface."""
85
+
86
+ application: str
87
+ category: str
88
+ src_ip: str
89
+ dst_ip: str
90
+ wan_interface: str
91
+ bytes_sent: int
92
+ bytes_received: int
93
+
94
+
95
+ @dataclass
96
+ class VPNPeerStatus:
97
+ """Holds the reachability and uplink state of a VPN peer."""
98
+
99
+ peer_id: str
100
+ peer_name: str
101
+ reachability: str
102
+ uplink: str
103
+
104
+
105
+ @dataclass
106
+ class TrafficShapingRule:
107
+ """Holds a traffic shaping rule.
108
+
109
+ Includes match criteria and optional DSCP, bandwidth, or priority.
110
+ """
111
+
112
+ name: str
113
+ match: dict[str, Any]
114
+ dscp_tag: int | None = None
115
+ bandwidth_limit_kbps: int | None = None
116
+ priority: str | None = None