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,22 @@
|
|
|
1
|
+
"""HTTP / Server template.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for HTTP server operations including
|
|
4
|
+
starting and stopping an HTTP service.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class HttpServer(Protocol):
|
|
14
|
+
"""Abstract contract for HTTP server operations."""
|
|
15
|
+
|
|
16
|
+
def start_http_service(self, port: str, ip_version: str) -> str:
|
|
17
|
+
"""Start an HTTP service on *port* for *ip_version*."""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
def stop_http_service(self, port: str) -> None:
|
|
21
|
+
"""Stop the HTTP service listening on *port*."""
|
|
22
|
+
...
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Hardware console template.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for managing physical hardware consoles,
|
|
4
|
+
power cycling, and bootloader-level flashing of a device under test.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class HwConsole(Protocol):
|
|
14
|
+
"""Abstract contract for hardware console and power management operations."""
|
|
15
|
+
|
|
16
|
+
def connect_to_consoles(self, device_name: str) -> None:
|
|
17
|
+
"""Connect to all hardware consoles for the named device."""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
def disconnect_from_consoles(self) -> None:
|
|
21
|
+
"""Disconnect from all hardware consoles."""
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
def get_console(self, console_name: str) -> Any:
|
|
25
|
+
"""Return the console object identified by *console_name*."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def get_interactive_consoles(self) -> dict[str, Any]:
|
|
29
|
+
"""Return a mapping of console names to interactive console objects."""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
def power_cycle(self) -> None:
|
|
33
|
+
"""Power cycle the device (power off, then power on)."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
def wait_for_hw_boot(self) -> None:
|
|
37
|
+
"""Block until the hardware has completed its boot sequence."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def flash_via_bootloader(
|
|
41
|
+
self,
|
|
42
|
+
image: str,
|
|
43
|
+
tftp_devices: dict[str, Any],
|
|
44
|
+
termination_sys: Any = None,
|
|
45
|
+
method: str | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Flash the given image to the device via the bootloader."""
|
|
48
|
+
...
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Infra-controller capability template.
|
|
2
|
+
|
|
3
|
+
Abstract contract for environment/fabric control delegated by the test
|
|
4
|
+
orchestrator to a hypervisor (vSphere). It reads and (for re-homing) moves a
|
|
5
|
+
VM NIC's port-group, and asserts a named port-group is present. It does NOT
|
|
6
|
+
create fabric — the lab carries the test VLANs already.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Protocol, runtime_checkable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@runtime_checkable
|
|
15
|
+
class InfraController(Protocol):
|
|
16
|
+
"""Read / presence-check / move a VM NIC's port-group."""
|
|
17
|
+
|
|
18
|
+
def get_nic_portgroup(self, vm_name: str, nic_index: int) -> str:
|
|
19
|
+
"""Return the portgroup name backing NIC *nic_index* on *vm_name*."""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def ensure_port_group(self, name: str) -> None:
|
|
23
|
+
"""Assert a portgroup named *name* exists (raise if absent). No create."""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
def set_nic_portgroup(self, vm_name: str, nic_index: int, portgroup: str) -> None:
|
|
27
|
+
"""Repoint NIC *nic_index* on *vm_name* onto the named *portgroup*."""
|
|
28
|
+
...
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Per-SVI DHCP server/relay configuration (reuses appliance DHCP models)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
from testprotocols.models.sdwan_appliance import DhcpLease
|
|
8
|
+
from testprotocols.models.switch_routing import InterfaceDhcpConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@runtime_checkable
|
|
12
|
+
class InterfaceDhcp(Protocol):
|
|
13
|
+
"""Abstract contract for per-interface DHCP server/relay.
|
|
14
|
+
|
|
15
|
+
Best-effort lease read: a product without a true lease table may approximate
|
|
16
|
+
or raise unsupported-capability on ``get_dhcp_leases``.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def set_interface_dhcp(self, config: InterfaceDhcpConfig) -> None:
|
|
20
|
+
"""Create or replace the DHCP config for ``config.interface``."""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
def get_interface_dhcp(self, interface: str) -> InterfaceDhcpConfig:
|
|
24
|
+
"""Return the DHCP config for *interface*."""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
def get_dhcp_leases(self, interface: str | None = None) -> list[DhcpLease]:
|
|
28
|
+
"""Return current DHCP leases (best-effort; a product without a true
|
|
29
|
+
lease table may approximate or raise unsupported-capability)."""
|
|
30
|
+
...
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""IP / Interface template.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for querying and configuring IP interface state.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from ipaddress import IPv4Address
|
|
9
|
+
from typing import Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class IpInterface(Protocol):
|
|
14
|
+
"""Abstract contract for IP interface operations."""
|
|
15
|
+
|
|
16
|
+
def get_interface_ipv4addr(self, interface: str) -> str:
|
|
17
|
+
"""Return the IPv4 address assigned to *interface*."""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
def get_interface_ipv6addr(self, interface: str) -> str:
|
|
21
|
+
"""Return the global IPv6 address assigned to *interface*."""
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
def get_interface_link_local_ipv6addr(self, interface: str) -> str:
|
|
25
|
+
"""Return the link-local IPv6 address of *interface*."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def get_interface_macaddr(self, interface: str) -> str:
|
|
29
|
+
"""Return the MAC address of *interface*."""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
def get_interface_mask(self, interface: str) -> str:
|
|
33
|
+
"""Return the subnet mask of *interface*."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
def get_interface_mtu_size(self, interface: str) -> int:
|
|
37
|
+
"""Return the MTU size of *interface*."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def is_link_up(self, interface: str, pattern: str = "BROADCAST,MULTICAST,UP") -> bool:
|
|
41
|
+
"""Return True if *interface* flags match *pattern*."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
def set_link_state(self, interface: str, state: str) -> None:
|
|
45
|
+
"""Bring *interface* up or down according to *state*."""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
def enable_ipv6(self) -> None:
|
|
49
|
+
"""Enable IPv6 on the device."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def disable_ipv6(self) -> None:
|
|
53
|
+
"""Disable IPv6 on the device."""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
def set_static_ip(self, interface: str, ip_address: IPv4Address, netmask: IPv4Address) -> None:
|
|
57
|
+
"""Assign a static IP address and netmask to *interface*."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
def remove_static_ip(self, interface: str) -> None:
|
|
61
|
+
"""Remove any static IP address configuration from *interface*."""
|
|
62
|
+
...
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""IP / Routing template.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for IP routing operations such as ping, traceroute,
|
|
4
|
+
and default gateway management.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ipaddress import IPv4Address
|
|
10
|
+
from typing import Any, Protocol, runtime_checkable
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@runtime_checkable
|
|
14
|
+
class IpRouting(Protocol):
|
|
15
|
+
"""Abstract contract for IP routing operations."""
|
|
16
|
+
|
|
17
|
+
def ping(
|
|
18
|
+
self,
|
|
19
|
+
ping_ip: str,
|
|
20
|
+
ping_count: int = 4,
|
|
21
|
+
ping_interface: str | None = None,
|
|
22
|
+
options: str = "",
|
|
23
|
+
timeout: int = 50,
|
|
24
|
+
json_output: bool = False,
|
|
25
|
+
) -> bool | dict[str, Any]:
|
|
26
|
+
"""Send ICMP echo requests to *ping_ip* and return success or parsed output."""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def traceroute(
|
|
30
|
+
self,
|
|
31
|
+
host_ip: str | IPv4Address,
|
|
32
|
+
version: str = "",
|
|
33
|
+
options: str = "",
|
|
34
|
+
timeout: int = 60,
|
|
35
|
+
) -> str | None:
|
|
36
|
+
"""Run a traceroute to *host_ip* and return the output."""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
def add_route(self, destination: str, gw_interface: str) -> None:
|
|
40
|
+
"""Add a static route to *destination* via *gw_interface*."""
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
def delete_route(self, destination: str) -> None:
|
|
44
|
+
"""Delete the static route to *destination*."""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
def get_default_gateway(self) -> IPv4Address:
|
|
48
|
+
"""Return the current default gateway address."""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
def del_default_route(self, interface: str | None = None) -> None:
|
|
52
|
+
"""Delete the default route, optionally scoped to *interface*."""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
def set_default_gw(self, ip_address: IPv4Address, interface: str) -> None:
|
|
56
|
+
"""Set the default gateway to *ip_address* via *interface*."""
|
|
57
|
+
...
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Traffic / IperfClient template.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for iperf client operations including
|
|
4
|
+
sending traffic and collecting logs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class IperfClient(Protocol):
|
|
14
|
+
"""Abstract contract for iperf client (traffic sender) operations."""
|
|
15
|
+
|
|
16
|
+
def start_traffic_sender(
|
|
17
|
+
self,
|
|
18
|
+
host: str,
|
|
19
|
+
traffic_port: int,
|
|
20
|
+
bandwidth: int | None = None,
|
|
21
|
+
bind_to_ip: str | None = None,
|
|
22
|
+
direction: str | None = None,
|
|
23
|
+
ip_version: int | None = None,
|
|
24
|
+
udp_protocol: bool = False,
|
|
25
|
+
time: int = 10,
|
|
26
|
+
client_port: int | None = None,
|
|
27
|
+
udp_only: bool | None = None,
|
|
28
|
+
) -> tuple[int, str]:
|
|
29
|
+
"""Start an iperf traffic sender towards *host* on *traffic_port*.
|
|
30
|
+
|
|
31
|
+
Returns a tuple of (pid, log_file_path).
|
|
32
|
+
"""
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
def stop_traffic(self, pid: int | None = None) -> bool:
|
|
36
|
+
"""Stop a running iperf traffic sender.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
pid:
|
|
41
|
+
Process ID to stop. If *None*, stop the most recently started sender.
|
|
42
|
+
"""
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
def get_iperf_logs(self, log_file: str) -> str:
|
|
46
|
+
"""Return the iperf log contents from *log_file*."""
|
|
47
|
+
...
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Traffic / IperfGenerator template.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for a higher-level iperf traffic generator
|
|
4
|
+
that manages flows and returns structured results.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
from testprotocols.models.traffic import TrafficResult, TrafficSpec
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@runtime_checkable
|
|
15
|
+
class IperfGenerator(Protocol):
|
|
16
|
+
"""Abstract contract for iperf-based traffic flow management."""
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def server_ip(self) -> str:
|
|
20
|
+
"""IP address of the iperf server."""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def active_flows(self) -> list[str]:
|
|
25
|
+
"""List of currently active traffic flow IDs."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def start_traffic(self, spec: TrafficSpec) -> str:
|
|
29
|
+
"""Start a traffic flow defined by *spec* and return its flow ID."""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
def stop_traffic(self, flow_id: str) -> TrafficResult:
|
|
33
|
+
"""Stop the traffic flow identified by *flow_id* and return its result."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
def stop_all_traffic(self) -> dict[str, TrafficResult]:
|
|
37
|
+
"""Stop all active traffic flows and return a mapping of flow_id to result."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def run_traffic(self, spec: TrafficSpec) -> TrafficResult:
|
|
41
|
+
"""Run a traffic flow defined by *spec* to completion and return the result."""
|
|
42
|
+
...
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Traffic / IperfServer template.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for iperf server operations including
|
|
4
|
+
receiving traffic and collecting logs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class IperfServer(Protocol):
|
|
14
|
+
"""Abstract contract for iperf server (traffic receiver) operations."""
|
|
15
|
+
|
|
16
|
+
def start_traffic_receiver(
|
|
17
|
+
self,
|
|
18
|
+
traffic_port: int,
|
|
19
|
+
bind_to_ip: str | None = None,
|
|
20
|
+
ip_version: int | None = None,
|
|
21
|
+
udp_only: bool | None = None,
|
|
22
|
+
) -> tuple[int, str]:
|
|
23
|
+
"""Start an iperf traffic receiver on *traffic_port*.
|
|
24
|
+
|
|
25
|
+
Returns a tuple of (pid, log_file_path).
|
|
26
|
+
"""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def stop_traffic(self, pid: int | None = None) -> bool:
|
|
30
|
+
"""Stop a running iperf traffic receiver.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
pid:
|
|
35
|
+
Process ID to stop. If *None*, stop the most recently started receiver.
|
|
36
|
+
"""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
def get_iperf_logs(self, log_file: str) -> str:
|
|
40
|
+
"""Return the iperf log contents from *log_file*."""
|
|
41
|
+
...
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""L3 firewall template — managed SD-WAN appliance edition.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for a managed SD-WAN appliance's L3 firewall:
|
|
4
|
+
**ordered allow/deny policy lists** for outbound (LAN→WAN) and inbound (WAN→LAN)
|
|
5
|
+
traffic. A managed appliance exposes each direction as a single ordered list
|
|
6
|
+
that is read and replaced as a whole — distinct from
|
|
7
|
+
``packet_filter.PacketFilter`` (netfilter INPUT/OUTPUT/FORWARD chains, add/remove
|
|
8
|
+
a rule by name), which models a Linux host's stateless filter.
|
|
9
|
+
|
|
10
|
+
In scope: read and replace the outbound, inbound, and site-to-site VPN
|
|
11
|
+
ordered rule lists.
|
|
12
|
+
|
|
13
|
+
Out of scope: L7 / application-aware rules (see ``l7_firewall``), URL / category
|
|
14
|
+
filtering (see ``content_filtering``), NAT and port-forwarding (see
|
|
15
|
+
``appliance_nat``), and netfilter-chain filtering (see ``packet_filter``).
|
|
16
|
+
|
|
17
|
+
Rules are transport-agnostic ``L3Rule`` records; the driver translates them
|
|
18
|
+
into its product's API. Value vocabularies (``action``, ``protocol``) are the
|
|
19
|
+
normalized sets in ``models.sdwan_appliance``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from typing import Protocol, runtime_checkable
|
|
25
|
+
|
|
26
|
+
from testprotocols.models.sdwan_appliance import L3Rule
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@runtime_checkable
|
|
30
|
+
class L3Firewall(Protocol):
|
|
31
|
+
"""Abstract contract for an appliance's ordered L3 firewall policy."""
|
|
32
|
+
|
|
33
|
+
def set_outbound_rules(self, rules: list[L3Rule]) -> None:
|
|
34
|
+
"""Replace the ordered outbound (LAN→WAN) policy with *rules*.
|
|
35
|
+
|
|
36
|
+
The list is the complete policy in evaluation order; the driver
|
|
37
|
+
replaces the appliance's outbound ruleset wholesale.
|
|
38
|
+
"""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
def get_outbound_rules(self) -> list[L3Rule]:
|
|
42
|
+
"""Return the outbound (LAN→WAN) rules in evaluation order."""
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
def set_inbound_rules(self, rules: list[L3Rule]) -> None:
|
|
46
|
+
"""Replace the ordered inbound (WAN→LAN) policy with *rules*.
|
|
47
|
+
|
|
48
|
+
The list is the complete policy in evaluation order; the driver
|
|
49
|
+
replaces the appliance's inbound ruleset wholesale.
|
|
50
|
+
|
|
51
|
+
Conformance note: not every appliance family exposes a generic
|
|
52
|
+
inbound rule list — some offer only an implicit stateful deny plus
|
|
53
|
+
NAT-scoped inbound allowances. Such drivers raise
|
|
54
|
+
unsupported-capability here rather than approximating.
|
|
55
|
+
"""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
def get_inbound_rules(self) -> list[L3Rule]:
|
|
59
|
+
"""Return the inbound (WAN→LAN) rules in evaluation order."""
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
def set_vpn_rules(self, rules: list[L3Rule]) -> None:
|
|
63
|
+
"""Replace the ordered site-to-site VPN policy with *rules*.
|
|
64
|
+
|
|
65
|
+
The list is the complete policy, in evaluation order, governing
|
|
66
|
+
traffic traversing the site-to-site VPN overlay. On some products
|
|
67
|
+
this rule set is scoped wider than a single device (e.g.
|
|
68
|
+
fleet-wide); that is a driver/testbed concern, not a contract one.
|
|
69
|
+
"""
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
def get_vpn_rules(self) -> list[L3Rule]:
|
|
73
|
+
"""Return the site-to-site VPN rules in evaluation order."""
|
|
74
|
+
...
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""L7 firewall template — application-aware allow/deny for a managed appliance.
|
|
2
|
+
|
|
3
|
+
Defines the abstract contract for an SD-WAN appliance's L7 (application-aware)
|
|
4
|
+
firewall: an ordered list of rules that match by application, application
|
|
5
|
+
category, host, port, or IP range and allow/deny accordingly. Like
|
|
6
|
+
``l3_firewall.L3Firewall`` the policy is read and replaced as a whole list.
|
|
7
|
+
|
|
8
|
+
In scope: read and replace the ordered L7 rule list.
|
|
9
|
+
|
|
10
|
+
Out of scope: L3 5-tuple filtering (see ``l3_firewall``), URL / content-category
|
|
11
|
+
blocking (see ``content_filtering``), and traffic shaping (see
|
|
12
|
+
``traffic_shaping``).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Protocol, runtime_checkable
|
|
18
|
+
|
|
19
|
+
from testprotocols.models.sdwan_appliance import L7Rule
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@runtime_checkable
|
|
23
|
+
class L7Firewall(Protocol):
|
|
24
|
+
"""Abstract contract for an appliance's ordered L7 firewall policy."""
|
|
25
|
+
|
|
26
|
+
def set_rules(self, rules: list[L7Rule]) -> None:
|
|
27
|
+
"""Replace the ordered L7 policy with *rules* (complete, in order)."""
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
def get_rules(self) -> list[L7Rule]:
|
|
31
|
+
"""Return the L7 rules in evaluation order."""
|
|
32
|
+
...
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Link-aggregation (LAG) configuration by member ports + mode."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
from testprotocols.models.switch import LinkAggregationGroup
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@runtime_checkable
|
|
11
|
+
class LinkAggregation(Protocol):
|
|
12
|
+
"""Abstract contract for LAG configuration."""
|
|
13
|
+
|
|
14
|
+
def list_groups(self) -> list[LinkAggregationGroup]:
|
|
15
|
+
"""Return every aggregation group."""
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def set_group(self, group: LinkAggregationGroup) -> None:
|
|
19
|
+
"""Create or replace the group ``group.name``."""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def remove_group(self, name: str) -> None:
|
|
23
|
+
"""Remove the group *name*."""
|
|
24
|
+
...
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Read-only forwarding-database (FDB / MAC address-table) read."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
from testprotocols.models.l2_common import MacTableEntry
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@runtime_checkable
|
|
11
|
+
class MacTable(Protocol):
|
|
12
|
+
"""Abstract contract for the FDB read.
|
|
13
|
+
|
|
14
|
+
A product with no FDB API raises unsupported-capability. A raw-dump white-box
|
|
15
|
+
extension is a LEVELS.md candidate (``MacTableWhiteBox``).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def get_mac_table(self, vlan: int | None = None) -> list[MacTableEntry]:
|
|
19
|
+
"""Return FDB entries, optionally filtered to one *vlan*."""
|
|
20
|
+
...
|