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.
- iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/METADATA +1282 -0
- iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/RECORD +81 -0
- iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/WHEEL +4 -0
- iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/entry_points.txt +2 -0
- iflow_mcp_enuno_unifi_mcp_server-0.2.1.dist-info/licenses/LICENSE +201 -0
- src/__init__.py +3 -0
- src/__main__.py +6 -0
- src/api/__init__.py +5 -0
- src/api/client.py +727 -0
- src/api/site_manager_client.py +176 -0
- src/cache.py +483 -0
- src/config/__init__.py +5 -0
- src/config/config.py +321 -0
- src/main.py +2234 -0
- src/models/__init__.py +126 -0
- src/models/acl.py +41 -0
- src/models/backup.py +272 -0
- src/models/client.py +74 -0
- src/models/device.py +53 -0
- src/models/dpi.py +50 -0
- src/models/firewall_policy.py +123 -0
- src/models/firewall_zone.py +28 -0
- src/models/network.py +62 -0
- src/models/qos_profile.py +458 -0
- src/models/radius.py +141 -0
- src/models/reference_data.py +34 -0
- src/models/site.py +59 -0
- src/models/site_manager.py +120 -0
- src/models/topology.py +138 -0
- src/models/traffic_flow.py +137 -0
- src/models/traffic_matching_list.py +56 -0
- src/models/voucher.py +42 -0
- src/models/vpn.py +73 -0
- src/models/wan.py +48 -0
- src/models/zbf_matrix.py +49 -0
- src/resources/__init__.py +8 -0
- src/resources/clients.py +111 -0
- src/resources/devices.py +102 -0
- src/resources/networks.py +93 -0
- src/resources/site_manager.py +64 -0
- src/resources/sites.py +86 -0
- src/tools/__init__.py +25 -0
- src/tools/acls.py +328 -0
- src/tools/application.py +42 -0
- src/tools/backups.py +1173 -0
- src/tools/client_management.py +505 -0
- src/tools/clients.py +203 -0
- src/tools/device_control.py +325 -0
- src/tools/devices.py +354 -0
- src/tools/dpi.py +241 -0
- src/tools/dpi_tools.py +89 -0
- src/tools/firewall.py +417 -0
- src/tools/firewall_policies.py +430 -0
- src/tools/firewall_zones.py +515 -0
- src/tools/network_config.py +388 -0
- src/tools/networks.py +190 -0
- src/tools/port_forwarding.py +263 -0
- src/tools/qos.py +1070 -0
- src/tools/radius.py +763 -0
- src/tools/reference_data.py +107 -0
- src/tools/site_manager.py +466 -0
- src/tools/site_vpn.py +95 -0
- src/tools/sites.py +187 -0
- src/tools/topology.py +406 -0
- src/tools/traffic_flows.py +1062 -0
- src/tools/traffic_matching_lists.py +371 -0
- src/tools/vouchers.py +249 -0
- src/tools/vpn.py +76 -0
- src/tools/wans.py +30 -0
- src/tools/wifi.py +498 -0
- src/tools/zbf_matrix.py +326 -0
- src/utils/__init__.py +88 -0
- src/utils/audit.py +213 -0
- src/utils/exceptions.py +114 -0
- src/utils/helpers.py +159 -0
- src/utils/logger.py +105 -0
- src/utils/sanitize.py +244 -0
- src/utils/validators.py +160 -0
- src/webhooks/__init__.py +6 -0
- src/webhooks/handlers.py +196 -0
- 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
|
+
}
|