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.
- testprotocols/__init__.py +217 -0
- testprotocols/aftr_gateway.py +22 -0
- testprotocols/appliance_nat.py +52 -0
- testprotocols/appliance_uplinks.py +32 -0
- testprotocols/appliance_vlans.py +50 -0
- testprotocols/arp_client.py +26 -0
- testprotocols/bgp.py +55 -0
- testprotocols/conntrack.py +147 -0
- testprotocols/content_filtering.py +47 -0
- testprotocols/device_lifecycle.py +49 -0
- testprotocols/device_management.py +50 -0
- testprotocols/devices/__init__.py +46 -0
- testprotocols/devices/base.py +40 -0
- testprotocols/devices/client.py +133 -0
- testprotocols/devices/cpe.py +66 -0
- testprotocols/devices/infra.py +62 -0
- testprotocols/devices/sdwan.py +97 -0
- testprotocols/devices/switch.py +115 -0
- testprotocols/devices/traffic.py +53 -0
- testprotocols/devices/voice.py +69 -0
- testprotocols/devices/wan.py +60 -0
- testprotocols/dhcp_client.py +30 -0
- testprotocols/dhcp_server.py +23 -0
- testprotocols/discovery.py +20 -0
- testprotocols/dns_client.py +23 -0
- testprotocols/file_transfer.py +22 -0
- testprotocols/firewall.py +121 -0
- testprotocols/firewall_zones.py +133 -0
- testprotocols/first_hop_security.py +52 -0
- testprotocols/gateway_redundancy.py +29 -0
- testprotocols/http_client.py +36 -0
- testprotocols/http_server.py +22 -0
- testprotocols/hw_console.py +48 -0
- testprotocols/infra_controller.py +28 -0
- testprotocols/interface_dhcp.py +30 -0
- testprotocols/ip_interface.py +62 -0
- testprotocols/ip_routing.py +57 -0
- testprotocols/iperf_client.py +47 -0
- testprotocols/iperf_generator.py +42 -0
- testprotocols/iperf_server.py +41 -0
- testprotocols/l3_firewall.py +74 -0
- testprotocols/l7_firewall.py +32 -0
- testprotocols/link_aggregation.py +24 -0
- testprotocols/mac_table.py +20 -0
- testprotocols/models/__init__.py +304 -0
- testprotocols/models/dhcp.py +28 -0
- testprotocols/models/firewall.py +197 -0
- testprotocols/models/impairment.py +18 -0
- testprotocols/models/l2_common.py +53 -0
- testprotocols/models/multicast.py +22 -0
- testprotocols/models/networking.py +50 -0
- testprotocols/models/packets.py +21 -0
- testprotocols/models/qoe.py +31 -0
- testprotocols/models/radius.py +63 -0
- testprotocols/models/sdwan_appliance.py +637 -0
- testprotocols/models/switch.py +297 -0
- testprotocols/models/switch_routing.py +122 -0
- testprotocols/models/tr069.py +35 -0
- testprotocols/models/traffic.py +29 -0
- testprotocols/models/wan_edge.py +116 -0
- testprotocols/models/wifi.py +183 -0
- testprotocols/multicast_client.py +20 -0
- testprotocols/nat.py +87 -0
- testprotocols/netem_controller.py +42 -0
- testprotocols/network_endpoint.py +32 -0
- testprotocols/network_probe.py +27 -0
- testprotocols/nmap_scanner.py +27 -0
- testprotocols/ntp_client.py +26 -0
- testprotocols/ntp_config.py +25 -0
- testprotocols/ospf.py +24 -0
- testprotocols/packet_filter.py +144 -0
- testprotocols/pcap_capture.py +39 -0
- testprotocols/pdu_controller.py +26 -0
- testprotocols/port_poe.py +25 -0
- testprotocols/port_security.py +25 -0
- testprotocols/port_status.py +23 -0
- testprotocols/py.typed +0 -0
- testprotocols/qoe_browser.py +62 -0
- testprotocols/radius_client.py +78 -0
- testprotocols/radius_server.py +130 -0
- testprotocols/routed_interfaces.py +29 -0
- testprotocols/router.py +53 -0
- testprotocols/routing_read.py +22 -0
- testprotocols/sdwan_policy_manager.py +64 -0
- testprotocols/sip_phone.py +230 -0
- testprotocols/sip_server.py +205 -0
- testprotocols/site_to_site_vpn.py +61 -0
- testprotocols/snmp_client.py +17 -0
- testprotocols/spanning_tree.py +37 -0
- testprotocols/static_routes.py +47 -0
- testprotocols/storm_control.py +24 -0
- testprotocols/streaming_server.py +32 -0
- testprotocols/switch_acl.py +29 -0
- testprotocols/switch_ports.py +28 -0
- testprotocols/switch_qos.py +33 -0
- testprotocols/switch_vlans.py +34 -0
- testprotocols/syslog_config.py +31 -0
- testprotocols/tftp_server.py +22 -0
- testprotocols/threat_prevention.py +60 -0
- testprotocols/tr069_client.py +47 -0
- testprotocols/tr069_server.py +151 -0
- testprotocols/traffic_shaping.py +54 -0
- testprotocols/upnp_client.py +37 -0
- testprotocols/vlan_client.py +22 -0
- testprotocols/wan_link_admin.py +34 -0
- testprotocols/wifi_bss.py +197 -0
- testprotocols/wifi_client.py +72 -0
- testprotocols/wifi_mesh.py +259 -0
- testprotocols/wifi_onboarding.py +105 -0
- testprotocols/wifi_radio.py +153 -0
- testprotocols/wifi_rf.py +78 -0
- testprotocols/wifi_stations.py +59 -0
- testprotocols/wifi_transitions.py +112 -0
- testprotocols-0.1.0.dist-info/METADATA +29 -0
- testprotocols-0.1.0.dist-info/RECORD +119 -0
- testprotocols-0.1.0.dist-info/WHEEL +5 -0
- testprotocols-0.1.0.dist-info/licenses/LICENSE +201 -0
- testprotocols-0.1.0.dist-info/licenses/NOTICE +11 -0
- 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
|