testprotocols 0.5.0__tar.gz → 0.5.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.
Files changed (145) hide show
  1. {testprotocols-0.5.0/src/testprotocols.egg-info → testprotocols-0.5.2}/PKG-INFO +1 -1
  2. {testprotocols-0.5.0 → testprotocols-0.5.2}/pyproject.toml +1 -1
  3. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/client.py +8 -0
  4. testprotocols-0.5.2/src/testprotocols/dhcp_client.py +57 -0
  5. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/ip_interface.py +12 -0
  6. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/iperf_client.py +4 -0
  7. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/__init__.py +2 -1
  8. testprotocols-0.5.2/src/testprotocols/models/dhcp.py +65 -0
  9. {testprotocols-0.5.0 → testprotocols-0.5.2/src/testprotocols.egg-info}/PKG-INFO +1 -1
  10. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_dhcp_templates.py +21 -1
  11. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_ip_interface.py +1 -0
  12. testprotocols-0.5.0/src/testprotocols/dhcp_client.py +0 -30
  13. testprotocols-0.5.0/src/testprotocols/models/dhcp.py +0 -28
  14. {testprotocols-0.5.0 → testprotocols-0.5.2}/LICENSE +0 -0
  15. {testprotocols-0.5.0 → testprotocols-0.5.2}/NOTICE +0 -0
  16. {testprotocols-0.5.0 → testprotocols-0.5.2}/README.md +0 -0
  17. {testprotocols-0.5.0 → testprotocols-0.5.2}/setup.cfg +0 -0
  18. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/__init__.py +0 -0
  19. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/aftr_gateway.py +0 -0
  20. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/appliance_nat.py +0 -0
  21. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/appliance_uplinks.py +0 -0
  22. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/appliance_vlans.py +0 -0
  23. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/arp_client.py +0 -0
  24. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/bgp.py +0 -0
  25. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/conntrack.py +0 -0
  26. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/content_filtering.py +0 -0
  27. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/device_lifecycle.py +0 -0
  28. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/device_management.py +0 -0
  29. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/__init__.py +0 -0
  30. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/base.py +0 -0
  31. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/cpe.py +0 -0
  32. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/infra.py +0 -0
  33. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/sdwan.py +0 -0
  34. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/switch.py +0 -0
  35. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/traffic.py +0 -0
  36. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/voice.py +0 -0
  37. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/devices/wan.py +0 -0
  38. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/dhcp_server.py +0 -0
  39. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/discovery.py +0 -0
  40. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/dns_client.py +0 -0
  41. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/file_transfer.py +0 -0
  42. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/firewall.py +0 -0
  43. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/firewall_zones.py +0 -0
  44. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/first_hop_security.py +0 -0
  45. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/gateway_redundancy.py +0 -0
  46. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/http_client.py +0 -0
  47. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/http_server.py +0 -0
  48. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/hw_console.py +0 -0
  49. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/interface_dhcp.py +0 -0
  50. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/ip_routing.py +0 -0
  51. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/iperf_generator.py +0 -0
  52. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/iperf_server.py +0 -0
  53. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/l3_firewall.py +0 -0
  54. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/l7_firewall.py +0 -0
  55. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/link_aggregation.py +0 -0
  56. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/mac_table.py +0 -0
  57. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/firewall.py +0 -0
  58. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/impairment.py +0 -0
  59. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/l2_common.py +0 -0
  60. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/multicast.py +0 -0
  61. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/networking.py +0 -0
  62. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/packets.py +0 -0
  63. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/qoe.py +0 -0
  64. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/radius.py +0 -0
  65. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/sdwan_appliance.py +0 -0
  66. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/switch.py +0 -0
  67. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/switch_routing.py +0 -0
  68. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/tr069.py +0 -0
  69. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/traffic.py +0 -0
  70. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/wan_edge.py +0 -0
  71. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/models/wifi.py +0 -0
  72. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/multicast_client.py +0 -0
  73. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/nat.py +0 -0
  74. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/netem_controller.py +0 -0
  75. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/network_endpoint.py +0 -0
  76. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/network_probe.py +0 -0
  77. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/nmap_scanner.py +0 -0
  78. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/ntp_client.py +0 -0
  79. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/ntp_config.py +0 -0
  80. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/ospf.py +0 -0
  81. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/packet_filter.py +0 -0
  82. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/pcap_capture.py +0 -0
  83. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/pdu_controller.py +0 -0
  84. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/port_poe.py +0 -0
  85. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/port_security.py +0 -0
  86. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/port_status.py +0 -0
  87. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/py.typed +0 -0
  88. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/qoe_browser.py +0 -0
  89. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/radius_client.py +0 -0
  90. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/radius_server.py +0 -0
  91. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/reachability_responder.py +0 -0
  92. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/routed_interfaces.py +0 -0
  93. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/router.py +0 -0
  94. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/routing_read.py +0 -0
  95. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/sdwan_policy_manager.py +0 -0
  96. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/sip_phone.py +0 -0
  97. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/sip_server.py +0 -0
  98. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/site_to_site_vpn.py +0 -0
  99. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/snmp_client.py +0 -0
  100. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/spanning_tree.py +0 -0
  101. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/static_routes.py +0 -0
  102. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/storm_control.py +0 -0
  103. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/streaming_server.py +0 -0
  104. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/switch_acl.py +0 -0
  105. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/switch_ports.py +0 -0
  106. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/switch_qos.py +0 -0
  107. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/switch_vlans.py +0 -0
  108. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/syslog_config.py +0 -0
  109. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/tftp_server.py +0 -0
  110. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/threat_prevention.py +0 -0
  111. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/tr069_client.py +0 -0
  112. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/tr069_server.py +0 -0
  113. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/traffic_shaping.py +0 -0
  114. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/upnp_client.py +0 -0
  115. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/vlan_client.py +0 -0
  116. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wan_link_admin.py +0 -0
  117. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_bss.py +0 -0
  118. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_client.py +0 -0
  119. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_mesh.py +0 -0
  120. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_onboarding.py +0 -0
  121. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_radio.py +0 -0
  122. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_rf.py +0 -0
  123. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_stations.py +0 -0
  124. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols/wifi_transitions.py +0 -0
  125. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols.egg-info/SOURCES.txt +0 -0
  126. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols.egg-info/dependency_links.txt +0 -0
  127. {testprotocols-0.5.0 → testprotocols-0.5.2}/src/testprotocols.egg-info/top_level.txt +0 -0
  128. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_appliance_vlans.py +0 -0
  129. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_cpe_templates.py +0 -0
  130. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_device_types.py +0 -0
  131. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_firewall_templates.py +0 -0
  132. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_infra_templates.py +0 -0
  133. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_ip_routing.py +0 -0
  134. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_network_tool_templates.py +0 -0
  135. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_package_imports.py +0 -0
  136. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_radius_templates.py +0 -0
  137. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_sdwan_appliance_models.py +0 -0
  138. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_sdwan_appliance_templates.py +0 -0
  139. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_switch_models.py +0 -0
  140. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_switch_routing_models.py +0 -0
  141. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_switch_templates.py +0 -0
  142. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_traffic_templates.py +0 -0
  143. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_voice_wifi_templates.py +0 -0
  144. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_wan_edge_templates.py +0 -0
  145. {testprotocols-0.5.0 → testprotocols-0.5.2}/tests/test_wifi_templates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testprotocols
