iflow-mcp_enuno-unifi-mcp-server 0.2.1__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.
Files changed (81) hide show
  1. iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/METADATA +1282 -0
  2. iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/RECORD +81 -0
  3. iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/WHEEL +4 -0
  4. iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/licenses/LICENSE +201 -0
  6. src/__init__.py +3 -0
  7. src/__main__.py +6 -0
  8. src/api/__init__.py +5 -0
  9. src/api/client.py +727 -0
  10. src/api/site_manager_client.py +176 -0
  11. src/cache.py +483 -0
  12. src/config/__init__.py +5 -0
  13. src/config/config.py +321 -0
  14. src/main.py +2234 -0
  15. src/models/__init__.py +126 -0
  16. src/models/acl.py +41 -0
  17. src/models/backup.py +272 -0
  18. src/models/client.py +74 -0
  19. src/models/device.py +53 -0
  20. src/models/dpi.py +50 -0
  21. src/models/firewall_policy.py +123 -0
  22. src/models/firewall_zone.py +28 -0
  23. src/models/network.py +62 -0
  24. src/models/qos_profile.py +458 -0
  25. src/models/radius.py +141 -0
  26. src/models/reference_data.py +34 -0
  27. src/models/site.py +59 -0
  28. src/models/site_manager.py +120 -0
  29. src/models/topology.py +138 -0
  30. src/models/traffic_flow.py +137 -0
  31. src/models/traffic_matching_list.py +56 -0
  32. src/models/voucher.py +42 -0
  33. src/models/vpn.py +73 -0
  34. src/models/wan.py +48 -0
  35. src/models/zbf_matrix.py +49 -0
  36. src/resources/__init__.py +8 -0
  37. src/resources/clients.py +111 -0
  38. src/resources/devices.py +102 -0
  39. src/resources/networks.py +93 -0
  40. src/resources/site_manager.py +64 -0
  41. src/resources/sites.py +86 -0
  42. src/tools/__init__.py +25 -0
  43. src/tools/acls.py +328 -0
  44. src/tools/application.py +42 -0
  45. src/tools/backups.py +1173 -0
  46. src/tools/client_management.py +505 -0
  47. src/tools/clients.py +203 -0
  48. src/tools/device_control.py +325 -0
  49. src/tools/devices.py +354 -0
  50. src/tools/dpi.py +241 -0
  51. src/tools/dpi_tools.py +89 -0
  52. src/tools/firewall.py +417 -0
  53. src/tools/firewall_policies.py +430 -0
  54. src/tools/firewall_zones.py +515 -0
  55. src/tools/network_config.py +388 -0
  56. src/tools/networks.py +190 -0
  57. src/tools/port_forwarding.py +263 -0
  58. src/tools/qos.py +1070 -0
  59. src/tools/radius.py +763 -0
  60. src/tools/reference_data.py +107 -0
  61. src/tools/site_manager.py +466 -0
  62. src/tools/site_vpn.py +95 -0
  63. src/tools/sites.py +187 -0
  64. src/tools/topology.py +406 -0
  65. src/tools/traffic_flows.py +1062 -0
  66. src/tools/traffic_matching_lists.py +371 -0
  67. src/tools/vouchers.py +249 -0
  68. src/tools/vpn.py +76 -0
  69. src/tools/wans.py +30 -0
  70. src/tools/wifi.py +498 -0
  71. src/tools/zbf_matrix.py +326 -0
  72. src/utils/__init__.py +88 -0
  73. src/utils/audit.py +213 -0
  74. src/utils/exceptions.py +114 -0
  75. src/utils/helpers.py +159 -0
  76. src/utils/logger.py +105 -0
  77. src/utils/sanitize.py +244 -0
  78. src/utils/validators.py +160 -0
  79. src/webhooks/__init__.py +6 -0
  80. src/webhooks/handlers.py +196 -0
  81. src/webhooks/receiver.py +290 -0
