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
src/models/__init__.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Data models for UniFi MCP Server."""
|
|
2
|
+
|
|
3
|
+
from .acl import ACLRule
|
|
4
|
+
from .backup import (
|
|
5
|
+
BackupMetadata,
|
|
6
|
+
BackupOperation,
|
|
7
|
+
BackupSchedule,
|
|
8
|
+
BackupStatus,
|
|
9
|
+
BackupType,
|
|
10
|
+
BackupValidationResult,
|
|
11
|
+
RestoreOperation,
|
|
12
|
+
RestoreStatus,
|
|
13
|
+
)
|
|
14
|
+
from .client import Client
|
|
15
|
+
from .device import Device
|
|
16
|
+
from .dpi import Country, DPIApplication, DPICategory
|
|
17
|
+
from .firewall_policy import (
|
|
18
|
+
ConnectionStateType,
|
|
19
|
+
FirewallPolicy,
|
|
20
|
+
FirewallPolicyCreate,
|
|
21
|
+
FirewallPolicyUpdate,
|
|
22
|
+
IPVersion,
|
|
23
|
+
MatchingTarget,
|
|
24
|
+
MatchTarget,
|
|
25
|
+
PolicyAction,
|
|
26
|
+
Schedule,
|
|
27
|
+
)
|
|
28
|
+
from .firewall_zone import FirewallZone
|
|
29
|
+
from .network import Network
|
|
30
|
+
from .qos_profile import (
|
|
31
|
+
DSCPValue,
|
|
32
|
+
MatchCriteria,
|
|
33
|
+
ProAVProtocol,
|
|
34
|
+
ProAVTemplate,
|
|
35
|
+
QoSAction,
|
|
36
|
+
QoSPriority,
|
|
37
|
+
QoSProfile,
|
|
38
|
+
QueueAlgorithm,
|
|
39
|
+
RouteAction,
|
|
40
|
+
RouteSchedule,
|
|
41
|
+
SmartQueueConfig,
|
|
42
|
+
TrafficRoute,
|
|
43
|
+
)
|
|
44
|
+
from .radius import RADIUSProfile
|
|
45
|
+
from .reference_data import DeviceTag
|
|
46
|
+
from .site import Site
|
|
47
|
+
from .site_manager import (
|
|
48
|
+
CrossSiteStatistics,
|
|
49
|
+
InternetHealthMetrics,
|
|
50
|
+
SiteHealthSummary,
|
|
51
|
+
VantagePoint,
|
|
52
|
+
)
|
|
53
|
+
from .traffic_flow import FlowRisk, FlowStatistics, FlowView, TrafficFlow
|
|
54
|
+
from .traffic_matching_list import (
|
|
55
|
+
TrafficMatchingList,
|
|
56
|
+
TrafficMatchingListCreate,
|
|
57
|
+
TrafficMatchingListType,
|
|
58
|
+
TrafficMatchingListUpdate,
|
|
59
|
+
)
|
|
60
|
+
from .voucher import Voucher
|
|
61
|
+
from .vpn import VPNServer, VPNTunnel
|
|
62
|
+
from .wan import WANConnection
|
|
63
|
+
from .zbf_matrix import ApplicationBlockRule, ZoneNetworkAssignment, ZonePolicy, ZonePolicyMatrix
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
"Site",
|
|
67
|
+
"Device",
|
|
68
|
+
"Client",
|
|
69
|
+
"Network",
|
|
70
|
+
"ACLRule",
|
|
71
|
+
"Voucher",
|
|
72
|
+
"FirewallZone",
|
|
73
|
+
"FirewallPolicy",
|
|
74
|
+
"FirewallPolicyCreate",
|
|
75
|
+
"FirewallPolicyUpdate",
|
|
76
|
+
"PolicyAction",
|
|
77
|
+
"MatchingTarget",
|
|
78
|
+
"ConnectionStateType",
|
|
79
|
+
"IPVersion",
|
|
80
|
+
"MatchTarget",
|
|
81
|
+
"Schedule",
|
|
82
|
+
"WANConnection",
|
|
83
|
+
"DPICategory",
|
|
84
|
+
"DPIApplication",
|
|
85
|
+
"Country",
|
|
86
|
+
"ZonePolicyMatrix",
|
|
87
|
+
"ZonePolicy",
|
|
88
|
+
"ApplicationBlockRule",
|
|
89
|
+
"ZoneNetworkAssignment",
|
|
90
|
+
"TrafficFlow",
|
|
91
|
+
"FlowStatistics",
|
|
92
|
+
"FlowRisk",
|
|
93
|
+
"FlowView",
|
|
94
|
+
"TrafficMatchingList",
|
|
95
|
+
"TrafficMatchingListCreate",
|
|
96
|
+
"TrafficMatchingListUpdate",
|
|
97
|
+
"TrafficMatchingListType",
|
|
98
|
+
"VPNTunnel",
|
|
99
|
+
"VPNServer",
|
|
100
|
+
"RADIUSProfile",
|
|
101
|
+
"DeviceTag",
|
|
102
|
+
"SiteHealthSummary",
|
|
103
|
+
"InternetHealthMetrics",
|
|
104
|
+
"CrossSiteStatistics",
|
|
105
|
+
"VantagePoint",
|
|
106
|
+
"BackupMetadata",
|
|
107
|
+
"BackupOperation",
|
|
108
|
+
"BackupSchedule",
|
|
109
|
+
"BackupStatus",
|
|
110
|
+
"BackupType",
|
|
111
|
+
"BackupValidationResult",
|
|
112
|
+
"RestoreOperation",
|
|
113
|
+
"RestoreStatus",
|
|
114
|
+
"QoSProfile",
|
|
115
|
+
"QoSPriority",
|
|
116
|
+
"DSCPValue",
|
|
117
|
+
"QoSAction",
|
|
118
|
+
"RouteAction",
|
|
119
|
+
"QueueAlgorithm",
|
|
120
|
+
"ProAVProtocol",
|
|
121
|
+
"ProAVTemplate",
|
|
122
|
+
"MatchCriteria",
|
|
123
|
+
"RouteSchedule",
|
|
124
|
+
"TrafficRoute",
|
|
125
|
+
"SmartQueueConfig",
|
|
126
|
+
]
|
src/models/acl.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Access Control List (ACL) models."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ACLRule(BaseModel):
|
|
7
|
+
"""ACL rule model."""
|
|
8
|
+
|
|
9
|
+
id: str = Field(..., alias="_id", description="ACL rule identifier")
|
|
10
|
+
site_id: str = Field(..., description="Site identifier")
|
|
11
|
+
name: str = Field(..., description="Rule name")
|
|
12
|
+
enabled: bool = Field(True, description="Whether the rule is enabled")
|
|
13
|
+
action: str = Field(..., description="Action to take (allow/deny)")
|
|
14
|
+
|
|
15
|
+
# Source configuration
|
|
16
|
+
source_type: str | None = Field(None, description="Source type (network/device/ip/any)")
|
|
17
|
+
source_id: str | None = Field(None, description="Source identifier")
|
|
18
|
+
source_network: str | None = Field(None, description="Source network CIDR")
|
|
19
|
+
|
|
20
|
+
# Destination configuration
|
|
21
|
+
destination_type: str | None = Field(
|
|
22
|
+
None, description="Destination type (network/ip/port/dpi-category/dpi-app)"
|
|
23
|
+
)
|
|
24
|
+
destination_id: str | None = Field(None, description="Destination identifier")
|
|
25
|
+
destination_network: str | None = Field(None, description="Destination network CIDR")
|
|
26
|
+
|
|
27
|
+
# Protocol and ports
|
|
28
|
+
protocol: str | None = Field(None, description="Protocol (tcp/udp/icmp/all)")
|
|
29
|
+
src_port: int | None = Field(None, description="Source port")
|
|
30
|
+
dst_port: int | None = Field(None, description="Destination port")
|
|
31
|
+
|
|
32
|
+
# Priority and metadata
|
|
33
|
+
priority: int = Field(100, description="Rule priority (lower = higher priority)")
|
|
34
|
+
description: str | None = Field(None, description="Rule description")
|
|
35
|
+
rule_index: int | None = Field(None, description="Rule index in the list")
|
|
36
|
+
|
|
37
|
+
# Statistics
|
|
38
|
+
byte_count: int | None = Field(None, description="Bytes matched by this rule")
|
|
39
|
+
packet_count: int | None = Field(None, description="Packets matched by this rule")
|
|
40
|
+
|
|
41
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
src/models/backup.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""Backup and restore data models."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BackupType(str, Enum):
|
|
11
|
+
"""Backup type enumeration."""
|
|
12
|
+
|
|
13
|
+
SYSTEM = "SYSTEM" # Complete OS, application, and device configurations
|
|
14
|
+
NETWORK = "NETWORK" # Network settings and device configurations only
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BackupStatus(str, Enum):
|
|
18
|
+
"""Backup operation status."""
|
|
19
|
+
|
|
20
|
+
PENDING = "pending"
|
|
21
|
+
IN_PROGRESS = "in_progress"
|
|
22
|
+
COMPLETED = "completed"
|
|
23
|
+
FAILED = "failed"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RestoreStatus(str, Enum):
|
|
27
|
+
"""Restore operation status."""
|
|
28
|
+
|
|
29
|
+
PENDING = "pending"
|
|
30
|
+
IN_PROGRESS = "in_progress"
|
|
31
|
+
COMPLETED = "completed"
|
|
32
|
+
FAILED = "failed"
|
|
33
|
+
ROLLED_BACK = "rolled_back"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class BackupMetadata(BaseModel):
|
|
37
|
+
"""Backup file metadata and information."""
|
|
38
|
+
|
|
39
|
+
backup_id: str = Field(..., description="Unique backup identifier")
|
|
40
|
+
filename: str = Field(..., description="Backup filename (e.g., backup_2025-01-29.unf)")
|
|
41
|
+
backup_type: BackupType = Field(..., description="Type of backup (SYSTEM or NETWORK)")
|
|
42
|
+
created_at: datetime = Field(..., description="Backup creation timestamp")
|
|
43
|
+
size_bytes: int | None = Field(None, description="Backup file size in bytes")
|
|
44
|
+
version: str | None = Field(None, description="UniFi Network version at backup time")
|
|
45
|
+
|
|
46
|
+
# Metadata about backup contents
|
|
47
|
+
device_count: int | None = Field(None, description="Number of devices in backup")
|
|
48
|
+
site_count: int | None = Field(None, description="Number of sites in backup")
|
|
49
|
+
network_count: int | None = Field(None, description="Number of networks in backup")
|
|
50
|
+
|
|
51
|
+
# Cloud backup status
|
|
52
|
+
cloud_synced: bool = Field(False, description="Whether backup is synced to cloud")
|
|
53
|
+
cloud_sync_time: datetime | None = Field(None, description="Last cloud sync timestamp")
|
|
54
|
+
|
|
55
|
+
# Backup location
|
|
56
|
+
download_url: str | None = Field(None, description="Download URL for backup file")
|
|
57
|
+
local_path: str | None = Field(None, description="Local filesystem path (if applicable)")
|
|
58
|
+
|
|
59
|
+
# Validation
|
|
60
|
+
checksum: str | None = Field(None, description="Backup file checksum (MD5 or SHA256)")
|
|
61
|
+
is_valid: bool = Field(True, description="Whether backup passed validation checks")
|
|
62
|
+
validation_message: str | None = Field(None, description="Validation error message if any")
|
|
63
|
+
|
|
64
|
+
model_config = ConfigDict(
|
|
65
|
+
populate_by_name=True,
|
|
66
|
+
json_schema_extra={
|
|
67
|
+
"example": {
|
|
68
|
+
"backup_id": "backup_20250129_123456",
|
|
69
|
+
"filename": "backup_2025-01-29_12-34-56.unf",
|
|
70
|
+
"backup_type": "NETWORK",
|
|
71
|
+
"created_at": "2025-01-29T12:34:56Z",
|
|
72
|
+
"size_bytes": 1048576,
|
|
73
|
+
"version": "10.0.160",
|
|
74
|
+
"device_count": 15,
|
|
75
|
+
"site_count": 1,
|
|
76
|
+
"network_count": 5,
|
|
77
|
+
"cloud_synced": True,
|
|
78
|
+
"is_valid": True,
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class BackupOperation(BaseModel):
|
|
85
|
+
"""Backup operation status and details."""
|
|
86
|
+
|
|
87
|
+
operation_id: str = Field(..., description="Unique operation identifier")
|
|
88
|
+
backup_type: BackupType = Field(..., description="Type of backup being created")
|
|
89
|
+
status: BackupStatus = Field(..., description="Current operation status")
|
|
90
|
+
started_at: datetime = Field(..., description="Operation start time")
|
|
91
|
+
completed_at: datetime | None = Field(None, description="Operation completion time")
|
|
92
|
+
|
|
93
|
+
# Progress tracking
|
|
94
|
+
progress_percent: int = Field(0, ge=0, le=100, description="Progress percentage (0-100)")
|
|
95
|
+
current_step: str | None = Field(None, description="Current operation step description")
|
|
96
|
+
|
|
97
|
+
# Result
|
|
98
|
+
backup_metadata: BackupMetadata | None = Field(None, description="Metadata of created backup")
|
|
99
|
+
error_message: str | None = Field(None, description="Error message if failed")
|
|
100
|
+
|
|
101
|
+
model_config = ConfigDict(
|
|
102
|
+
populate_by_name=True,
|
|
103
|
+
json_schema_extra={
|
|
104
|
+
"example": {
|
|
105
|
+
"operation_id": "op_backup_abc123",
|
|
106
|
+
"backup_type": "NETWORK",
|
|
107
|
+
"status": "completed",
|
|
108
|
+
"started_at": "2025-01-29T12:34:00Z",
|
|
109
|
+
"completed_at": "2025-01-29T12:35:30Z",
|
|
110
|
+
"progress_percent": 100,
|
|
111
|
+
"current_step": "Finalizing backup",
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class RestoreOperation(BaseModel):
|
|
118
|
+
"""Restore operation status and details."""
|
|
119
|
+
|
|
120
|
+
operation_id: str = Field(..., description="Unique operation identifier")
|
|
121
|
+
backup_id: str = Field(..., description="Backup being restored")
|
|
122
|
+
status: RestoreStatus = Field(..., description="Current operation status")
|
|
123
|
+
started_at: datetime = Field(..., description="Operation start time")
|
|
124
|
+
completed_at: datetime | None = Field(None, description="Operation completion time")
|
|
125
|
+
|
|
126
|
+
# Progress tracking
|
|
127
|
+
progress_percent: int = Field(0, ge=0, le=100, description="Progress percentage (0-100)")
|
|
128
|
+
current_step: str | None = Field(None, description="Current operation step description")
|
|
129
|
+
|
|
130
|
+
# Safety features
|
|
131
|
+
pre_restore_backup_id: str | None = Field(
|
|
132
|
+
None,
|
|
133
|
+
description="Backup ID of automatic pre-restore backup (for rollback)",
|
|
134
|
+
)
|
|
135
|
+
can_rollback: bool = Field(False, description="Whether rollback is available")
|
|
136
|
+
|
|
137
|
+
# Result
|
|
138
|
+
error_message: str | None = Field(None, description="Error message if failed")
|
|
139
|
+
rollback_reason: str | None = Field(None, description="Reason for rollback if applicable")
|
|
140
|
+
|
|
141
|
+
model_config = ConfigDict(
|
|
142
|
+
populate_by_name=True,
|
|
143
|
+
json_schema_extra={
|
|
144
|
+
"example": {
|
|
145
|
+
"operation_id": "op_restore_xyz789",
|
|
146
|
+
"backup_id": "backup_20250129_123456",
|
|
147
|
+
"status": "in_progress",
|
|
148
|
+
"started_at": "2025-01-29T14:00:00Z",
|
|
149
|
+
"progress_percent": 45,
|
|
150
|
+
"current_step": "Restoring device configurations",
|
|
151
|
+
"pre_restore_backup_id": "backup_20250129_140000_preRestore",
|
|
152
|
+
"can_rollback": True,
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class BackupSchedule(BaseModel):
|
|
159
|
+
"""Automated backup schedule configuration."""
|
|
160
|
+
|
|
161
|
+
schedule_id: str = Field(..., description="Unique schedule identifier")
|
|
162
|
+
enabled: bool = Field(True, description="Whether schedule is enabled")
|
|
163
|
+
backup_type: BackupType = Field(..., description="Type of backup to create")
|
|
164
|
+
|
|
165
|
+
# Schedule configuration
|
|
166
|
+
frequency: Literal["daily", "weekly", "monthly"] = Field(
|
|
167
|
+
...,
|
|
168
|
+
description="Backup frequency",
|
|
169
|
+
)
|
|
170
|
+
time_of_day: str = Field(
|
|
171
|
+
...,
|
|
172
|
+
description="Time to run backup (HH:MM format, 24-hour)",
|
|
173
|
+
pattern=r"^([01]\d|2[0-3]):([0-5]\d)$",
|
|
174
|
+
)
|
|
175
|
+
day_of_week: int | None = Field(
|
|
176
|
+
None,
|
|
177
|
+
ge=0,
|
|
178
|
+
le=6,
|
|
179
|
+
description="Day of week for weekly backups (0=Monday, 6=Sunday)",
|
|
180
|
+
)
|
|
181
|
+
day_of_month: int | None = Field(
|
|
182
|
+
None,
|
|
183
|
+
ge=1,
|
|
184
|
+
le=31,
|
|
185
|
+
description="Day of month for monthly backups",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Retention policy
|
|
189
|
+
retention_days: int = Field(
|
|
190
|
+
30,
|
|
191
|
+
ge=1,
|
|
192
|
+
le=365,
|
|
193
|
+
description="Number of days to retain backups",
|
|
194
|
+
)
|
|
195
|
+
max_backups: int = Field(
|
|
196
|
+
10,
|
|
197
|
+
ge=1,
|
|
198
|
+
le=100,
|
|
199
|
+
description="Maximum number of backups to keep",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Cloud backup
|
|
203
|
+
cloud_backup_enabled: bool = Field(
|
|
204
|
+
False,
|
|
205
|
+
description="Whether to sync backups to cloud",
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Last run info
|
|
209
|
+
last_run: datetime | None = Field(None, description="Last execution timestamp")
|
|
210
|
+
last_backup_id: str | None = Field(None, description="Last created backup ID")
|
|
211
|
+
next_run: datetime | None = Field(None, description="Next scheduled execution")
|
|
212
|
+
|
|
213
|
+
model_config = ConfigDict(
|
|
214
|
+
populate_by_name=True,
|
|
215
|
+
json_schema_extra={
|
|
216
|
+
"example": {
|
|
217
|
+
"schedule_id": "schedule_daily_network",
|
|
218
|
+
"enabled": True,
|
|
219
|
+
"backup_type": "NETWORK",
|
|
220
|
+
"frequency": "daily",
|
|
221
|
+
"time_of_day": "03:00",
|
|
222
|
+
"retention_days": 30,
|
|
223
|
+
"max_backups": 10,
|
|
224
|
+
"cloud_backup_enabled": True,
|
|
225
|
+
"last_run": "2025-01-29T03:00:00Z",
|
|
226
|
+
"next_run": "2025-01-30T03:00:00Z",
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class BackupValidationResult(BaseModel):
|
|
233
|
+
"""Result of backup file validation."""
|
|
234
|
+
|
|
235
|
+
backup_id: str = Field(..., description="Backup being validated")
|
|
236
|
+
is_valid: bool = Field(..., description="Whether backup is valid")
|
|
237
|
+
|
|
238
|
+
# Validation checks
|
|
239
|
+
checksum_valid: bool = Field(..., description="File integrity check passed")
|
|
240
|
+
format_valid: bool = Field(..., description="File format is correct")
|
|
241
|
+
version_compatible: bool = Field(..., description="Version is compatible with current system")
|
|
242
|
+
|
|
243
|
+
# Validation details
|
|
244
|
+
backup_version: str | None = Field(None, description="UniFi version of backup")
|
|
245
|
+
current_version: str | None = Field(None, description="Current UniFi version")
|
|
246
|
+
warnings: list[str] = Field(default_factory=list, description="Validation warnings")
|
|
247
|
+
errors: list[str] = Field(default_factory=list, description="Validation errors")
|
|
248
|
+
|
|
249
|
+
# Backup contents preview
|
|
250
|
+
contains_devices: int | None = Field(None, description="Number of devices in backup")
|
|
251
|
+
contains_networks: int | None = Field(None, description="Number of networks in backup")
|
|
252
|
+
contains_sites: int | None = Field(None, description="Number of sites in backup")
|
|
253
|
+
|
|
254
|
+
model_config = ConfigDict(
|
|
255
|
+
populate_by_name=True,
|
|
256
|
+
json_schema_extra={
|
|
257
|
+
"example": {
|
|
258
|
+
"backup_id": "backup_20250129_123456",
|
|
259
|
+
"is_valid": True,
|
|
260
|
+
"checksum_valid": True,
|
|
261
|
+
"format_valid": True,
|
|
262
|
+
"version_compatible": True,
|
|
263
|
+
"backup_version": "10.0.160",
|
|
264
|
+
"current_version": "10.0.160",
|
|
265
|
+
"warnings": [],
|
|
266
|
+
"errors": [],
|
|
267
|
+
"contains_devices": 15,
|
|
268
|
+
"contains_networks": 5,
|
|
269
|
+
"contains_sites": 1,
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
)
|
src/models/client.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Client data model."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Client(BaseModel):
|
|
7
|
+
"""UniFi network client information."""
|
|
8
|
+
|
|
9
|
+
mac: str = Field(..., description="Client MAC address")
|
|
10
|
+
ip: str | None = Field(None, description="Client IP address")
|
|
11
|
+
hostname: str | None = Field(None, description="Client hostname")
|
|
12
|
+
name: str | None = Field(None, description="Client name (user-assigned)")
|
|
13
|
+
|
|
14
|
+
# Connection info
|
|
15
|
+
is_wired: bool | None = Field(None, description="Whether client is wired")
|
|
16
|
+
is_guest: bool | None = Field(None, description="Whether client is on guest network")
|
|
17
|
+
essid: str | None = Field(None, description="SSID name (for wireless clients)")
|
|
18
|
+
channel: int | None = Field(None, description="WiFi channel (for wireless clients)")
|
|
19
|
+
radio: str | None = Field(None, description="Radio type (ng, na, etc.)")
|
|
20
|
+
|
|
21
|
+
# Signal strength (wireless only)
|
|
22
|
+
signal: int | None = Field(None, description="Signal strength in dBm")
|
|
23
|
+
rssi: int | None = Field(None, description="RSSI value")
|
|
24
|
+
noise: int | None = Field(None, description="Noise level in dBm")
|
|
25
|
+
|
|
26
|
+
# Network statistics
|
|
27
|
+
tx_bytes: int | None = Field(None, description="Transmitted bytes")
|
|
28
|
+
rx_bytes: int | None = Field(None, description="Received bytes")
|
|
29
|
+
tx_packets: int | None = Field(None, description="Transmitted packets")
|
|
30
|
+
rx_packets: int | None = Field(None, description="Received packets")
|
|
31
|
+
tx_rate: int | None = Field(None, description="Transmission rate in Kbps")
|
|
32
|
+
rx_rate: int | None = Field(None, description="Receiving rate in Kbps")
|
|
33
|
+
|
|
34
|
+
# Session info
|
|
35
|
+
uptime: int | None = Field(None, description="Session uptime in seconds")
|
|
36
|
+
last_seen: int | None = Field(None, description="Last seen timestamp")
|
|
37
|
+
first_seen: int | None = Field(None, description="First seen timestamp")
|
|
38
|
+
|
|
39
|
+
# Device info
|
|
40
|
+
oui: str | None = Field(None, description="MAC OUI manufacturer")
|
|
41
|
+
os_class: int | None = Field(None, description="Operating system class")
|
|
42
|
+
os_name: str | None = Field(None, description="Operating system name")
|
|
43
|
+
|
|
44
|
+
# Associated device
|
|
45
|
+
ap_mac: str | None = Field(None, description="Access point MAC address")
|
|
46
|
+
sw_mac: str | None = Field(None, description="Switch MAC address")
|
|
47
|
+
gw_mac: str | None = Field(None, description="Gateway MAC address")
|
|
48
|
+
|
|
49
|
+
# VLAN
|
|
50
|
+
vlan: int | None = Field(None, description="VLAN ID")
|
|
51
|
+
network: str | None = Field(None, description="Network name")
|
|
52
|
+
|
|
53
|
+
@field_validator("os_name", mode="before")
|
|
54
|
+
@classmethod
|
|
55
|
+
def coerce_os_name_to_str(cls, v: int | str | None) -> str | None:
|
|
56
|
+
"""Convert os_name from int to str if needed (local API returns int)."""
|
|
57
|
+
if v is None:
|
|
58
|
+
return None
|
|
59
|
+
return str(v)
|
|
60
|
+
|
|
61
|
+
model_config = ConfigDict(
|
|
62
|
+
populate_by_name=True,
|
|
63
|
+
json_schema_extra={
|
|
64
|
+
"example": {
|
|
65
|
+
"mac": "aa:bb:cc:dd:ee:01",
|
|
66
|
+
"ip": "192.168.1.100",
|
|
67
|
+
"hostname": "laptop-001",
|
|
68
|
+
"is_wired": False,
|
|
69
|
+
"signal": -45,
|
|
70
|
+
"tx_bytes": 1024000,
|
|
71
|
+
"rx_bytes": 2048000,
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
)
|
src/models/device.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Device data model."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Device(BaseModel):
|
|
7
|
+
"""UniFi network device information."""
|
|
8
|
+
|
|
9
|
+
id: str = Field(..., description="Device ID", alias="_id")
|
|
10
|
+
name: str | None = Field(None, description="Device name")
|
|
11
|
+
model: str = Field(..., description="Device model")
|
|
12
|
+
type: str = Field(..., description="Device type (uap, usw, ugw, etc.)")
|
|
13
|
+
mac: str = Field(..., description="Device MAC address")
|
|
14
|
+
ip: str | None = Field(None, description="Device IP address")
|
|
15
|
+
|
|
16
|
+
# Status fields
|
|
17
|
+
state: int = Field(..., description="Device state (0=offline, 1=online, etc.)")
|
|
18
|
+
adopted: bool | None = Field(None, description="Whether device is adopted")
|
|
19
|
+
disabled: bool | None = Field(None, description="Whether device is disabled")
|
|
20
|
+
|
|
21
|
+
# Hardware info
|
|
22
|
+
version: str | None = Field(None, description="Firmware version")
|
|
23
|
+
uptime: int | None = Field(None, description="Uptime in seconds")
|
|
24
|
+
|
|
25
|
+
# Performance metrics
|
|
26
|
+
cpu: float | None = Field(None, description="CPU usage percentage")
|
|
27
|
+
mem: float | None = Field(None, description="Memory usage percentage")
|
|
28
|
+
uplink_depth: int | None = Field(None, description="Uplink depth in network topology")
|
|
29
|
+
|
|
30
|
+
# Network stats
|
|
31
|
+
bytes: int | None = Field(None, description="Total bytes transferred")
|
|
32
|
+
tx_bytes: int | None = Field(None, description="Transmitted bytes")
|
|
33
|
+
rx_bytes: int | None = Field(None, description="Received bytes")
|
|
34
|
+
|
|
35
|
+
# Additional metadata
|
|
36
|
+
serial: str | None = Field(None, description="Device serial number")
|
|
37
|
+
license_state: str | None = Field(None, description="License state")
|
|
38
|
+
|
|
39
|
+
model_config = ConfigDict(
|
|
40
|
+
populate_by_name=True,
|
|
41
|
+
json_schema_extra={
|
|
42
|
+
"example": {
|
|
43
|
+
"_id": "507f1f77bcf86cd799439011",
|
|
44
|
+
"name": "AP-Office",
|
|
45
|
+
"model": "U6-LR",
|
|
46
|
+
"type": "uap",
|
|
47
|
+
"mac": "aa:bb:cc:dd:ee:ff",
|
|
48
|
+
"ip": "192.168.1.10",
|
|
49
|
+
"state": 1,
|
|
50
|
+
"uptime": 86400,
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
)
|
src/models/dpi.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Deep Packet Inspection (DPI) models."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DPICategory(BaseModel):
|
|
7
|
+
"""DPI category model."""
|
|
8
|
+
|
|
9
|
+
id: str = Field(..., alias="_id", description="Category identifier")
|
|
10
|
+
name: str = Field(..., description="Category name")
|
|
11
|
+
description: str | None = Field(None, description="Category description")
|
|
12
|
+
|
|
13
|
+
# Application count
|
|
14
|
+
app_count: int | None = Field(None, description="Number of applications in this category")
|
|
15
|
+
|
|
16
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DPIApplication(BaseModel):
|
|
20
|
+
"""DPI application model."""
|
|
21
|
+
|
|
22
|
+
id: str = Field(..., alias="_id", description="Application identifier")
|
|
23
|
+
name: str = Field(..., description="Application name")
|
|
24
|
+
category_id: str = Field(..., description="Category identifier")
|
|
25
|
+
category_name: str | None = Field(None, description="Category name")
|
|
26
|
+
|
|
27
|
+
# Application metadata
|
|
28
|
+
enabled: bool = Field(True, description="Whether application detection is enabled")
|
|
29
|
+
|
|
30
|
+
# Traffic classification
|
|
31
|
+
protocols: list[str] = Field(
|
|
32
|
+
default_factory=list, description="Protocols used by this application"
|
|
33
|
+
)
|
|
34
|
+
ports: list[int] = Field(default_factory=list, description="Common ports used")
|
|
35
|
+
|
|
36
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Country(BaseModel):
|
|
40
|
+
"""Country information model."""
|
|
41
|
+
|
|
42
|
+
code: str = Field(..., description="ISO country code")
|
|
43
|
+
name: str = Field(..., description="Country name")
|
|
44
|
+
iso_code: str | None = Field(None, description="ISO 3166-1 alpha-2 code")
|
|
45
|
+
iso3_code: str | None = Field(None, description="ISO 3166-1 alpha-3 code")
|
|
46
|
+
|
|
47
|
+
# Regulatory information
|
|
48
|
+
regulatory_domain: str | None = Field(None, description="Regulatory domain for wireless")
|
|
49
|
+
|
|
50
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|