3
- Version: 0.5.0
3
+ Version: 0.5.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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "testprotocols"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "Common Test Resource Layer — capability and device protocols for telco testing."
5
5
  requires-python = ">=3.12"
6
6
  dependencies = []
@@ -134,6 +134,14 @@ class QoeMeasurementClientDevice(QoeClientDevice, Protocol):
134
134
  driver resolves from its homing config — read it here rather than reaching into
135
135
  a framework-specific config object."""
136
136
 
137
+ attachment_segment: str
138
+ """The declared segment LABEL of a guest-attached client (a client whose
139
+ test leg sits on a segment the testbed does not manage), empty for managed
140
+ homing or when undeclared. A stable role name (e.g. ``"provider-vpn"``),
141
+ never an address: it lets tests resolve "the client on segment X" by
142
+ declaration when address facts are foreign-owned or discovered late.
143
+ Vendor-neutral metadata from the driver's attachment config."""
144
+
137
145
 
138
146
  register_device_type("linux_lan_client", LanClientDevice)
139
147
  register_device_type("linux_wlan_client", WlanClientDevice)
@@ -0,0 +1,57 @@
1
+ """DHCP / Client template.
2
+
3
+ Defines the abstract contract for DHCP client operations including lease
4
+ release and renewal for both IPv4 and IPv6, and the scoped lease observation
5
+ that validates a served scope's option contents from the client side.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Sequence
11
+ from typing import Protocol, runtime_checkable
12
+
13
+ from testprotocols.models.dhcp import DhcpLeaseObservation
14
+
15
+
16
+ @runtime_checkable
17
+ class DhcpClient(Protocol):
18
+ """Abstract contract for DHCP client operations."""
19
+
20
+ def release_dhcp(self, interface: str) -> None:
21
+ """Release the DHCPv4 lease on *interface*."""
22
+ ...
23
+
24
+ def renew_dhcp(self, interface: str) -> None:
25
+ """Renew the DHCPv4 lease on *interface*."""
26
+ ...
27
+
28
+ def release_ipv6(self, interface: str, stateless: bool = False) -> None:
29
+ """Release the DHCPv6 lease on *interface*."""
30
+ ...
31
+
32
+ def renew_ipv6(self, interface: str, stateless: bool = False) -> None:
33
+ """Renew the DHCPv6 lease on *interface*."""
34
+ ...
35
+
36
+ def observe_lease(
37
+ self, interface: str, request_options: Sequence[int] = ()
38
+ ) -> DhcpLeaseObservation:
39
+ """Obtain a fresh DHCPv4 lease on *interface* as a scoped observation.
40
+
41
+ Performs a full DHCP exchange that explicitly requests every option
42
+ code in *request_options* (on top of the core lease parameters — a
43
+ DHCP server only answers the options a client asks for), WITHOUT
44
+ altering the interface's configured addressing: the lease exists as
45
+ an observation record, not as interface state. Returns the received
46
+ lease with its raw option values. The caller closes the observation
47
+ with :meth:`release_observed_lease`. Raises if no lease is granted.
48
+ """
49
+ ...
50
+
51
+ def release_observed_lease(self, interface: str) -> None:
52
+ """Release the lease obtained by :meth:`observe_lease` (idempotent).
53
+
54
+ Sends a DHCP release for the observed lease and discards the
55
+ observation record. A no-op when no observed lease is outstanding.
56
+ """
57
+ ...
@@ -37,6 +37,18 @@ class IpInterface(Protocol):
37
37
  """Return the MTU size of *interface*."""
38
38
  ...
39
39
 
40
+ def set_interface_mtu_size(self, interface: str, mtu: int) -> None:
41
+ """Set the MTU of *interface* to *mtu* bytes.
42
+
43
+ The write counterpart to :meth:`get_interface_mtu_size`. Lowering the
44
+ MTU also lowers the MSS the host advertises on that interface, so it
45
+ governs TCP segment size in *both* directions of a flow — unlike a
46
+ per-socket ``TCP_MAXSEG``, which caps only the local send. Stateless
47
+ in contract: a caller that needs to revert captures the prior via
48
+ :meth:`get_interface_mtu_size` and restores it itself.
49
+ """
50
+ ...
51
+
40
52
  def is_link_up(self, interface: str, pattern: str = "BROADCAST,MULTICAST,UP") -> bool:
41
53
  """Return True if *interface* flags match *pattern*."""
42
54
  ...
@@ -29,6 +29,7 @@ class IperfClient(Protocol):
29
29
  omit_s: int | None = None,
30
30
  json_output: bool = False,
31
31
  window: str | None = None,
32
+ parallel: int | None = None,
32
33
  ) -> tuple[int, str]:
33
34
  """Start an iperf traffic sender towards *host* on *traffic_port*.
