testprotocols 0.8.0__tar.gz → 0.9.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.
- {testprotocols-0.8.0/src/testprotocols.egg-info → testprotocols-0.9.0}/PKG-INFO +1 -1
- {testprotocols-0.8.0 → testprotocols-0.9.0}/pyproject.toml +1 -1
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/__init__.py +4 -1
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/sdwan.py +7 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/sdwan_appliance.py +17 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/sdwan_policy_manager.py +42 -4
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/site_to_site_vpn.py +27 -2
- testprotocols-0.9.0/src/testprotocols/uplink_ports.py +54 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0/src/testprotocols.egg-info}/PKG-INFO +1 -1
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols.egg-info/SOURCES.txt +1 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_boundary_capability_templates.py +22 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/LICENSE +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/NOTICE +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/README.md +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/setup.cfg +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/aftr_gateway.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/appliance_nat.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/appliance_uplinks.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/appliance_vlans.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/arp_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/bgp.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/config_ownership.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/conntrack.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/content_filtering.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/device_info.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/device_lifecycle.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/device_management.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/__init__.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/base.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/cpe.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/infra.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/switch.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/traffic.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/voice.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/devices/wan.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/dhcp_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/dhcp_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/discovery.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/dns_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/file_transfer.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/firewall.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/firewall_zones.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/first_hop_security.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/gateway_redundancy.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/http_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/http_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/hw_console.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/interface_dhcp.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/ip_interface.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/ip_routing.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/iperf_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/iperf_generator.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/iperf_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/l3_firewall.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/l7_firewall.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/link_aggregation.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/mac_table.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/__init__.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/dhcp.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/firewall.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/impairment.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/l2_common.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/multicast.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/networking.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/packets.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/qoe.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/radius.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/switch.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/switch_routing.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/tr069.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/traffic.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/wan_edge.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/models/wifi.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/multicast_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/nat.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/netem_controller.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/network_attachment.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/network_endpoint.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/network_probe.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/nmap_scanner.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/ntp_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/ntp_config.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/ospf.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/packet_filter.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/pcap_capture.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/pdu_controller.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/placement.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/port_poe.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/port_security.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/port_status.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/py.typed +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/qoe_browser.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/radius_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/radius_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/reachability_responder.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/routed_interfaces.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/router.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/routing_read.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/sip_phone.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/sip_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/snmp_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/spanning_tree.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/static_routes.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/storm_control.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/streaming_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/switch_acl.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/switch_ports.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/switch_qos.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/switch_vlans.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/syslog_config.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/tftp_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/threat_prevention.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/tr069_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/tr069_server.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/traffic_shaping.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/upnp_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/vlan_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wan_link_admin.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_bss.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_client.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_mesh.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_onboarding.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_radio.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_rf.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_stations.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols/wifi_transitions.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols.egg-info/dependency_links.txt +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/src/testprotocols.egg-info/top_level.txt +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_appliance_vlans.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_archetype_purity.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_cpe_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_device_types.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_dhcp_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_firewall_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_infra_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_ip_interface.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_ip_routing.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_network_tool_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_package_imports.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_radius_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_sdwan_appliance_models.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_sdwan_appliance_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_switch_models.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_switch_routing_models.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_switch_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_traffic_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_voice_wifi_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_wan_edge_templates.py +0 -0
- {testprotocols-0.8.0 → testprotocols-0.9.0}/tests/test_wifi_templates.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testprotocols
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Common Test Resource Layer — capability and device protocols for telco testing.
|
|
5
5
|
Author-email: Alottabits <rjvisser@alottabits.com>
|
|
6
6
|
Maintainer-email: Alottabits <rjvisser@alottabits.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""testprotocols — capability and device Protocols for telco resources under test."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.
|
|
3
|
+
__version__ = "0.9.0"
|
|
4
4
|
|
|
5
5
|
from testprotocols.aftr_gateway import AftrGateway
|
|
6
6
|
from testprotocols.appliance_nat import ApplianceNat
|
|
@@ -99,6 +99,7 @@ from testprotocols.threat_prevention import ThreatPrevention
|
|
|
99
99
|
from testprotocols.tr069_client import Tr069Client
|
|
100
100
|
from testprotocols.tr069_server import Tr069Server
|
|
101
101
|
from testprotocols.traffic_shaping import TrafficShaping
|
|
102
|
+
from testprotocols.uplink_ports import UplinkPortRef, UplinkPorts
|
|
102
103
|
from testprotocols.upnp_client import UpnpClient
|
|
103
104
|
from testprotocols.vlan_client import VlanClient
|
|
104
105
|
from testprotocols.wan_link_admin import WanLinkAdmin
|
|
@@ -206,6 +207,8 @@ __all__ = [
|
|
|
206
207
|
"Tr069Server",
|
|
207
208
|
"TrafficControllerDevice",
|
|
208
209
|
"TrafficShaping",
|
|
210
|
+
"UplinkPortRef",
|
|
211
|
+
"UplinkPorts",
|
|
209
212
|
"UpnpClient",
|
|
210
213
|
"VlanClient",
|
|
211
214
|
"WanLinkAdmin",
|
|
@@ -26,6 +26,7 @@ from testprotocols.static_routes import StaticRoutes
|
|
|
26
26
|
from testprotocols.syslog_config import SyslogConfig
|
|
27
27
|
from testprotocols.threat_prevention import ThreatPrevention
|
|
28
28
|
from testprotocols.traffic_shaping import TrafficShaping
|
|
29
|
+
from testprotocols.uplink_ports import UplinkPorts
|
|
29
30
|
from testprotocols.wan_link_admin import WanLinkAdmin
|
|
30
31
|
|
|
31
32
|
|
|
@@ -104,5 +105,11 @@ class SdwanApplianceDevice(BaseDeviceProtocol, Protocol):
|
|
|
104
105
|
"""Whether this session owns the appliance's network configuration —
|
|
105
106
|
False for a warm-spare member whose primary owns the writes."""
|
|
106
107
|
|
|
108
|
+
uplink_ports: UplinkPorts
|
|
109
|
+
"""Declared uplink→switch-port wiring — which managed-switch ports carry
|
|
110
|
+
each WAN uplink, one entry per pair member (see ``uplink_ports``). A
|
|
111
|
+
testbed that splices or re-VLANs an uplink resolves the ports here; the
|
|
112
|
+
switch's ``PlacementPorts`` stays the write authorization."""
|
|
113
|
+
|
|
107
114
|
|
|
108
115
|
register_device_type("sdwan_appliance", SdwanApplianceDevice)
|
|
@@ -555,6 +555,23 @@ class UplinkSelectionRule:
|
|
|
555
555
|
performance_class: str | None = None
|
|
556
556
|
|
|
557
557
|
|
|
558
|
+
@dataclass(frozen=True)
|
|
559
|
+
class UplinkSelectionSettings:
|
|
560
|
+
"""The scalar half of the uplink-selection surface (read-only snapshot).
|
|
561
|
+
|
|
562
|
+
The network-wide settings that frame the ordered rule list: which uplink
|
|
563
|
+
carries default-routed traffic, whether load balancing across uplinks is
|
|
564
|
+
on, and whether the overlay runs tunnels over all active uplinks
|
|
565
|
+
concurrently (``active_active_vpn`` — a prerequisite on some products for
|
|
566
|
+
performance-class steering to evaluate at all). Frozen: a captured
|
|
567
|
+
snapshot is a restore target, so it must not mutate after capture.
|
|
568
|
+
"""
|
|
569
|
+
|
|
570
|
+
default_uplink: str
|
|
571
|
+
load_balancing_enabled: bool
|
|
572
|
+
active_active_vpn: bool
|
|
573
|
+
|
|
574
|
+
|
|
558
575
|
# --- Static routes ---
|
|
559
576
|
|
|
560
577
|
|
|
@@ -12,16 +12,23 @@ path steering (``set_uplink_selection`` /
|
|
|
12
12
|
classes reuse ``SLAPolicy`` by name) landed 2026-06-12; the default
|
|
13
13
|
(primary) uplink pair (``get_default_uplink`` / ``set_default_uplink``)
|
|
14
14
|
landed 2026-07-03 — the network-wide "which uplink carries default-routed
|
|
15
|
-
traffic" knob, distinct from per-flow steering.
|
|
16
|
-
the
|
|
17
|
-
|
|
15
|
+
traffic" knob, distinct from per-flow steering. The SLA read-back
|
|
16
|
+
(``get_sla_policies``), the scalar-settings snapshot
|
|
17
|
+
(``get_uplink_selection_settings``) and the concurrent-overlay-tunnels
|
|
18
|
+
write (``set_active_active_vpn``) landed 2026-08-10 — the read-back and
|
|
19
|
+
converge surface for the settings the rule list rides on. ``apply_policy``
|
|
20
|
+
remains the generic escape hatch for vendor-shaped policies beyond that
|
|
21
|
+
surface. Application-match steering grows on evidence.
|
|
18
22
|
"""
|
|
19
23
|
|
|
20
24
|
from __future__ import annotations
|
|
21
25
|
|
|
22
26
|
from typing import Any, Protocol, runtime_checkable
|
|
23
27
|
|
|
24
|
-
from testprotocols.models.sdwan_appliance import
|
|
28
|
+
from testprotocols.models.sdwan_appliance import (
|
|
29
|
+
UplinkSelectionRule,
|
|
30
|
+
UplinkSelectionSettings,
|
|
31
|
+
)
|
|
25
32
|
from testprotocols.models.wan_edge import AppFlow, SLAPolicy
|
|
26
33
|
|
|
27
34
|
|
|
@@ -86,3 +93,34 @@ class SdwanPolicyManager(Protocol):
|
|
|
86
93
|
def get_uplink_selection(self) -> list[UplinkSelectionRule]:
|
|
87
94
|
"""Return the uplink-selection rules in evaluation order."""
|
|
88
95
|
...
|
|
96
|
+
|
|
97
|
+
def get_sla_policies(self) -> list[SLAPolicy]:
|
|
98
|
+
"""Return the configured SLA policies (performance classes).
|
|
99
|
+
|
|
100
|
+
The read-back for ``configure_sla_policy`` / ``remove_sla_policy``:
|
|
101
|
+
thresholds as the product stores them, names as configured. Policies
|
|
102
|
+
the product created outside this session are included — a caller
|
|
103
|
+
arranging classes captures this read as its restore reference and
|
|
104
|
+
removes only what it configured.
|
|
105
|
+
"""
|
|
106
|
+
...
|
|
107
|
+
|
|
108
|
+
def get_uplink_selection_settings(self) -> UplinkSelectionSettings:
|
|
109
|
+
"""Return the scalar settings framing the uplink-selection rule list.
|
|
110
|
+
|
|
111
|
+
One snapshot of the network-wide knobs — default uplink, load
|
|
112
|
+
balancing, concurrent overlay tunnels — read together so a caller
|
|
113
|
+
converging or restoring them works from a single consistent capture.
|
|
114
|
+
"""
|
|
115
|
+
...
|
|
116
|
+
|
|
117
|
+
def set_active_active_vpn(self, enabled: bool) -> None:
|
|
118
|
+
"""Enable or disable concurrent overlay tunnels over all active uplinks.
|
|
119
|
+
|
|
120
|
+
On products where performance-class steering only evaluates with
|
|
121
|
+
concurrent tunnels enabled, this is the gate a testbed converges
|
|
122
|
+
before arranging classes. Read-back via
|
|
123
|
+
``get_uplink_selection_settings``; the ordered rule list and the
|
|
124
|
+
default uplink are left untouched.
|
|
125
|
+
"""
|
|
126
|
+
...
|
|
@@ -22,8 +22,11 @@ write registers/dereferences the device in the relevant site configs. The
|
|
|
22
22
|
round-trip is intent-preserving even where the stored shape differs.
|
|
23
23
|
|
|
24
24
|
In scope: overlay participation (role, hubs + default route, subnets),
|
|
25
|
-
overlay exclusions (flow-level membership carve-outs — local breakout),
|
|
26
|
-
peer status
|
|
25
|
+
overlay exclusions (flow-level membership carve-outs — local breakout),
|
|
26
|
+
peer status, and observed overlay path quality per local uplink
|
|
27
|
+
(``get_vpn_path_metrics`` — the *tunnel* quantity, distinct from the
|
|
28
|
+
underlay probe metrics on ``Router.get_wan_path_metrics``; see SPLITS.md).
|
|
29
|
+
Overlay *membership* — which flows are in the overlay at all
|
|
27
30
|
(role, hubs + default route, subnets, exclusions) — lives here; path
|
|
28
31
|
selection *within* a domain (e.g. which overlay uplink, or which internet
|
|
29
32
|
uplink) lives in ``sdwan_policy_manager``.
|
|
@@ -43,6 +46,7 @@ from testprotocols.models.sdwan_appliance import (
|
|
|
43
46
|
SiteToSiteVpnConfig,
|
|
44
47
|
VpnPeerStatus,
|
|
45
48
|
)
|
|
49
|
+
from testprotocols.models.wan_edge import PathMetrics
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
@runtime_checkable
|
|
@@ -88,3 +92,24 @@ class SiteToSiteVpn(Protocol):
|
|
|
88
92
|
unchanged by the driver.
|
|
89
93
|
"""
|
|
90
94
|
...
|
|
95
|
+
|
|
96
|
+
def get_vpn_path_metrics(
|
|
97
|
+
self, timespan_s: int, peer: str | None = None
|
|
98
|
+
) -> dict[str, PathMetrics]:
|
|
99
|
+
"""Return observed overlay path quality per local uplink.
|
|
100
|
+
|
|
101
|
+
Keyed by local uplink name (``PathMetrics.link_name`` repeats the
|
|
102
|
+
key); aggregated over every overlay peer, or over the single peer
|
|
103
|
+
named by *peer* (its testbed-level site name, as ``get_vpn_peers``
|
|
104
|
+
reports it). This is the **tunnel** quantity — the path quality the
|
|
105
|
+
product's own overlay steering evaluates — not the underlay probe
|
|
106
|
+
metrics of ``Router.get_wan_path_metrics``.
|
|
107
|
+
|
|
108
|
+
*timespan_s* is explicit because products report this as a windowed
|
|
109
|
+
aggregate: the caller owns the freshness/staleness trade and sizes
|
|
110
|
+
its observation intervals beyond the product's reporting lag. An
|
|
111
|
+
uplink absent from the result carried no measured overlay path in
|
|
112
|
+
the window — presence is itself evidence a tunnel rode that uplink.
|
|
113
|
+
Empty dict when the device participates in no overlay.
|
|
114
|
+
"""
|
|
115
|
+
...
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Uplink switch-port wiring — boundary view of a WAN edge's declared uplink ports.
|
|
2
|
+
|
|
3
|
+
A testbed that instruments a WAN uplink (splicing an impairment path into it,
|
|
4
|
+
re-VLANing the port that carries it) needs to know **which managed-switch
|
|
5
|
+
ports carry which uplink**. That is a declared-topology fact the device's
|
|
6
|
+
driver resolves from its own configuration — a boundary view in the
|
|
7
|
+
``DeviceInfo`` / ``PlacementPipe`` family, not behaviour.
|
|
8
|
+
|
|
9
|
+
The placement split carries over: the WAN-edge device declares which ports
|
|
10
|
+
carry its uplinks (**topology**, this view — the ``PlacementPipe`` analogue),
|
|
11
|
+
while the switch's ``PlacementPorts`` remains the **write authorization** for
|
|
12
|
+
re-VLANing anything. The two declarations stay independent, owned by the two
|
|
13
|
+
resources they describe.
|
|
14
|
+
|
|
15
|
+
One uplink maps to a *tuple* of port references — one per chassis / redundant
|
|
16
|
+
pair member — because a warm-spare pair shares its WAN segments: moving one
|
|
17
|
+
member's port and not its twin's splits the segment, so a consumer must see
|
|
18
|
+
the full member set to act on an uplink coherently.
|
|
19
|
+
|
|
20
|
+
The **as-found VLAN is deliberately absent**: port VLANs drift, so a recorded
|
|
21
|
+
value would rot; a consumer captures the as-found state live from the switch
|
|
22
|
+
at the moment it writes.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from collections.abc import Mapping
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from typing import Protocol, runtime_checkable
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class UplinkPortRef:
|
|
34
|
+
"""One switch port carrying (one member's leg of) a WAN uplink."""
|
|
35
|
+
|
|
36
|
+
member: str
|
|
37
|
+
"""Device (chassis / redundant-pair member) whose WAN port this is."""
|
|
38
|
+
|
|
39
|
+
switch: str
|
|
40
|
+
"""Name of the managed switch carrying the port."""
|
|
41
|
+
|
|
42
|
+
port: str
|
|
43
|
+
"""The switch port carrying this member's uplink."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@runtime_checkable
|
|
47
|
+
class UplinkPorts(Protocol):
|
|
48
|
+
"""Abstract contract for a WAN edge's declared uplink→switch-port wiring."""
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def wan_ports(self) -> Mapping[str, tuple[UplinkPortRef, ...]]:
|
|
52
|
+
"""Uplink name → the switch ports carrying that uplink, one entry per
|
|
53
|
+
chassis / pair member; empty mapping when the wiring is undeclared."""
|
|
54
|
+
...
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testprotocols
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Common Test Resource Layer — capability and device protocols for telco testing.
|
|
5
5
|
Author-email: Alottabits <rjvisser@alottabits.com>
|
|
6
6
|
Maintainer-email: Alottabits <rjvisser@alottabits.com>
|
|
@@ -81,6 +81,7 @@ src/testprotocols/threat_prevention.py
|
|
|
81
81
|
src/testprotocols/tr069_client.py
|
|
82
82
|
src/testprotocols/tr069_server.py
|
|
83
83
|
src/testprotocols/traffic_shaping.py
|
|
84
|
+
src/testprotocols/uplink_ports.py
|
|
84
85
|
src/testprotocols/upnp_client.py
|
|
85
86
|
src/testprotocols/vlan_client.py
|
|
86
87
|
src/testprotocols/wan_link_admin.py
|
|
@@ -13,6 +13,7 @@ from testprotocols.config_ownership import ConfigOwnership
|
|
|
13
13
|
from testprotocols.device_info import DeviceInfo
|
|
14
14
|
from testprotocols.network_attachment import NetworkAttachment
|
|
15
15
|
from testprotocols.placement import PlacementPipe, PlacementPorts
|
|
16
|
+
from testprotocols.uplink_ports import UplinkPortRef, UplinkPorts
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def test_network_attachment_members() -> None:
|
|
@@ -35,6 +36,27 @@ def test_config_ownership_members() -> None:
|
|
|
35
36
|
assert protocol_attrs(ConfigOwnership) == {"manages_network"}
|
|
36
37
|
|
|
37
38
|
|
|
39
|
+
def test_uplink_ports_members() -> None:
|
|
40
|
+
assert protocol_attrs(UplinkPorts) == {"wan_ports"}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_uplink_ports_view_satisfies_runtime_check() -> None:
|
|
44
|
+
"""A driver view exposing the wiring mapping satisfies the runtime check."""
|
|
45
|
+
|
|
46
|
+
class _View:
|
|
47
|
+
@property
|
|
48
|
+
def wan_ports(self) -> dict[str, tuple[UplinkPortRef, ...]]:
|
|
49
|
+
return {
|
|
50
|
+
"wan1": (
|
|
51
|
+
UplinkPortRef(member="edge-a", switch="dist-1", port="7"),
|
|
52
|
+
UplinkPortRef(member="edge-b", switch="dist-1", port="8"),
|
|
53
|
+
),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
assert isinstance(_View(), UplinkPorts)
|
|
57
|
+
assert _View().wan_ports["wan1"][0].switch == "dist-1"
|
|
58
|
+
|
|
59
|
+
|
|
38
60
|
def test_runtime_checkable_with_plain_attributes() -> None:
|
|
39
61
|
"""A driver view exposing plain attributes satisfies the runtime check."""
|
|
40
62
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|