@@ -0,0 +1,123 @@
1
+ from enum import Enum
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class PolicyAction(str, Enum):
7
+ ALLOW = "ALLOW"
8
+ BLOCK = "BLOCK"
9
+
10
+
11
+ class MatchingTarget(str, Enum):
12
+ ANY = "ANY"
13
+ IP = "IP"
14
+ NETWORK = "NETWORK"
15
+ REGION = "REGION"
16
+ CLIENT = "CLIENT"
17
+
18
+
19
+ class ConnectionStateType(str, Enum):
20
+ ALL = "ALL"
21
+ CUSTOM = "CUSTOM"
22
+ RESPOND_ONLY = "RESPOND_ONLY"
23
+
24
+
25
+ class IPVersion(str, Enum):
26
+ BOTH = "BOTH"
27
+ IPV4 = "IPV4"
28
+ IPV6 = "IPV6"
29
+
30
+
31
+ class MatchTarget(BaseModel):
32
+ zone_id: str = Field(..., description="Firewall zone ID")
33
+ matching_target: MatchingTarget = Field(..., description="Target matching type")
34
+ matching_target_type: str | None = Field(None, description="Target type qualifier")
35
+ port_matching_type: str | None = Field(None, description="Port matching type (ANY/SPECIFIC)")
36
+ port: str | None = Field(None, description="Port(s) e.g. '53', '80,443', '1000-2000'")
37
+ match_opposite_ports: bool | None = Field(None, description="Invert port matching")
38
+ ips: list[str] | None = Field(None, description="IP addresses for IP matching")
39
+ match_opposite_ips: bool | None = Field(None, description="Invert IP matching")
40
+ network_ids: list[str] | None = Field(None, description="Network IDs for NETWORK matching")
41
+ match_opposite_networks: bool | None = Field(None, description="Invert network matching")
42
+ regions: list[str] | None = Field(None, description="ISO country codes for REGION matching")
43
+ client_macs: list[str] | None = Field(None, description="MAC addresses for CLIENT matching")
44
+ match_mac: bool | None = Field(None, description="Match by MAC address")
45
+
46
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
47
+
48
+
49
+ class Schedule(BaseModel):
50
+ mode: str = Field(..., description="Schedule mode (ALWAYS/CUSTOM)")
51
+ date_start: str | None = Field(None, description="Start date YYYY-MM-DD")
52
+ date_end: str | None = Field(None, description="End date YYYY-MM-DD")
53
+ time_all_day: bool | None = Field(None, description="All day or specific time")
54
+ time_range_start: str | None = Field(None, description="Start time HH:MM")
55
+ time_range_end: str | None = Field(None, description="End time HH:MM")
56
+ repeat_on_days: list[str] | None = Field(None, description="Days to repeat")
57
+
58
+ model_config = ConfigDict(extra="allow")
59
+
60
+
61
+ class FirewallPolicy(BaseModel):
62
+ id: str = Field(..., alias="_id", description="Unique policy identifier")
63
+ name: str = Field(..., description="Policy name")
64
+ action: PolicyAction = Field(..., description="Policy action (ALLOW/BLOCK)")
65
+ enabled: bool = Field(True, description="Whether policy is active")
66
+ predefined: bool = Field(False, description="Whether this is a system rule")
67
+ index: int = Field(10000, description="Priority order (lower = higher priority)")
68
+ protocol: str = Field("all", description="Protocol (all/tcp/udp/tcp_udp/icmpv6)")
69
+ ip_version: IPVersion = Field(IPVersion.BOTH, description="IP version filter")
70
+ connection_state_type: ConnectionStateType = Field(
71
+ ConnectionStateType.ALL, description="Connection state matching type"
72
+ )
73
+ connection_states: list[str] | None = Field(
74
+ None, description="Connection states when type is CUSTOM"
75
+ )
76
+ create_allow_respond: bool | None = Field(None, description="Auto-allow response traffic")
77
+ logging: bool | None = Field(None, description="Enable rule logging")
78
+ match_ip_sec: bool | None = Field(None, description="Match IPsec traffic")
79
+ match_opposite_protocol: bool | None = Field(None, description="Match opposite protocol")
80
+ icmp_typename: str | None = Field(None, description="ICMP type name")
81
+ icmp_v6_typename: str | None = Field(None, description="ICMPv6 type name")
82
+ description: str | None = Field(None, description="Policy description")
83
+ origin_id: str | None = Field(None, description="Related origin object ID")
84
+ origin_type: str | None = Field(None, description="Origin type (e.g. port_forward)")
85
+ source: MatchTarget = Field(..., description="Source matching criteria")
86
+ destination: MatchTarget = Field(..., description="Destination matching criteria")
87
+ schedule: Schedule | None = Field(None, description="Time-based scheduling")
88
+
89
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
90
+
91
+
92
+ class FirewallPolicyCreate(BaseModel):
93
+ name: str = Field(..., description="Policy name")
94
+ action: str = Field(..., description="Policy action (ALLOW/BLOCK)")
95
+ enabled: bool = Field(True, description="Whether policy is active")
96
+ protocol: str = Field("all", description="Protocol")
97
+ ip_version: str = Field("BOTH", description="IP version filter")
98
+ connection_state_type: str = Field("ALL", description="Connection state type")
99
+ connection_states: list[str] | None = Field(None, description="Connection states")
100
+ source: dict = Field(..., description="Source matching criteria")
101
+ destination: dict = Field(..., description="Destination matching criteria")
102
+ description: str | None = Field(None, description="Policy description")
103
+ index: int | None = Field(None, description="Priority order")
104
+ schedule: dict | None = Field(None, description="Time-based scheduling")
105
+
106
+ model_config = ConfigDict(extra="allow")
107
+
108
+
109
+ class FirewallPolicyUpdate(BaseModel):
110
+ name: str | None = Field(None, description="Policy name")
111
+ action: str | None = Field(None, description="Policy action")
112
+ enabled: bool | None = Field(None, description="Whether policy is active")
113
+ protocol: str | None = Field(None, description="Protocol")
114
+ ip_version: str | None = Field(None, description="IP version filter")
115
+ connection_state_type: str | None = Field(None, description="Connection state type")
116
+ connection_states: list[str] | None = Field(None, description="Connection states")
117
+ source: dict | None = Field(None, description="Source matching criteria")
118
+ destination: dict | None = Field(None, description="Destination matching criteria")
119
+ description: str | None = Field(None, description="Policy description")
120
+ index: int | None = Field(None, description="Priority order")
121
+ schedule: dict | None = Field(None, description="Time-based scheduling")
122
+
123
+ model_config = ConfigDict(extra="allow")
@@ -0,0 +1,28 @@
1
+ """Firewall zone models."""
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class FirewallZone(BaseModel):
7
+ """Firewall zone model."""
8
+
9
+ id: str = Field(..., alias="_id", description="Firewall zone identifier")
10
+ site_id: str = Field(..., description="Site identifier")
11
+ name: str = Field(..., description="Zone name")
12
+ description: str | None = Field(None, description="Zone description")
13
+
14
+ # Network assignments
15
+ network_ids: list[str] = Field(
16
+ default_factory=list, alias="networks", description="Network IDs assigned to this zone"
17
+ )
18
+
19
+ # Zone type
20
+ zone_type: str | None = Field(None, description="Zone type (lan/wan/guest/custom)")
21
+
22
+ # Policy configuration
23
+ default_policy: str | None = Field(None, description="Default policy (allow/deny)")
24
+
25
+ # Metadata
26
+ is_predefined: bool = Field(False, description="Whether this is a system-defined zone")
27
+
28
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
src/models/network.py ADDED
@@ -0,0 +1,62 @@
1
+ """Network data model."""
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class Network(BaseModel):
7
+ """UniFi network configuration."""
8
+
9
+ id: str = Field(..., description="Network ID", alias="_id")
10
+ name: str = Field(..., description="Network name")
11
+ purpose: str = Field(..., description="Network purpose (corporate, guest, etc.)")
12
+
13
+ # Network configuration
14
+ vlan: int | None = Field(None, description="VLAN ID", alias="vlan_enabled")
15
+ vlan_id: int | None = Field(None, description="VLAN number")
16
+ enabled: bool | None = Field(None, description="Whether network is enabled")
17
+
18
+ # IP configuration
19
+ ip_subnet: str | None = Field(None, description="IP subnet (CIDR notation)")
20
+ networkgroup: str | None = Field(None, description="Network group")
21
+ domain_name: str | None = Field(None, description="Domain name")
22
+
23
+ # DHCP settings
24
+ dhcpd_enabled: bool | None = Field(None, description="Whether DHCP is enabled")
25
+ dhcpd_start: str | None = Field(None, description="DHCP range start")
26
+ dhcpd_stop: str | None = Field(None, description="DHCP range end")
27
+ dhcpd_leasetime: int | None = Field(None, description="DHCP lease time in seconds")
28
+ dhcpd_dns_enabled: bool | None = Field(None, description="Whether DHCP DNS is enabled")
29
+ dhcpd_dns_1: str | None = Field(None, description="Primary DNS server")
30
+ dhcpd_dns_2: str | None = Field(None, description="Secondary DNS server")
31
+ dhcpd_dns_3: str | None = Field(None, description="Tertiary DNS server")
32
+ dhcpd_dns_4: str | None = Field(None, description="Quaternary DNS server")
33
+ dhcpd_gateway_enabled: bool | None = Field(None, description="Whether DHCP gateway is enabled")
34
+ dhcpd_gateway: str | None = Field(None, description="DHCP gateway IP")
35
+
36
+ # IGMP settings
37
+ igmp_snooping: bool | None = Field(None, description="IGMP snooping enabled")
38
+
39
+ # IPv6
40
+ ipv6_interface_type: str | None = Field(None, description="IPv6 interface type")
41
+ ipv6_pd_start: str | None = Field(None, description="IPv6 prefix delegation start")
42
+ ipv6_pd_stop: str | None = Field(None, description="IPv6 prefix delegation stop")
43
+
44
+ # Site association
45
+ site_id: str | None = Field(None, description="Site ID")
46
+
47
+ model_config = ConfigDict(
48
+ populate_by_name=True,
49
+ json_schema_extra={
50
+ "example": {
51
+ "_id": "507f191e810c19729de860ea",
52
+ "name": "LAN",
53
+ "purpose": "corporate",
54
+ "vlan_enabled": True,
55
+ "vlan_id": 1,
56
+ "ip_subnet": "192.168.1.0/24",
57
+ "dhcpd_enabled": True,
58
+ "dhcpd_start": "192.168.1.100",
59
+ "dhcpd_stop": "192.168.1.254",
60
+ }
61
+ },
62
+ )
@@ -0,0 +1,458 @@
1
+ """QoS (Quality of Service) Profile data models."""
2
+
3
+ from enum import Enum
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class QoSPriority(str, Enum):
10
+ """QoS priority levels (0-7, where 7 is highest)."""
11
+
12
+ BACKGROUND = "0" # Best Effort, low priority
13
+ BEST_EFFORT = "1" # Standard traffic
14
+ EXCELLENT_EFFORT = "2" # Better than best effort
15
+ CRITICAL_APPLICATIONS = "3" # Business-critical apps
16
+ VIDEO = "4" # Streaming video (<100ms latency)
17
+ VOICE = "5" # VoIP (<10ms latency)
18
+ INTERNETWORK_CONTROL = "6" # Network control traffic
19
+ NETWORK_CONTROL = "7" # Routing protocols, highest priority
20
+
21
+
22
+ class DSCPValue(str, Enum):
23
+ """Common DSCP (Differentiated Services Code Point) values."""
24
+
25
+ # Default
26
+ DEFAULT = "0" # Best Effort (BE)
27
+ CS0 = "0" # Class Selector 0
28
+
29
+ # Class Selectors
30
+ CS1 = "8" # Class Selector 1
31
+ CS2 = "16" # Class Selector 2
32
+ CS3 = "24" # Class Selector 3
33
+ CS4 = "32" # Class Selector 4
34
+ CS5 = "40" # Class Selector 5
35
+ CS6 = "48" # Class Selector 6
36
+ CS7 = "56" # Class Selector 7
37
+
38
+ # Assured Forwarding (AFxy)
39
+ AF11 = "10" # AF Class 1, Low Drop
40
+ AF12 = "12" # AF Class 1, Medium Drop
41
+ AF13 = "14" # AF Class 1, High Drop
42
+ AF21 = "18" # AF Class 2, Low Drop
43
+ AF22 = "20" # AF Class 2, Medium Drop
44
+ AF23 = "22" # AF Class 2, High Drop
45
+ AF31 = "26" # AF Class 3, Low Drop
46
+ AF32 = "28" # AF Class 3, Medium Drop
47
+ AF33 = "30" # AF Class 3, High Drop
48
+ AF41 = "34" # AF Class 4, Low Drop
49
+ AF42 = "36" # AF Class 4, Medium Drop
50
+ AF43 = "38" # AF Class 4, High Drop
51
+
52
+ # Expedited Forwarding
53
+ EF = "46" # Expedited Forwarding (Voice)
54
+
55
+ # Voice Admit
56
+ VOICE_ADMIT = "44" # Voice-Admit
57
+
58
+
59
+ class QoSAction(str, Enum):
60
+ """QoS action types."""
61
+
62
+ PRIORITIZE = "prioritize" # Set priority/DSCP
63
+ LIMIT = "limit" # Apply bandwidth limit
64
+ BLOCK = "block" # Block traffic
65
+ MARK = "mark" # Mark with DSCP value
66
+ SHAPE = "shape" # Shape traffic to rate
67
+
68
+
69
+ class RouteAction(str, Enum):
70
+ """Traffic route action types."""
71
+
72
+ ALLOW = "allow" # Allow traffic
73
+ DENY = "deny" # Deny traffic
74
+ MARK = "mark" # Mark with DSCP
75
+ SHAPE = "shape" # Shape to rate
76
+
77
+
78
+ class QueueAlgorithm(str, Enum):
79
+ """Smart queue management algorithms."""
80
+
81
+ FQ_CODEL = "fq_codel" # Fair Queueing with Controlled Delay
82
+ CAKE = "cake" # Common Applications Kept Enhanced
83
+
84
+
85
+ class ProAVProtocol(str, Enum):
86
+ """Professional Audio/Video protocols."""
87
+
88
+ DANTE = "dante" # Audinate Dante
89
+ Q_SYS = "q-sys" # QSC Q-SYS
90
+ SDVOE = "sdvoe" # SDVoE (Software Defined Video over Ethernet)
91
+ AVB = "avb" # Audio Video Bridging
92
+ RAVENNA = "ravenna" # AES67/RAVENNA
93
+ NDI = "ndi" # NewTek NDI
94
+ SMPTE_2110 = "smpte-2110" # SMPTE ST 2110
95
+
96
+
97
+ class QoSProfile(BaseModel):
98
+ """QoS Profile for traffic prioritization and shaping."""
99
+
100
+ id: str = Field(alias="_id", description="Profile ID")
101
+ name: str = Field(..., description="Profile name")
102
+ priority_level: int = Field(..., ge=0, le=7, description="Priority level (0-7, 7=highest)")
103
+ description: str | None = Field(None, description="Profile description")
104
+
105
+ # Traffic matching
106
+ applications: list[str] = Field(default_factory=list, description="Application IDs to match")
107
+ categories: list[str] = Field(default_factory=list, description="Category IDs to match")
108
+ ports: list[int] = Field(default_factory=list, description="Port numbers to match")
109
+ protocols: list[str] = Field(default_factory=list, description="Protocols (tcp, udp, icmp)")
110
+
111
+ # DSCP marking
112
+ dscp_marking: int | None = Field(None, ge=0, le=63, description="DSCP value to mark packets")
113
+ preserve_dscp: bool = Field(False, description="Preserve existing DSCP markings")
114
+
115
+ # Bandwidth control
116
+ bandwidth_limit_down_kbps: int | None = Field(
117
+ None, ge=0, description="Download bandwidth limit in kbps"
118
+ )
119
+ bandwidth_limit_up_kbps: int | None = Field(
120
+ None, ge=0, description="Upload bandwidth limit in kbps"
121
+ )
122
+ bandwidth_guaranteed_down_kbps: int | None = Field(
123
+ None, ge=0, description="Guaranteed download bandwidth in kbps"
124
+ )
125
+ bandwidth_guaranteed_up_kbps: int | None = Field(
126
+ None, ge=0, description="Guaranteed upload bandwidth in kbps"
127
+ )
128
+
129
+ # Scheduling
130
+ schedule_enabled: bool = Field(False, description="Enable time-based schedule")
131
+ schedule_days: list[str] = Field(
132
+ default_factory=list, description="Days active (mon, tue, wed, thu, fri, sat, sun)"
133
+ )
134
+ schedule_time_start: str | None = Field(None, description="Start time (HH:MM format)")
135
+ schedule_time_end: str | None = Field(None, description="End time (HH:MM format)")
136
+
137
+ # ProAV specific
138
+ proav_protocol: str | None = Field(None, description="ProAV protocol type")
139
+ proav_multicast_enabled: bool = Field(False, description="Enable multicast support")
140
+ proav_ptp_enabled: bool = Field(False, description="Enable PTP (Precision Time Protocol)")
141
+
142
+ # State
143
+ enabled: bool = Field(True, description="Profile enabled")
144
+ site_id: str | None = Field(None, description="Site ID")
145
+
146
+ class Config:
147
+ """Pydantic configuration."""
148
+
149
+ populate_by_name = True
150
+ use_enum_values = True
151
+
152
+
153
+ class ProAVTemplate(BaseModel):
154
+ """Pre-configured template for ProAV protocols."""
155
+
156
+ name: str = Field(..., description="Template name")
157
+ protocol: ProAVProtocol = Field(..., description="ProAV protocol")
158
+ description: str = Field(..., description="Template description")
159
+
160
+ # Recommended QoS settings
161
+ priority_level: int = Field(..., ge=0, le=7)
162
+ dscp_marking: int = Field(..., ge=0, le=63)
163
+
164
+ # Protocol-specific ports
165
+ udp_ports: list[int] = Field(default_factory=list)
166
+ tcp_ports: list[int] = Field(default_factory=list)
167
+
168
+ # Multicast configuration
169
+ multicast_enabled: bool = Field(False)
170
+ multicast_range: str | None = Field(None, description="Multicast IP range")
171
+
172
+ # PTP configuration
173
+ ptp_enabled: bool = Field(False)
174
+ ptp_domain: int | None = Field(None, ge=0, le=255)
175
+
176
+ # Bandwidth requirements
177
+ min_bandwidth_mbps: int | None = Field(None, description="Minimum bandwidth in Mbps")
178
+ max_latency_ms: int | None = Field(None, description="Maximum latency in milliseconds")
179
+
180
+ class Config:
181
+ """Pydantic configuration."""
182
+
183
+ use_enum_values = True
184
+
185
+
186
+ class MatchCriteria(BaseModel):
187
+ """Traffic matching criteria for routing policies."""
188
+
189
+ source_ip: str | None = Field(None, description="Source IP address or CIDR")
190
+ destination_ip: str | None = Field(None, description="Destination IP address or CIDR")
191
+ source_port: int | None = Field(None, ge=1, le=65535, description="Source port")
192
+ destination_port: int | None = Field(None, ge=1, le=65535, description="Destination port")
193
+ protocol: str | None = Field(None, description="Protocol (tcp, udp, icmp, all)")
194
+ vlan_id: int | None = Field(None, ge=1, le=4094, description="VLAN ID")
195
+
196
+ class Config:
197
+ """Pydantic configuration."""
198
+
199
+ use_enum_values = True
200
+
201
+
202
+ class RouteSchedule(BaseModel):
203
+ """Time-based routing schedule."""
204
+
205
+ enabled: bool = Field(False, description="Enable time-based schedule")
206
+ days: list[str] = Field(
207
+ default_factory=list, description="Days active (mon, tue, wed, thu, fri, sat, sun)"
208
+ )
209
+ start_time: str | None = Field(None, description="Start time (HH:MM format)")
210
+ end_time: str | None = Field(None, description="End time (HH:MM format)")
211
+
212
+ class Config:
213
+ """Pydantic configuration."""
214
+
215
+ use_enum_values = True
216
+
217
+
218
+ class TrafficRoute(BaseModel):
219
+ """Policy-based traffic routing configuration."""
220
+
221
+ id: str = Field(alias="_id", description="Route ID")
222
+ name: str = Field(..., description="Route name")
223
+ description: str | None = Field(None, description="Route description")
224
+ action: RouteAction = Field(..., description="Route action")
225
+ enabled: bool = Field(True, description="Route enabled")
226
+
227
+ # Traffic matching
228
+ match_criteria: MatchCriteria = Field(..., description="Traffic matching criteria")
229
+
230
+ # QoS settings
231
+ dscp_marking: int | None = Field(None, ge=0, le=63, description="DSCP value to mark")
232
+ bandwidth_limit_kbps: int | None = Field(None, ge=0, description="Bandwidth limit in kbps")
233
+
234
+ # Scheduling
235
+ schedule: RouteSchedule | None = Field(None, description="Time-based schedule")
236
+
237
+ # Priority
238
+ priority: int = Field(
239
+ default=100, ge=1, le=1000, description="Route priority (lower = higher priority)"
240
+ )
241
+
242
+ # State
243
+ site_id: str | None = Field(None, description="Site ID")
244
+
245
+ class Config:
246
+ """Pydantic configuration."""
247
+
248
+ populate_by_name = True
249
+ use_enum_values = True
250
+
251
+
252
+ class SmartQueueConfig(BaseModel):
253
+ """Smart Queue Management (SQM) configuration for bufferbloat mitigation."""
254
+
255
+ id: str = Field(alias="_id", description="Config ID")
256
+ enabled: bool = Field(False, description="Enable SQM")
257
+ wan_id: str = Field(..., description="WAN interface ID")
258
+
259
+ # Queue algorithm
260
+ algorithm: QueueAlgorithm = Field(
261
+ default=QueueAlgorithm.FQ_CODEL, description="Queue algorithm"
262
+ )
263
+
264
+ # Bandwidth limits
265
+ download_kbps: int = Field(..., ge=0, description="Download bandwidth in kbps")
266
+ upload_kbps: int = Field(..., ge=0, description="Upload bandwidth in kbps")
267
+
268
+ # Advanced settings
269
+ overhead_bytes: int = Field(
270
+ default=44, ge=0, le=256, description="Per-packet overhead in bytes"
271
+ )
272
+ target_delay_ms: int = Field(default=5, ge=1, le=100, description="Target queueing delay in ms")
273
+
274
+ # Bufferbloat test metadata
275
+ last_test_date: str | None = Field(None, description="Last bufferbloat test date (ISO 8601)")
276
+ last_test_grade: str | None = Field(None, description="Last bufferbloat test grade (A-F)")
277
+
278
+ # State
279
+ site_id: str | None = Field(None, description="Site ID")
280
+
281
+ class Config:
282
+ """Pydantic configuration."""
283
+
284
+ populate_by_name = True
285
+ use_enum_values = True
286
+
287
+
288
+ # Pre-defined ProAV templates
289
+ PROAV_TEMPLATES: dict[str, dict[str, Any]] = {
290
+ "dante": {
291
+ "name": "Audinate Dante",
292
+ "protocol": "dante",
293
+ "description": "Professional audio networking over IP with low latency",
294
+ "priority_level": 5, # Voice priority
295
+ "dscp_marking": 46, # EF (Expedited Forwarding)
296
+ "udp_ports": [319, 320, 4440, 4444, 4455, 8700, 8800, 14336, 14337],
297
+ "tcp_ports": [8019, 8700],
298
+ "multicast_enabled": True,
299
+ "multicast_range": "239.255.0.0/16",
300
+ "ptp_enabled": True,
301
+ "ptp_domain": 0,
302
+ "min_bandwidth_mbps": 10,
303
+ "max_latency_ms": 1,
304
+ },
305
+ "q-sys": {
306
+ "name": "QSC Q-SYS",
307
+ "protocol": "q-sys",
308
+ "description": "QSC ecosystem for audio, video, and control",
309
+ "priority_level": 5,
310
+ "dscp_marking": 46,
311
+ "udp_ports": [1700, 1701, 1702, 1703, 1704, 1705],
312
+ "tcp_ports": [1700, 1701, 1710],
313
+ "multicast_enabled": True,
314
+ "multicast_range": "239.255.254.0/24",
315
+ "ptp_enabled": False,
316
+ "min_bandwidth_mbps": 100,
317
+ "max_latency_ms": 2,
318
+ },
319
+ "sdvoe": {
320
+ "name": "SDVoE Alliance",
321
+ "protocol": "sdvoe",
322
+ "description": "Software Defined Video over Ethernet for AV distribution",
323
+ "priority_level": 4, # Video priority
324
+ "dscp_marking": 34, # AF41
325
+ "udp_ports": [5353], # mDNS
326
+ "tcp_ports": [],
327
+ "multicast_enabled": True,
328
+ "multicast_range": "239.0.0.0/8",
329
+ "ptp_enabled": True,
330
+ "ptp_domain": 127,
331
+ "min_bandwidth_mbps": 1000, # 1 Gbps minimum for 4K
332
+ "max_latency_ms": 100,
333
+ },
334
+ "avb": {
335
+ "name": "Audio Video Bridging (IEEE 802.1)",
336
+ "protocol": "avb",
337
+ "description": "IEEE 802.1 standard for time-sensitive networking",
338
+ "priority_level": 5,
339
+ "dscp_marking": 46,
340
+ "udp_ports": [],
341
+ "tcp_ports": [],
342
+ "multicast_enabled": True,
343
+ "multicast_range": "224.0.1.129/32",
344
+ "ptp_enabled": True,
345
+ "ptp_domain": 0,
346
+ "min_bandwidth_mbps": 75,
347
+ "max_latency_ms": 2,
348
+ },
349
+ "ravenna": {
350
+ "name": "AES67/RAVENNA",
351
+ "protocol": "ravenna",
352
+ "description": "AES67 and RAVENNA for professional IP audio",
353
+ "priority_level": 5,
354
+ "dscp_marking": 46,
355
+ "udp_ports": [5353, 5004, 5005], # mDNS, RTP, RTCP
356
+ "tcp_ports": [554, 8080], # RTSP, HTTP
357
+ "multicast_enabled": True,
358
+ "multicast_range": "239.69.0.0/16",
359
+ "ptp_enabled": True,
360
+ "ptp_domain": 0,
361
+ "min_bandwidth_mbps": 48,
362
+ "max_latency_ms": 1,
363
+ },
364
+ "ndi": {
365
+ "name": "NewTek NDI",
366
+ "protocol": "ndi",
367
+ "description": "Network Device Interface for video production",
368
+ "priority_level": 4,
369
+ "dscp_marking": 34,
370
+ "udp_ports": [5353, 5960, 5961], # mDNS, NDI
371
+ "tcp_ports": [5353, 5960, 5961, 5962, 5963],
372
+ "multicast_enabled": True,
373
+ "multicast_range": "239.255.42.0/24",
374
+ "ptp_enabled": False,
375
+ "min_bandwidth_mbps": 125, # 1080p60
376
+ "max_latency_ms": 33, # One frame at 30fps
377
+ },
378
+ "smpte-2110": {
379
+ "name": "SMPTE ST 2110",
380
+ "protocol": "smpte-2110",
381
+ "description": "Professional media over managed IP networks",
382
+ "priority_level": 4,
383
+ "dscp_marking": 34,
384
+ "udp_ports": [], # Dynamic RTP ports
385
+ "tcp_ports": [],
386
+ "multicast_enabled": True,
387
+ "multicast_range": "239.0.0.0/8",
388
+ "ptp_enabled": True,
389
+ "ptp_domain": 127,
390
+ "min_bandwidth_mbps": 3000, # 4K uncompressed
391
+ "max_latency_ms": 1,
392
+ },
393
+ }
394
+
395
+
396
+ # Reference QoS profiles for common use cases
397
+ REFERENCE_PROFILES: dict[str, dict[str, Any]] = {
398
+ "voice-first": {
399
+ "name": "Voice First",
400
+ "description": "VoIP optimized profile with minimal latency (<10ms)",
401
+ "priority_level": 5, # Voice priority
402
+ "dscp_marking": 46, # EF (Expedited Forwarding)
403
+ "ports": [5060, 5061, 5004, 5005], # SIP, RTP, RTCP
404
+ "protocols": ["udp", "tcp"],
405
+ "bandwidth_guaranteed_down_kbps": 128, # G.711 codec minimum
406
+ "bandwidth_guaranteed_up_kbps": 128,
407
+ },
408
+ "video-conferencing": {
409
+ "name": "Video Conferencing",
410
+ "description": "HD video conferencing optimized (<100ms latency)",
411
+ "priority_level": 4, # Video priority
412
+ "dscp_marking": 34, # AF41
413
+ "ports": [3478, 3479, 8801, 8802], # STUN, TURN, WebRTC
414
+ "protocols": ["udp", "tcp"],
415
+ "bandwidth_guaranteed_down_kbps": 2500, # 1080p @ 30fps
416
+ "bandwidth_guaranteed_up_kbps": 2500,
417
+ },
418
+ "cloud-gaming": {
419
+ "name": "Cloud Gaming",
420
+ "description": "Low-latency gaming with consistent performance",
421
+ "priority_level": 4, # Video/interactive priority
422
+ "dscp_marking": 34, # AF41
423
+ "ports": [], # Application-specific
424
+ "protocols": ["udp"],
425
+ "bandwidth_guaranteed_down_kbps": 15000, # 4K streaming
426
+ "bandwidth_guaranteed_up_kbps": 5000, # Controller input
427
+ },
428
+ "streaming-media": {
429
+ "name": "Streaming Media",
430
+ "description": "Netflix, YouTube, Plex streaming optimization",
431
+ "priority_level": 3, # Critical applications
432
+ "dscp_marking": 26, # AF31
433
+ "ports": [443, 80, 32400], # HTTPS, HTTP, Plex
434
+ "protocols": ["tcp"],
435
+ "bandwidth_guaranteed_down_kbps": 25000, # 4K streaming
436
+ "bandwidth_guaranteed_up_kbps": 5000, # Upload for transcoding
437
+ },
438
+ "bulk-backup": {
439
+ "name": "Bulk Backup",
440
+ "description": "Rate-limited background transfers (Scavenger class)",
441
+ "priority_level": 1, # Background/scavenger
442
+ "dscp_marking": 8, # CS1
443
+ "ports": [22, 873, 3260], # SSH, rsync, iSCSI
444
+ "protocols": ["tcp"],
445
+ "bandwidth_limit_down_kbps": 50000, # Limit to prevent congestion
446
+ "bandwidth_limit_up_kbps": 50000,
447
+ },
448
+ "guest-best-effort": {
449
+ "name": "Guest Best Effort",
450
+ "description": "Minimal guarantees for guest networks",
451
+ "priority_level": 0, # Background/default
452
+ "dscp_marking": 0, # CS0/Best Effort
453
+ "ports": [], # All traffic
454
+ "protocols": ["tcp", "udp"],
455
+ "bandwidth_limit_down_kbps": 10000, # 10 Mbps max
456
+ "bandwidth_limit_up_kbps": 5000, # 5 Mbps max
457
+ },
458
+ }