34
35
 
@@ -36,6 +37,9 @@ class IperfClient(Protocol):
36
37
 
37
38
  - ``reverse``: iperf3 reverse mode (``-R``) — the listening receiver
38
39
  transmits; the sender still initiates the connection.
40
+ - ``parallel``: run the session over N parallel streams (``-P <n>``).
41
+ The end-of-test ``sum``/``sum_received`` summaries aggregate across
42
+ the streams, so a multi-stream session reports ONE aggregate rate.
39
43
  - ``omit_s``: skip the first N seconds (``-O <n>``, the TCP slow-start
40
44
  ramp); omitted seconds extend the wall clock and are excluded from
41
45
  the end-of-test summary.
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from testprotocols.models.dhcp import DHCPTraceData, DHCPV6TraceData
5
+ from testprotocols.models.dhcp import DhcpLeaseObservation, DHCPTraceData, DHCPV6TraceData
6
6
  from testprotocols.models.firewall import (
7
7
  Connection,
8
8
  ConntrackStats,
@@ -172,6 +172,7 @@ __all__ = [
172
172
  # tr069
173
173
  "CpeConnectionStatus",
174
174
  # dhcp
175
+ "DhcpLeaseObservation",
175
176
  "DHCPTraceData",
176
177
  "DHCPV6TraceData",
177
178
  "DhcpLease",
@@ -0,0 +1,65 @@
1
+ """DHCP and DHCPv6 packet trace data models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass, field
7
+ from typing import Any
8
+
9
+ from testprotocols.models.networking import IPAddresses
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class DhcpLeaseObservation:
14
+ """A DHCPv4 lease obtained as a scoped observation, with its received options.
15
+
16
+ Produced by :meth:`testprotocols.dhcp_client.DhcpClient.observe_lease` —
17
+ the lease a server granted to a client that explicitly requested a set of
18
+ option codes, read from the client's own lease records (data-plane truth).
19
+
20
+ The typed fields surface the core lease parameters. ``options`` carries the
21
+ raw client-reported value string of every other option present in the
22
+ lease, keyed by option code, exactly as the client recorded it — no
23
+ normalization; value-comparison semantics belong to the caller.
24
+ """
25
+
26
+ address: str
27
+ """The leased IPv4 address (dotted quad)."""
28
+
29
+ subnet_mask: str = ""
30
+ """Received subnet mask, option 1 (dotted quad; empty if absent)."""
31
+
32
+ gateway: str = ""
33
+ """First received router, option 3 (dotted quad; empty if absent)."""
34
+
35
+ dns_servers: tuple[str, ...] = ()
36
+ """Received DNS servers, option 6, in server order."""
37
+
38
+ lease_time_s: int = 0
39
+ """Received lease time in seconds, option 51 (0 if absent)."""
40
+
41
+ server: str = ""
42
+ """The DHCP server identifier, option 54 (dotted quad; empty if absent)."""
43
+
44
+ options: Mapping[int, str] = field(default_factory=dict)
45
+ """Raw received value per option code, as reported by the client."""
46
+
47
+
48
+ @dataclass
49
+ class DHCPTraceData:
50
+ """Holds a captured DHCPv4 packet with source/destination addresses and message type."""
51
+
52
+ source: IPAddresses
53
+ destination: IPAddresses
54
+ dhcp_packet: dict[str, Any]
55
+ dhcp_message_type: int
56
+
57
+
58
+ @dataclass
59
+ class DHCPV6TraceData:
60
+ """Holds a captured DHCPv6 packet with source/destination addresses and message type."""
61
+
62
+ source: IPAddresses
63
+ destination: IPAddresses
64
+ dhcpv6_packet: dict[str, Any]
65
+ dhcpv6_message_type: int
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testprotocols
3
- Version: 0.5.0
3
+ Version: 0.5.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>
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from testprotocols.dhcp_client import DhcpClient
6
6
  from testprotocols.dhcp_server import DhcpServer
7
+ from testprotocols.models.dhcp import DhcpLeaseObservation
7
8
 
8
9
  # ---------------------------------------------------------------------------
9
10
  # DhcpClient
@@ -16,11 +17,30 @@ def test_dhcp_client_is_runtime_checkable() -> None:
16
17
 
17
18
  def test_dhcp_client_protocol_shape() -> None:
18
19
  """DhcpClient declares the expected method set."""
19
- expected = {"release_dhcp", "renew_dhcp", "release_ipv6", "renew_ipv6"}
20
+ expected = {
21
+ "release_dhcp",
22
+ "renew_dhcp",
23
+ "release_ipv6",
24
+ "renew_ipv6",
25
+ "observe_lease",
26
+ "release_observed_lease",
27
+ }
20
28
  actual = set(DhcpClient.__protocol_attrs__)
21
29
  assert expected <= actual, f"missing: {expected - actual}"
22
30
 
23
31
 
32
+ def test_dhcp_lease_observation_defaults() -> None:
33
+ """Only the address is mandatory; every other field has an empty default."""
34
+ obs = DhcpLeaseObservation(address="192.0.2.10")
35
+ assert obs.address == "192.0.2.10"
36
+ assert obs.subnet_mask == ""
37
+ assert obs.gateway == ""
38
+ assert obs.dns_servers == ()
39
+ assert obs.lease_time_s == 0
40
+ assert obs.server == ""
41
+ assert dict(obs.options) == {}
42
+
43
+
24
44
  # ---------------------------------------------------------------------------
25
45
  # DhcpServer
26
46
  # ---------------------------------------------------------------------------
@@ -18,6 +18,7 @@ def test_ip_interface_protocol_shape() -> None:
18
18
  "get_interface_macaddr",
19
19
  "get_interface_mask",
20
20
  "get_interface_mtu_size",
21
+ "set_interface_mtu_size",
21
22
  "is_link_up",
22
23
  "set_link_state",
23
24
  "enable_ipv6",
@@ -1,30 +0,0 @@
1
- """DHCP / Client template.
2
-
3
- Defines the abstract contract for DHCP client operations including lease
4
- release and renewal for both IPv4 and IPv6.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from typing import Protocol, runtime_checkable
10
-
11
-
12
- @runtime_checkable
13
- class DhcpClient(Protocol):
14
- """Abstract contract for DHCP client operations."""
15
-
16
- def release_dhcp(self, interface: str) -> None:
17
- """Release the DHCPv4 lease on *interface*."""
18
- ...
19
-
20
- def renew_dhcp(self, interface: str) -> None:
21
- """Renew the DHCPv4 lease on *interface*."""
22
- ...
23
-
24
- def release_ipv6(self, interface: str, stateless: bool = False) -> None:
25
- """Release the DHCPv6 lease on *interface*."""
26
- ...
27
-
28
- def renew_ipv6(self, interface: str, stateless: bool = False) -> None:
29
- """Renew the DHCPv6 lease on *interface*."""
30
- ...
@@ -1,28 +0,0 @@
1
- """DHCP and DHCPv6 packet trace data models."""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import dataclass
6
- from typing import Any
7
-
8
- from testprotocols.models.networking import IPAddresses
9
-
10
-
11
- @dataclass
12
- class DHCPTraceData:
13
- """Holds a captured DHCPv4 packet with source/destination addresses and message type."""
14
-
15
- source: IPAddresses
16
- destination: IPAddresses
17
- dhcp_packet: dict[str, Any]
18
- dhcp_message_type: int
19
-
20
-
21
- @dataclass
22
- class DHCPV6TraceData:
23
- """Holds a captured DHCPv6 packet with source/destination addresses and message type."""
24
-
25
- source: IPAddresses
26
- destination: IPAddresses
27
- dhcpv6_packet: dict[str, Any]
28
- dhcpv6_message_type: int
File without changes
File without changes
File without changes
File without changes