testprotocols 0.7.0__tar.gz → 0.7.2__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.7.0/src/testprotocols.egg-info → testprotocols-0.7.2}/PKG-INFO +1 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/pyproject.toml +1 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/__init__.py +1 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/client.py +18 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/sdwan.py +6 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/switch.py +6 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/first_hop_security.py +1 -3
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/dhcp.py +1 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/sdwan_appliance.py +11 -11
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/switch.py +3 -3
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/switch_routing.py +7 -7
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/switch_acl.py +1 -3
- {testprotocols-0.7.0 → testprotocols-0.7.2/src/testprotocols.egg-info}/PKG-INFO +1 -1
- testprotocols-0.7.2/tests/test_appliance_vlans.py +52 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_cpe_templates.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_device_types.py +87 -46
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_dhcp_templates.py +3 -2
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_firewall_templates.py +5 -4
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_infra_templates.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_ip_interface.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_ip_routing.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_network_tool_templates.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_radius_templates.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_sdwan_appliance_models.py +16 -13
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_sdwan_appliance_templates.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_switch_models.py +19 -18
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_switch_routing_models.py +12 -7
- testprotocols-0.7.2/tests/test_switch_templates.py +95 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_traffic_templates.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_voice_wifi_templates.py +2 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_wan_edge_templates.py +4 -3
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_wifi_templates.py +6 -5
- testprotocols-0.7.0/tests/test_appliance_vlans.py +0 -33
- testprotocols-0.7.0/tests/test_switch_templates.py +0 -69
- {testprotocols-0.7.0 → testprotocols-0.7.2}/LICENSE +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/NOTICE +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/README.md +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/setup.cfg +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/aftr_gateway.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/appliance_nat.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/appliance_uplinks.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/appliance_vlans.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/arp_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/bgp.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/conntrack.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/content_filtering.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/device_lifecycle.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/device_management.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/__init__.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/base.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/cpe.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/infra.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/traffic.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/voice.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/devices/wan.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/dhcp_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/dhcp_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/discovery.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/dns_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/file_transfer.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/firewall.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/firewall_zones.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/gateway_redundancy.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/http_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/http_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/hw_console.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/interface_dhcp.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/ip_interface.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/ip_routing.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/iperf_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/iperf_generator.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/iperf_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/l3_firewall.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/l7_firewall.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/link_aggregation.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/mac_table.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/__init__.py +1 -1
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/firewall.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/impairment.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/l2_common.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/multicast.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/networking.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/packets.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/qoe.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/radius.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/tr069.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/traffic.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/wan_edge.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/models/wifi.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/multicast_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/nat.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/netem_controller.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/network_endpoint.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/network_probe.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/nmap_scanner.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/ntp_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/ntp_config.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/ospf.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/packet_filter.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/pcap_capture.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/pdu_controller.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/port_poe.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/port_security.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/port_status.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/py.typed +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/qoe_browser.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/radius_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/radius_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/reachability_responder.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/routed_interfaces.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/router.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/routing_read.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/sdwan_policy_manager.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/sip_phone.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/sip_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/site_to_site_vpn.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/snmp_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/spanning_tree.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/static_routes.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/storm_control.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/streaming_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/switch_ports.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/switch_qos.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/switch_vlans.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/syslog_config.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/tftp_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/threat_prevention.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/tr069_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/tr069_server.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/traffic_shaping.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/upnp_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/vlan_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wan_link_admin.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_bss.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_client.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_mesh.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_onboarding.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_radio.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_rf.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_stations.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols/wifi_transitions.py +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols.egg-info/SOURCES.txt +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols.egg-info/dependency_links.txt +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/src/testprotocols.egg-info/top_level.txt +0 -0
- {testprotocols-0.7.0 → testprotocols-0.7.2}/tests/test_package_imports.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testprotocols
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
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>
|
|
@@ -142,6 +142,24 @@ class QoeMeasurementClientDevice(QoeClientDevice, Protocol):
|
|
|
142
142
|
declaration when address facts are foreign-owned or discovered late.
|
|
143
143
|
Vendor-neutral metadata from the driver's attachment config."""
|
|
144
144
|
|
|
145
|
+
attachment_subnet: str
|
|
146
|
+
"""The declared subnet (CIDR) of a guest attachment, empty when undeclared.
|
|
147
|
+
Vendor-neutral metadata from the driver's attachment config — read it here
|
|
148
|
+
rather than reaching into a framework-specific config object."""
|
|
149
|
+
|
|
150
|
+
mgmt_ip: str
|
|
151
|
+
"""The client's management-plane address, empty when undeclared. Published
|
|
152
|
+
by the driver from its own config so isolation checks never consume a
|
|
153
|
+
framework-specific config object."""
|
|
154
|
+
|
|
155
|
+
pipe_switch: str
|
|
156
|
+
"""Name of the switch carrying this client's dedicated access port (its
|
|
157
|
+
placement pipe), empty when the client has no declared pipe."""
|
|
158
|
+
|
|
159
|
+
pipe_port: str
|
|
160
|
+
"""The dedicated access port whose VLAN is this client's placement knob,
|
|
161
|
+
empty when the client has no declared pipe."""
|
|
162
|
+
|
|
145
163
|
|
|
146
164
|
register_device_type("linux_lan_client", LanClientDevice)
|
|
147
165
|
register_device_type("linux_wlan_client", WlanClientDevice)
|
|
@@ -99,5 +99,11 @@ class SdwanApplianceDevice(BaseDeviceProtocol, Protocol):
|
|
|
99
99
|
model-parameterised tests; read it here rather than reaching into a
|
|
100
100
|
framework-specific config object."""
|
|
101
101
|
|
|
102
|
+
manages_network: bool
|
|
103
|
+
"""Whether this session owns the device's network configuration. False for
|
|
104
|
+
a warm-spare member whose primary owns the writes. Vendor-neutral metadata
|
|
105
|
+
from the driver's config — read it here rather than reaching into a
|
|
106
|
+
framework-specific config object."""
|
|
107
|
+
|
|
102
108
|
|
|
103
109
|
register_device_type("sdwan_appliance", SdwanApplianceDevice)
|
|
@@ -68,6 +68,12 @@ class L2Switch(BaseDeviceProtocol, Protocol):
|
|
|
68
68
|
syslog: SyslogConfig
|
|
69
69
|
ntp: NtpConfig
|
|
70
70
|
|
|
71
|
+
placement_ports: tuple[str, ...]
|
|
72
|
+
"""Designated ports a testbed may re-VLAN to place a client; empty means
|
|
73
|
+
no placement write is permitted on this switch. Independent of which
|
|
74
|
+
device's pipe enters a port: pipe ownership is topology, this is write
|
|
75
|
+
authorization — declared by the switch, the resource's owner."""
|
|
76
|
+
|
|
71
77
|
|
|
72
78
|
register_device_type("managed_switch_l2", L2Switch)
|
|
73
79
|
|
|
@@ -18,9 +18,7 @@ from testprotocols.models.switch import BindingSource, FhsBinding, FhsScope, Fhs
|
|
|
18
18
|
class FirstHopSecurity(Protocol):
|
|
19
19
|
"""Abstract contract for DHCP snooping + Dynamic ARP Inspection."""
|
|
20
20
|
|
|
21
|
-
def set_dhcp_snooping(
|
|
22
|
-
self, scope: FhsScope, enabled: bool, vlan: int | None = None
|
|
23
|
-
) -> None:
|
|
21
|
+
def set_dhcp_snooping(self, scope: FhsScope, enabled: bool, vlan: int | None = None) -> None:
|
|
24
22
|
"""Enable/disable DHCP snooping globally or per-VLAN. A switch-wide-only
|
|
25
23
|
product maps ``PER_VLAN`` to ``GLOBAL`` or raises unsupported-capability."""
|
|
26
24
|
...
|
|
@@ -41,7 +41,7 @@ class DhcpLeaseObservation:
|
|
|
41
41
|
server: str = ""
|
|
42
42
|
"""The DHCP server identifier, option 54 (dotted quad; empty if absent)."""
|
|
43
43
|
|
|
44
|
-
options: Mapping[int, str] = field(default_factory=dict)
|
|
44
|
+
options: Mapping[int, str] = field(default_factory=dict[int, str])
|
|
45
45
|
"""Raw received value per option code, as reported by the client."""
|
|
46
46
|
|
|
47
47
|
|
|
@@ -227,7 +227,7 @@ class OneToOneNatRule:
|
|
|
227
227
|
public_ip: str
|
|
228
228
|
lan_ip: str
|
|
229
229
|
uplink: str = "any"
|
|
230
|
-
allowed_inbound: list[NatInboundAllow] = field(default_factory=list)
|
|
230
|
+
allowed_inbound: list[NatInboundAllow] = field(default_factory=list[NatInboundAllow])
|
|
231
231
|
|
|
232
232
|
|
|
233
233
|
@dataclass
|
|
@@ -236,7 +236,7 @@ class OneToManyNatRule:
|
|
|
236
236
|
|
|
237
237
|
public_ip: str
|
|
238
238
|
uplink: str = "any"
|
|
239
|
-
port_forwards: list[PortForwardRule] = field(default_factory=list)
|
|
239
|
+
port_forwards: list[PortForwardRule] = field(default_factory=list[PortForwardRule])
|
|
240
240
|
|
|
241
241
|
|
|
242
242
|
# --- WAN uplinks ---
|
|
@@ -287,7 +287,7 @@ class SyslogServer:
|
|
|
287
287
|
|
|
288
288
|
host: str
|
|
289
289
|
port: int = 514
|
|
290
|
-
roles: list[SyslogRole] = field(default_factory=list)
|
|
290
|
+
roles: list[SyslogRole] = field(default_factory=list[SyslogRole])
|
|
291
291
|
|
|
292
292
|
|
|
293
293
|
# --- Threat prevention (IDS / IPS + malware) ---
|
|
@@ -423,10 +423,10 @@ class VlanConfig:
|
|
|
423
423
|
appliance_ip: str
|
|
424
424
|
dhcp_mode: DhcpMode = DhcpMode.SERVER
|
|
425
425
|
dhcp_lease_seconds: int = 86400
|
|
426
|
-
dns_servers: list[str] = field(default_factory=list)
|
|
427
|
-
dhcp_options: list[DhcpOption] = field(default_factory=list)
|
|
428
|
-
reservations: list[DhcpReservation] = field(default_factory=list)
|
|
429
|
-
reserved_ranges: list[tuple[str, str]] = field(default_factory=list)
|
|
426
|
+
dns_servers: list[str] = field(default_factory=list[str])
|
|
427
|
+
dhcp_options: list[DhcpOption] = field(default_factory=list[DhcpOption])
|
|
428
|
+
reservations: list[DhcpReservation] = field(default_factory=list[DhcpReservation])
|
|
429
|
+
reserved_ranges: list[tuple[str, str]] = field(default_factory=list[tuple[str, str]])
|
|
430
430
|
|
|
431
431
|
|
|
432
432
|
@dataclass
|
|
@@ -489,8 +489,8 @@ class SiteToSiteVpnConfig:
|
|
|
489
489
|
"""
|
|
490
490
|
|
|
491
491
|
role: VpnRole
|
|
492
|
-
hubs: list[VpnHub] = field(default_factory=list)
|
|
493
|
-
subnets: list[VpnSubnet] = field(default_factory=list)
|
|
492
|
+
hubs: list[VpnHub] = field(default_factory=list[VpnHub])
|
|
493
|
+
subnets: list[VpnSubnet] = field(default_factory=list[VpnSubnet])
|
|
494
494
|
|
|
495
495
|
|
|
496
496
|
@dataclass
|
|
@@ -619,8 +619,8 @@ class BgpConfig:
|
|
|
619
619
|
|
|
620
620
|
enabled: bool
|
|
621
621
|
as_number: int
|
|
622
|
-
neighbors: list[BgpNeighbor] = field(default_factory=list)
|
|
623
|
-
advertised_networks: list[str] = field(default_factory=list)
|
|
622
|
+
neighbors: list[BgpNeighbor] = field(default_factory=list[BgpNeighbor])
|
|
623
|
+
advertised_networks: list[str] = field(default_factory=list[str])
|
|
624
624
|
|
|
625
625
|
|
|
626
626
|
@dataclass
|
|
@@ -145,7 +145,7 @@ class SwitchPort:
|
|
|
145
145
|
mode: PortMode
|
|
146
146
|
enabled: bool = True
|
|
147
147
|
native_vlan: int | None = None
|
|
148
|
-
allowed_vlans: list[int] = field(default_factory=list)
|
|
148
|
+
allowed_vlans: list[int] = field(default_factory=list[int])
|
|
149
149
|
description: str = ""
|
|
150
150
|
voice_vlan: int | None = None
|
|
151
151
|
isolated: bool = False
|
|
@@ -195,7 +195,7 @@ class AccessPolicy:
|
|
|
195
195
|
|
|
196
196
|
port: str
|
|
197
197
|
policy_type: AccessPolicyType
|
|
198
|
-
allowed_macs: list[str] = field(default_factory=list)
|
|
198
|
+
allowed_macs: list[str] = field(default_factory=list[str])
|
|
199
199
|
max_macs: int | None = None
|
|
200
200
|
sticky: bool = False
|
|
201
201
|
|
|
@@ -209,7 +209,7 @@ class StormControlConfig:
|
|
|
209
209
|
"""
|
|
210
210
|
|
|
211
211
|
port: str
|
|
212
|
-
thresholds: dict[StormControlType, float] = field(default_factory=dict)
|
|
212
|
+
thresholds: dict[StormControlType, float] = field(default_factory=dict[StormControlType, float])
|
|
213
213
|
|
|
214
214
|
|
|
215
215
|
@dataclass
|
|
@@ -81,11 +81,11 @@ class InterfaceDhcpConfig:
|
|
|
81
81
|
interface: str
|
|
82
82
|
mode: DhcpMode = DhcpMode.DISABLED
|
|
83
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)
|
|
84
|
+
dns_servers: list[str] = field(default_factory=list[str])
|
|
85
|
+
options: list[DhcpOption] = field(default_factory=list[DhcpOption])
|
|
86
|
+
reservations: list[DhcpReservation] = field(default_factory=list[DhcpReservation])
|
|
87
|
+
reserved_ranges: list[tuple[str, str]] = field(default_factory=list[tuple[str, str]])
|
|
88
|
+
relay_targets: list[str] = field(default_factory=list[str])
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
@dataclass
|
|
@@ -105,8 +105,8 @@ class OspfConfig:
|
|
|
105
105
|
enabled: bool
|
|
106
106
|
router_id: str
|
|
107
107
|
version: OspfVersion = OspfVersion.V2
|
|
108
|
-
areas: list[str] = field(default_factory=list)
|
|
109
|
-
interfaces: list[OspfInterfaceSettings] = field(default_factory=list)
|
|
108
|
+
areas: list[str] = field(default_factory=list[str])
|
|
109
|
+
interfaces: list[OspfInterfaceSettings] = field(default_factory=list[OspfInterfaceSettings])
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
@dataclass
|
|
@@ -17,9 +17,7 @@ from testprotocols.models.switch import AclDirection, SwitchAclRule
|
|
|
17
17
|
class SwitchAcl(Protocol):
|
|
18
18
|
"""Abstract contract for the switch ACL."""
|
|
19
19
|
|
|
20
|
-
def set_acl(
|
|
21
|
-
self, binding: str, direction: AclDirection, rules: list[SwitchAclRule]
|
|
22
|
-
) -> None:
|
|
20
|
+
def set_acl(self, binding: str, direction: AclDirection, rules: list[SwitchAclRule]) -> None:
|
|
23
21
|
"""Replace the ordered ACL bound to *binding* (a port or ``vlan:<id>``)
|
|
24
22
|
in *direction*."""
|
|
25
23
|
...
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testprotocols
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
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>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Protocol-conformance tests for ApplianceVlans (runtime_checkable structural check)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from testprotocols.appliance_vlans import ApplianceVlans
|
|
6
|
+
from testprotocols.models.sdwan_appliance import DhcpLease, VlanConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _Complete:
|
|
10
|
+
"""Has every ApplianceVlans member, including delete_vlan.
|
|
11
|
+
|
|
12
|
+
Signatures mirror the protocol for documentation only — ``runtime_checkable``
|
|
13
|
+
``isinstance`` checks member presence, not shape, so these bodies are never
|
|
14
|
+
reached.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def list_vlans(self) -> list[VlanConfig]:
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
|
|
20
|
+
def get_vlan(self, vlan_id: int) -> VlanConfig:
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
|
|
23
|
+
def set_vlan(self, config: VlanConfig) -> None: ...
|
|
24
|
+
|
|
25
|
+
def delete_vlan(self, vlan_id: int) -> None: ...
|
|
26
|
+
|
|
27
|
+
def get_dhcp_leases(self, vlan_id: int | None = None) -> list[DhcpLease]:
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _NoDelete:
|
|
32
|
+
"""Missing delete_vlan — must NOT satisfy the protocol once delete_vlan is declared."""
|
|
33
|
+
|
|
34
|
+
def list_vlans(self) -> list[VlanConfig]:
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
def get_vlan(self, vlan_id: int) -> VlanConfig:
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
def set_vlan(self, config: VlanConfig) -> None: ...
|
|
41
|
+
|
|
42
|
+
def get_dhcp_leases(self, vlan_id: int | None = None) -> list[DhcpLease]:
|
|
43
|
+
raise NotImplementedError
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_complete_impl_satisfies_protocol() -> None:
|
|
47
|
+
assert isinstance(_Complete(), ApplianceVlans)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_delete_vlan_is_required_by_protocol() -> None:
|
|
51
|
+
# runtime_checkable isinstance requires every declared member be present.
|
|
52
|
+
assert not isinstance(_NoDelete(), ApplianceVlans)
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import importlib
|
|
10
10
|
|
|
11
11
|
import pytest
|
|
12
|
+
from _helpers import protocol_attrs
|
|
12
13
|
|
|
13
14
|
PROTOCOLS = [
|
|
14
15
|
(
|
|
@@ -93,5 +94,5 @@ def test_is_runtime_checkable(class_name: str, module: str, expected_methods: se
|
|
|
93
94
|
def test_protocol_shape(class_name: str, module: str, expected_methods: set[str]) -> None:
|
|
94
95
|
"""Each Protocol declares at least the expected method set."""
|
|
95
96
|
cls = getattr(importlib.import_module(module), class_name)
|
|
96
|
-
actual =
|
|
97
|
+
actual = protocol_attrs(cls)
|
|
97
98
|
assert expected_methods <= actual, f"{class_name} missing: {expected_methods - actual}"
|
|
@@ -10,6 +10,7 @@ inventory-string -> Protocol binding and the per-archetype capability set
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import pytest
|
|
13
|
+
from _helpers import protocol_attrs
|
|
13
14
|
from testprotocols.devices import DeviceTypeSpec, all_device_types, get_device_type
|
|
14
15
|
from testprotocols.devices.base import BaseDeviceProtocol
|
|
15
16
|
from testprotocols.devices.client import (
|
|
@@ -195,7 +196,7 @@ def test_sdwan_router_aggregates_expected_capabilities() -> None:
|
|
|
195
196
|
"nat",
|
|
196
197
|
"conntrack",
|
|
197
198
|
}
|
|
198
|
-
actual =
|
|
199
|
+
actual = protocol_attrs(SdwanRouterDevice)
|
|
199
200
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
200
201
|
assert "netem" not in actual, "netem should not be on SdwanRouterDevice"
|
|
201
202
|
|
|
@@ -219,8 +220,9 @@ def test_sdwan_appliance_aggregates_expected_capabilities() -> None:
|
|
|
219
220
|
"lan",
|
|
220
221
|
"syslog",
|
|
221
222
|
"vpn",
|
|
223
|
+
"manages_network",
|
|
222
224
|
}
|
|
223
|
-
actual =
|
|
225
|
+
actual = protocol_attrs(SdwanApplianceDevice)
|
|
224
226
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
225
227
|
# device_management is Linux-host-shaped (ps/memory/board-logs/file-content)
|
|
226
228
|
# and intentionally NOT on the managed-appliance archetype — see GAPS.md.
|
|
@@ -257,7 +259,7 @@ def test_cpe_aggregates_expected_capabilities() -> None:
|
|
|
257
259
|
"firewall_zones",
|
|
258
260
|
"ntp_client",
|
|
259
261
|
}
|
|
260
|
-
actual =
|
|
262
|
+
actual = protocol_attrs(CpeDevice)
|
|
261
263
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
262
264
|
|
|
263
265
|
|
|
@@ -281,7 +283,7 @@ def test_lan_client_aggregates_expected_capabilities() -> None:
|
|
|
281
283
|
"arp_client",
|
|
282
284
|
"vlan_client",
|
|
283
285
|
}
|
|
284
|
-
actual =
|
|
286
|
+
actual = protocol_attrs(LanClientDevice)
|
|
285
287
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
286
288
|
|
|
287
289
|
|
|
@@ -301,13 +303,13 @@ def test_wlan_client_aggregates_expected_capabilities() -> None:
|
|
|
301
303
|
"nmap_scanner",
|
|
302
304
|
"file_transfer",
|
|
303
305
|
}
|
|
304
|
-
actual =
|
|
306
|
+
actual = protocol_attrs(WlanClientDevice)
|
|
305
307
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
306
308
|
|
|
307
309
|
|
|
308
310
|
def test_qoe_client_aggregates_expected_capabilities() -> None:
|
|
309
311
|
expected = {"qoe_browser", "ip_interface", "dhcp_client", "syslog"}
|
|
310
|
-
actual =
|
|
312
|
+
actual = protocol_attrs(QoeClientDevice)
|
|
311
313
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
312
314
|
|
|
313
315
|
|
|
@@ -322,16 +324,18 @@ def test_qoe_measurement_client_aggregates_expected_capabilities() -> None:
|
|
|
322
324
|
"responder",
|
|
323
325
|
"pcap",
|
|
324
326
|
"syslog",
|
|
327
|
+
"attachment_subnet",
|
|
328
|
+
"mgmt_ip",
|
|
329
|
+
"pipe_switch",
|
|
330
|
+
"pipe_port",
|
|
325
331
|
}
|
|
326
|
-
actual =
|
|
332
|
+
actual = protocol_attrs(QoeMeasurementClientDevice)
|
|
327
333
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
328
334
|
|
|
329
335
|
|
|
330
336
|
def test_qoe_measurement_client_is_a_qoe_client() -> None:
|
|
331
337
|
"""Strict-superset invariant: the measurement client subsumes QoeClientDevice."""
|
|
332
|
-
assert
|
|
333
|
-
QoeMeasurementClientDevice.__protocol_attrs__
|
|
334
|
-
)
|
|
338
|
+
assert protocol_attrs(QoeClientDevice) <= protocol_attrs(QoeMeasurementClientDevice)
|
|
335
339
|
|
|
336
340
|
|
|
337
341
|
def test_acs_aggregates_expected_capabilities() -> None:
|
|
@@ -341,43 +345,43 @@ def test_acs_aggregates_expected_capabilities() -> None:
|
|
|
341
345
|
"file_transfer",
|
|
342
346
|
"packet_filter",
|
|
343
347
|
}
|
|
344
|
-
actual =
|
|
348
|
+
actual = protocol_attrs(AcsDevice)
|
|
345
349
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
346
350
|
|
|
347
351
|
|
|
348
352
|
def test_provisioner_aggregates_expected_capabilities() -> None:
|
|
349
353
|
expected = {"dhcp_server", "pcap", "file_transfer", "packet_filter"}
|
|
350
|
-
actual =
|
|
354
|
+
actual = protocol_attrs(ProvisionerDevice)
|
|
351
355
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
352
356
|
|
|
353
357
|
|
|
354
358
|
def test_tftp_aggregates_expected_capabilities() -> None:
|
|
355
359
|
expected = {"tftp_server"}
|
|
356
|
-
actual =
|
|
360
|
+
actual = protocol_attrs(TftpDevice)
|
|
357
361
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
358
362
|
|
|
359
363
|
|
|
360
364
|
def test_traffic_controller_aggregates_expected_capabilities() -> None:
|
|
361
365
|
expected = {"netem", "ip_interface", "pcap", "syslog"}
|
|
362
|
-
actual =
|
|
366
|
+
actual = protocol_attrs(TrafficControllerDevice)
|
|
363
367
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
364
368
|
|
|
365
369
|
|
|
366
370
|
def test_iperf_traffic_generator_aggregates_expected_capabilities() -> None:
|
|
367
371
|
expected = {"iperf_generator", "ip_interface"}
|
|
368
|
-
actual =
|
|
372
|
+
actual = protocol_attrs(IperfTrafficGeneratorDevice)
|
|
369
373
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
370
374
|
|
|
371
375
|
|
|
372
376
|
def test_sip_phone_device_aggregates_expected_capabilities() -> None:
|
|
373
377
|
expected = {"sip_phone", "ip_interface", "dhcp_client", "number"}
|
|
374
|
-
actual =
|
|
378
|
+
actual = protocol_attrs(SipPhoneDevice)
|
|
375
379
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
376
380
|
|
|
377
381
|
|
|
378
382
|
def test_sip_server_device_aggregates_expected_capabilities() -> None:
|
|
379
383
|
expected = {"sip_server", "pcap", "file_transfer"}
|
|
380
|
-
actual =
|
|
384
|
+
actual = protocol_attrs(SipServerDevice)
|
|
381
385
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
382
386
|
|
|
383
387
|
|
|
@@ -400,7 +404,7 @@ def test_wan_server_aggregates_expected_capabilities() -> None:
|
|
|
400
404
|
"nat",
|
|
401
405
|
"conntrack",
|
|
402
406
|
}
|
|
403
|
-
actual =
|
|
407
|
+
actual = protocol_attrs(WanServerDevice)
|
|
404
408
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
405
409
|
|
|
406
410
|
|
|
@@ -411,7 +415,7 @@ def test_wan_server_aggregates_expected_capabilities() -> None:
|
|
|
411
415
|
|
|
412
416
|
def test_acs_firewall_facets() -> None:
|
|
413
417
|
"""ACS aggregates packet_filter only — no NAT / port-forwarding / conntrack / zones."""
|
|
414
|
-
attrs =
|
|
418
|
+
attrs = protocol_attrs(AcsDevice)
|
|
415
419
|
assert "packet_filter" in attrs
|
|
416
420
|
for absent in ("nat", "port_forwarding", "conntrack", "firewall_zones"):
|
|
417
421
|
assert absent not in attrs, f"AcsDevice unexpectedly aggregates {absent}"
|
|
@@ -419,13 +423,13 @@ def test_acs_firewall_facets() -> None:
|
|
|
419
423
|
|
|
420
424
|
def test_provisioner_firewall_facets() -> None:
|
|
421
425
|
"""Provisioner aggregates packet_filter."""
|
|
422
|
-
attrs =
|
|
426
|
+
attrs = protocol_attrs(ProvisionerDevice)
|
|
423
427
|
assert "packet_filter" in attrs
|
|
424
428
|
|
|
425
429
|
|
|
426
430
|
def test_lan_client_firewall_facets() -> None:
|
|
427
431
|
"""LanClientDevice aggregates packet_filter only."""
|
|
428
|
-
attrs =
|
|
432
|
+
attrs = protocol_attrs(LanClientDevice)
|
|
429
433
|
assert "packet_filter" in attrs
|
|
430
434
|
for absent in ("nat", "port_forwarding", "conntrack", "firewall_zones"):
|
|
431
435
|
assert absent not in attrs, f"LanClientDevice unexpectedly aggregates {absent}"
|
|
@@ -433,7 +437,7 @@ def test_lan_client_firewall_facets() -> None:
|
|
|
433
437
|
|
|
434
438
|
def test_wan_server_firewall_facets() -> None:
|
|
435
439
|
"""WanServerDevice aggregates packet_filter, nat, conntrack — no port-forwarding / zones."""
|
|
436
|
-
attrs =
|
|
440
|
+
attrs = protocol_attrs(WanServerDevice)
|
|
437
441
|
for present in ("packet_filter", "nat", "conntrack"):
|
|
438
442
|
assert present in attrs
|
|
439
443
|
for absent in ("port_forwarding", "firewall_zones"):
|
|
@@ -442,7 +446,7 @@ def test_wan_server_firewall_facets() -> None:
|
|
|
442
446
|
|
|
443
447
|
def test_cpe_full_firewall_surface() -> None:
|
|
444
448
|
"""CpeDevice aggregates the complete firewall surface."""
|
|
445
|
-
attrs =
|
|
449
|
+
attrs = protocol_attrs(CpeDevice)
|
|
446
450
|
for present in ("firewall", "nat", "conntrack", "firewall_zones"):
|
|
447
451
|
assert present in attrs
|
|
448
452
|
# PortForwarding is no longer a separate attribute — it's bundled into firewall.
|
|
@@ -455,7 +459,12 @@ def test_cpe_full_firewall_surface() -> None:
|
|
|
455
459
|
def test_devices_without_firewall() -> None:
|
|
456
460
|
"""These archetypes must not pull in any of the firewall capabilities."""
|
|
457
461
|
firewall_attrs = (
|
|
458
|
-
"packet_filter",
|
|
462
|
+
"packet_filter",
|
|
463
|
+
"firewall",
|
|
464
|
+
"nat",
|
|
465
|
+
"port_forwarding",
|
|
466
|
+
"conntrack",
|
|
467
|
+
"firewall_zones",
|
|
459
468
|
)
|
|
460
469
|
for archetype in (
|
|
461
470
|
TftpDevice,
|
|
@@ -466,7 +475,7 @@ def test_devices_without_firewall() -> None:
|
|
|
466
475
|
TrafficControllerDevice,
|
|
467
476
|
IperfTrafficGeneratorDevice,
|
|
468
477
|
):
|
|
469
|
-
attrs =
|
|
478
|
+
attrs = protocol_attrs(archetype)
|
|
470
479
|
for absent in firewall_attrs:
|
|
471
480
|
assert absent not in attrs, f"{archetype.__name__} unexpectedly aggregates {absent}"
|
|
472
481
|
|
|
@@ -497,10 +506,10 @@ def test_archetype_inherits_base_device_protocol(archetype: type) -> None:
|
|
|
497
506
|
@pytest.mark.parametrize("archetype", _ALL_ARCHETYPES, ids=lambda a: a.__name__)
|
|
498
507
|
def test_archetype_carries_universal_identity(archetype: type) -> None:
|
|
499
508
|
"""Every archetype's protocol-attrs include device_name and device_type."""
|
|
500
|
-
attrs =
|
|
509
|
+
attrs = protocol_attrs(archetype)
|
|
501
510
|
assert {"device_name", "device_type"} <= attrs, (
|
|
502
511
|
f"{archetype.__name__} missing universal-identity attrs: "
|
|
503
|
-
f"{ {'device_name', 'device_type'} - attrs}"
|
|
512
|
+
f"{ {'device_name', 'device_type'} - attrs }"
|
|
504
513
|
)
|
|
505
514
|
|
|
506
515
|
|
|
@@ -529,36 +538,58 @@ def test_l3_switch_routed_registered() -> None:
|
|
|
529
538
|
|
|
530
539
|
def test_l3_switch_is_strict_superset_of_l2() -> None:
|
|
531
540
|
# Protocol inheritance: every L3 attribute set includes the full L2 set.
|
|
532
|
-
l2_attrs =
|
|
533
|
-
l3_attrs =
|
|
541
|
+
l2_attrs = protocol_attrs(L2Switch)
|
|
542
|
+
l3_attrs = protocol_attrs(L3Switch)
|
|
534
543
|
assert l2_attrs <= l3_attrs, f"L3Switch missing L2 attrs: {l2_attrs - l3_attrs}"
|
|
535
544
|
expected_l3 = {
|
|
536
|
-
"routed_interfaces",
|
|
537
|
-
"
|
|
545
|
+
"routed_interfaces",
|
|
546
|
+
"static_routes",
|
|
547
|
+
"routing_read",
|
|
548
|
+
"ospf",
|
|
549
|
+
"interface_dhcp",
|
|
550
|
+
"gateway_redundancy",
|
|
538
551
|
}
|
|
539
552
|
assert expected_l3 <= l3_attrs, f"missing: {expected_l3 - l3_attrs}"
|
|
540
553
|
assert "bgp" not in l3_attrs, "bgp belongs only on L3SwitchRouted"
|
|
541
554
|
|
|
542
555
|
|
|
543
556
|
def test_l3_switch_routed_adds_bgp() -> None:
|
|
544
|
-
attrs =
|
|
557
|
+
attrs = protocol_attrs(L3SwitchRouted)
|
|
545
558
|
assert "bgp" in attrs
|
|
546
|
-
assert
|
|
559
|
+
assert protocol_attrs(L3Switch) <= attrs # still a superset of L3Switch
|
|
547
560
|
|
|
548
561
|
|
|
549
562
|
def test_l2_switch_aggregates_expected_capabilities() -> None:
|
|
550
563
|
expected = {
|
|
551
|
-
"switch_ports",
|
|
552
|
-
"
|
|
553
|
-
"
|
|
554
|
-
"
|
|
564
|
+
"switch_ports",
|
|
565
|
+
"switch_vlans",
|
|
566
|
+
"spanning_tree",
|
|
567
|
+
"link_aggregation",
|
|
568
|
+
"port_poe",
|
|
569
|
+
"port_security",
|
|
570
|
+
"radius",
|
|
571
|
+
"first_hop_security",
|
|
572
|
+
"storm_control",
|
|
573
|
+
"switch_acl",
|
|
574
|
+
"discovery",
|
|
575
|
+
"mac_table",
|
|
576
|
+
"port_status",
|
|
577
|
+
"switch_qos",
|
|
578
|
+
"syslog",
|
|
579
|
+
"ntp",
|
|
580
|
+
"placement_ports",
|
|
555
581
|
}
|
|
556
|
-
actual =
|
|
582
|
+
actual = protocol_attrs(L2Switch)
|
|
557
583
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
558
584
|
# host-substrate levers stay on the Linux twins, never on a managed switch
|
|
559
585
|
for host_lever in (
|
|
560
|
-
"conntrack",
|
|
561
|
-
"
|
|
586
|
+
"conntrack",
|
|
587
|
+
"pcap",
|
|
588
|
+
"ip_interface",
|
|
589
|
+
"nat",
|
|
590
|
+
"packet_filter",
|
|
591
|
+
"firewall_zones",
|
|
592
|
+
"wan_admin",
|
|
562
593
|
):
|
|
563
594
|
assert host_lever not in actual, f"{host_lever} must not be on L2Switch"
|
|
564
595
|
|
|
@@ -570,19 +601,29 @@ def test_l3_switch_excludes_host_substrate_levers() -> None:
|
|
|
570
601
|
Linux-host-shaped ``ip_interface``; firewall / NAT / conntrack belong on the
|
|
571
602
|
CPE/router archetypes; wan_admin is a Linux-only forced link-down lever.
|
|
572
603
|
"""
|
|
573
|
-
actual =
|
|
604
|
+
actual = protocol_attrs(L3Switch)
|
|
574
605
|
for host_lever in (
|
|
575
|
-
"conntrack",
|
|
576
|
-
"
|
|
606
|
+
"conntrack",
|
|
607
|
+
"pcap",
|
|
608
|
+
"ip_interface",
|
|
609
|
+
"nat",
|
|
610
|
+
"packet_filter",
|
|
611
|
+
"firewall_zones",
|
|
612
|
+
"wan_admin",
|
|
577
613
|
):
|
|
578
614
|
assert host_lever not in actual, f"{host_lever} must not be on L3Switch"
|
|
579
615
|
|
|
580
616
|
|
|
581
617
|
def test_l3_switch_routed_excludes_host_substrate_levers() -> None:
|
|
582
618
|
"""L3SwitchRouted (the BGP-capable variant) likewise excludes host-substrate levers."""
|
|
583
|
-
actual =
|
|
619
|
+
actual = protocol_attrs(L3SwitchRouted)
|
|
584
620
|
for host_lever in (
|
|
585
|
-
"conntrack",
|
|
586
|
-
"
|
|
621
|
+
"conntrack",
|
|
622
|
+
"pcap",
|
|
623
|
+
"ip_interface",
|
|
624
|
+
"nat",
|
|
625
|
+
"packet_filter",
|
|
626
|
+
"firewall_zones",
|
|
627
|
+
"wan_admin",
|
|
587
628
|
):
|
|
588
629
|
assert host_lever not in actual, f"{host_lever} must not be on L3SwitchRouted"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from _helpers import protocol_attrs
|
|
5
6
|
from testprotocols.dhcp_client import DhcpClient
|
|
6
7
|
from testprotocols.dhcp_server import DhcpServer
|
|
7
8
|
from testprotocols.models.dhcp import DhcpLeaseObservation
|
|
@@ -25,7 +26,7 @@ def test_dhcp_client_protocol_shape() -> None:
|
|
|
25
26
|
"observe_lease",
|
|
26
27
|
"release_observed_lease",
|
|
27
28
|
}
|
|
28
|
-
actual =
|
|
29
|
+
actual = protocol_attrs(DhcpClient)
|
|
29
30
|
assert expected <= actual, f"missing: {expected - actual}"
|
|
30
31
|
|
|
31
32
|
|
|
@@ -53,5 +54,5 @@ def test_dhcp_server_is_runtime_checkable() -> None:
|
|
|
53
54
|
def test_dhcp_server_protocol_shape() -> None:
|
|
54
55
|
"""DhcpServer declares the expected method set."""
|
|
55
56
|
expected = {"provision_cpe"}
|
|
56
|
-
actual =
|
|
57
|
+
actual = protocol_attrs(DhcpServer)
|
|
57
58
|
assert expected <= actual, f"missing: {expected - actual}"
|