truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- truthound_dashboard/api/alerts.py +75 -86
- truthound_dashboard/api/anomaly.py +7 -13
- truthound_dashboard/api/cross_alerts.py +38 -52
- truthound_dashboard/api/drift.py +49 -59
- truthound_dashboard/api/drift_monitor.py +234 -79
- truthound_dashboard/api/enterprise_sampling.py +498 -0
- truthound_dashboard/api/history.py +57 -5
- truthound_dashboard/api/lineage.py +3 -48
- truthound_dashboard/api/maintenance.py +104 -49
- truthound_dashboard/api/mask.py +1 -2
- truthound_dashboard/api/middleware.py +2 -1
- truthound_dashboard/api/model_monitoring.py +435 -311
- truthound_dashboard/api/notifications.py +227 -191
- truthound_dashboard/api/notifications_advanced.py +21 -20
- truthound_dashboard/api/observability.py +586 -0
- truthound_dashboard/api/plugins.py +2 -433
- truthound_dashboard/api/profile.py +199 -37
- truthound_dashboard/api/quality_reporter.py +701 -0
- truthound_dashboard/api/reports.py +7 -16
- truthound_dashboard/api/router.py +66 -0
- truthound_dashboard/api/rule_suggestions.py +5 -5
- truthound_dashboard/api/scan.py +17 -19
- truthound_dashboard/api/schedules.py +85 -50
- truthound_dashboard/api/schema_evolution.py +6 -6
- truthound_dashboard/api/schema_watcher.py +667 -0
- truthound_dashboard/api/sources.py +98 -27
- truthound_dashboard/api/tiering.py +1323 -0
- truthound_dashboard/api/triggers.py +14 -11
- truthound_dashboard/api/validations.py +12 -11
- truthound_dashboard/api/versioning.py +1 -6
- truthound_dashboard/core/__init__.py +129 -3
- truthound_dashboard/core/actions/__init__.py +62 -0
- truthound_dashboard/core/actions/custom.py +426 -0
- truthound_dashboard/core/actions/notifications.py +910 -0
- truthound_dashboard/core/actions/storage.py +472 -0
- truthound_dashboard/core/actions/webhook.py +281 -0
- truthound_dashboard/core/anomaly.py +262 -67
- truthound_dashboard/core/anomaly_explainer.py +4 -3
- truthound_dashboard/core/backends/__init__.py +67 -0
- truthound_dashboard/core/backends/base.py +299 -0
- truthound_dashboard/core/backends/errors.py +191 -0
- truthound_dashboard/core/backends/factory.py +423 -0
- truthound_dashboard/core/backends/mock_backend.py +451 -0
- truthound_dashboard/core/backends/truthound_backend.py +718 -0
- truthound_dashboard/core/checkpoint/__init__.py +87 -0
- truthound_dashboard/core/checkpoint/adapters.py +814 -0
- truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
- truthound_dashboard/core/checkpoint/runner.py +270 -0
- truthound_dashboard/core/connections.py +437 -10
- truthound_dashboard/core/converters/__init__.py +14 -0
- truthound_dashboard/core/converters/truthound.py +620 -0
- truthound_dashboard/core/cross_alerts.py +540 -320
- truthound_dashboard/core/datasource_factory.py +1672 -0
- truthound_dashboard/core/drift_monitor.py +216 -20
- truthound_dashboard/core/enterprise_sampling.py +1291 -0
- truthound_dashboard/core/interfaces/__init__.py +225 -0
- truthound_dashboard/core/interfaces/actions.py +652 -0
- truthound_dashboard/core/interfaces/base.py +247 -0
- truthound_dashboard/core/interfaces/checkpoint.py +676 -0
- truthound_dashboard/core/interfaces/protocols.py +664 -0
- truthound_dashboard/core/interfaces/reporters.py +650 -0
- truthound_dashboard/core/interfaces/routing.py +646 -0
- truthound_dashboard/core/interfaces/triggers.py +619 -0
- truthound_dashboard/core/lineage.py +407 -71
- truthound_dashboard/core/model_monitoring.py +431 -3
- truthound_dashboard/core/notifications/base.py +4 -0
- truthound_dashboard/core/notifications/channels.py +501 -1203
- truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
- truthound_dashboard/core/notifications/deduplication/service.py +131 -348
- truthound_dashboard/core/notifications/dispatcher.py +202 -11
- truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
- truthound_dashboard/core/notifications/escalation/engine.py +168 -358
- truthound_dashboard/core/notifications/routing/__init__.py +88 -128
- truthound_dashboard/core/notifications/routing/engine.py +90 -317
- truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
- truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
- truthound_dashboard/core/notifications/throttling/builder.py +117 -255
- truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
- truthound_dashboard/core/phase5/collaboration.py +1 -1
- truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
- truthound_dashboard/core/quality_reporter.py +1359 -0
- truthound_dashboard/core/report_history.py +0 -6
- truthound_dashboard/core/reporters/__init__.py +175 -14
- truthound_dashboard/core/reporters/adapters.py +943 -0
- truthound_dashboard/core/reporters/base.py +0 -3
- truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
- truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
- truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
- truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
- truthound_dashboard/core/reporters/compat.py +266 -0
- truthound_dashboard/core/reporters/csv_reporter.py +2 -35
- truthound_dashboard/core/reporters/factory.py +526 -0
- truthound_dashboard/core/reporters/interfaces.py +745 -0
- truthound_dashboard/core/reporters/registry.py +1 -10
- truthound_dashboard/core/scheduler.py +165 -0
- truthound_dashboard/core/schema_evolution.py +3 -3
- truthound_dashboard/core/schema_watcher.py +1528 -0
- truthound_dashboard/core/services.py +595 -76
- truthound_dashboard/core/store_manager.py +810 -0
- truthound_dashboard/core/streaming_anomaly.py +169 -4
- truthound_dashboard/core/tiering.py +1309 -0
- truthound_dashboard/core/triggers/evaluators.py +178 -8
- truthound_dashboard/core/truthound_adapter.py +2620 -197
- truthound_dashboard/core/unified_alerts.py +23 -20
- truthound_dashboard/db/__init__.py +8 -0
- truthound_dashboard/db/database.py +8 -2
- truthound_dashboard/db/models.py +944 -25
- truthound_dashboard/db/repository.py +2 -0
- truthound_dashboard/main.py +11 -0
- truthound_dashboard/schemas/__init__.py +177 -16
- truthound_dashboard/schemas/base.py +44 -23
- truthound_dashboard/schemas/collaboration.py +19 -6
- truthound_dashboard/schemas/cross_alerts.py +19 -3
- truthound_dashboard/schemas/drift.py +61 -55
- truthound_dashboard/schemas/drift_monitor.py +67 -23
- truthound_dashboard/schemas/enterprise_sampling.py +653 -0
- truthound_dashboard/schemas/lineage.py +0 -33
- truthound_dashboard/schemas/mask.py +10 -8
- truthound_dashboard/schemas/model_monitoring.py +89 -10
- truthound_dashboard/schemas/notifications_advanced.py +13 -0
- truthound_dashboard/schemas/observability.py +453 -0
- truthound_dashboard/schemas/plugins.py +0 -280
- truthound_dashboard/schemas/profile.py +154 -247
- truthound_dashboard/schemas/quality_reporter.py +403 -0
- truthound_dashboard/schemas/reports.py +2 -2
- truthound_dashboard/schemas/rule_suggestion.py +8 -1
- truthound_dashboard/schemas/scan.py +4 -24
- truthound_dashboard/schemas/schedule.py +11 -3
- truthound_dashboard/schemas/schema_watcher.py +727 -0
- truthound_dashboard/schemas/source.py +17 -2
- truthound_dashboard/schemas/tiering.py +822 -0
- truthound_dashboard/schemas/triggers.py +16 -0
- truthound_dashboard/schemas/unified_alerts.py +7 -0
- truthound_dashboard/schemas/validation.py +0 -13
- truthound_dashboard/schemas/validators/base.py +41 -21
- truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
- truthound_dashboard/schemas/validators/localization_validators.py +273 -0
- truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
- truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
- truthound_dashboard/schemas/validators/referential_validators.py +312 -0
- truthound_dashboard/schemas/validators/registry.py +93 -8
- truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
- truthound_dashboard/schemas/versioning.py +1 -6
- truthound_dashboard/static/index.html +2 -2
- truthound_dashboard-1.5.0.dist-info/METADATA +309 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/RECORD +149 -148
- truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
- truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
- truthound_dashboard/core/plugins/hooks/manager.py +0 -403
- truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
- truthound_dashboard/core/reporters/junit_reporter.py +0 -233
- truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
- truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
- truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
- truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
- truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
- truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
- truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
- truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
- truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
- truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
- truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
- truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
- truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
- truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
- truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
- truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
- truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
- truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
- truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
- truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
- truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
- truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
- truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
- truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
- truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
- truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
- truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
- truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
- truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
- truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
- truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
- truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
- truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
- truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
- truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
- truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
- truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
- truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
- truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
- truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
- truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
- truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
- truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
- truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
- truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
- truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
- truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
- truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
"""Pydantic schemas for storage tiering features.
|
|
2
|
+
|
|
3
|
+
This module provides schemas for:
|
|
4
|
+
- Storage tiers (hot, warm, cold, archive)
|
|
5
|
+
- Tier policies (age-based, access-based, size-based, scheduled, composite, custom)
|
|
6
|
+
- Tiering configurations
|
|
7
|
+
- Migration history
|
|
8
|
+
|
|
9
|
+
Based on truthound 1.2.10+ storage tiering capabilities.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
19
|
+
|
|
20
|
+
from .base import BaseSchema, IDMixin, ListResponseWrapper, TimestampMixin
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# Enums
|
|
25
|
+
# =============================================================================
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TierType(str, Enum):
|
|
29
|
+
"""Storage tier types."""
|
|
30
|
+
|
|
31
|
+
HOT = "hot"
|
|
32
|
+
WARM = "warm"
|
|
33
|
+
COLD = "cold"
|
|
34
|
+
ARCHIVE = "archive"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MigrationDirection(str, Enum):
|
|
38
|
+
"""Migration direction for tier policies."""
|
|
39
|
+
|
|
40
|
+
DEMOTE = "demote"
|
|
41
|
+
PROMOTE = "promote"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TierPolicyType(str, Enum):
|
|
45
|
+
"""Types of tier policies."""
|
|
46
|
+
|
|
47
|
+
AGE_BASED = "age_based"
|
|
48
|
+
ACCESS_BASED = "access_based"
|
|
49
|
+
SIZE_BASED = "size_based"
|
|
50
|
+
SCHEDULED = "scheduled"
|
|
51
|
+
COMPOSITE = "composite"
|
|
52
|
+
CUSTOM = "custom"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class MigrationStatus(str, Enum):
|
|
56
|
+
"""Migration operation status."""
|
|
57
|
+
|
|
58
|
+
PENDING = "pending"
|
|
59
|
+
IN_PROGRESS = "in_progress"
|
|
60
|
+
COMPLETED = "completed"
|
|
61
|
+
FAILED = "failed"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# =============================================================================
|
|
65
|
+
# Storage Tier Schemas
|
|
66
|
+
# =============================================================================
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class StorageTierBase(BaseSchema):
|
|
70
|
+
"""Base storage tier schema."""
|
|
71
|
+
|
|
72
|
+
name: str = Field(
|
|
73
|
+
...,
|
|
74
|
+
description="Unique tier name",
|
|
75
|
+
min_length=1,
|
|
76
|
+
max_length=50,
|
|
77
|
+
examples=["hot", "warm", "cold", "archive"],
|
|
78
|
+
)
|
|
79
|
+
tier_type: TierType = Field(
|
|
80
|
+
default=TierType.HOT,
|
|
81
|
+
description="Tier classification",
|
|
82
|
+
)
|
|
83
|
+
store_type: str = Field(
|
|
84
|
+
...,
|
|
85
|
+
description="Backend store type",
|
|
86
|
+
min_length=1,
|
|
87
|
+
max_length=50,
|
|
88
|
+
examples=["filesystem", "s3", "gcs", "azure_blob"],
|
|
89
|
+
)
|
|
90
|
+
store_config: dict[str, Any] = Field(
|
|
91
|
+
default_factory=dict,
|
|
92
|
+
description="Store backend configuration",
|
|
93
|
+
)
|
|
94
|
+
priority: int = Field(
|
|
95
|
+
default=1,
|
|
96
|
+
description="Read order priority (lower = higher priority)",
|
|
97
|
+
ge=1,
|
|
98
|
+
le=100,
|
|
99
|
+
)
|
|
100
|
+
cost_per_gb: float | None = Field(
|
|
101
|
+
default=None,
|
|
102
|
+
description="Cost per GB for cost analysis",
|
|
103
|
+
ge=0,
|
|
104
|
+
)
|
|
105
|
+
retrieval_time_ms: int | None = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
description="Expected retrieval latency in milliseconds",
|
|
108
|
+
ge=0,
|
|
109
|
+
)
|
|
110
|
+
metadata: dict[str, Any] | None = Field(
|
|
111
|
+
default=None,
|
|
112
|
+
description="Additional tier metadata",
|
|
113
|
+
)
|
|
114
|
+
is_active: bool = Field(
|
|
115
|
+
default=True,
|
|
116
|
+
description="Whether the tier is active",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class StorageTierCreate(StorageTierBase):
|
|
121
|
+
"""Schema for creating a storage tier."""
|
|
122
|
+
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class StorageTierUpdate(BaseSchema):
|
|
127
|
+
"""Schema for updating a storage tier."""
|
|
128
|
+
|
|
129
|
+
name: str | None = Field(None, min_length=1, max_length=50)
|
|
130
|
+
tier_type: TierType | None = None
|
|
131
|
+
store_type: str | None = Field(None, min_length=1, max_length=50)
|
|
132
|
+
store_config: dict[str, Any] | None = None
|
|
133
|
+
priority: int | None = Field(None, ge=1, le=100)
|
|
134
|
+
cost_per_gb: float | None = Field(None, ge=0)
|
|
135
|
+
retrieval_time_ms: int | None = Field(None, ge=0)
|
|
136
|
+
metadata: dict[str, Any] | None = None
|
|
137
|
+
is_active: bool | None = None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class StorageTierResponse(StorageTierBase, IDMixin, TimestampMixin):
|
|
141
|
+
"""Schema for storage tier response."""
|
|
142
|
+
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class StorageTierListResponse(ListResponseWrapper):
|
|
147
|
+
"""Schema for storage tier list response."""
|
|
148
|
+
|
|
149
|
+
items: list[StorageTierResponse]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# =============================================================================
|
|
153
|
+
# Tier Policy Schemas
|
|
154
|
+
# =============================================================================
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class PolicyConfigBase(BaseModel):
|
|
158
|
+
"""Base configuration for all policy types."""
|
|
159
|
+
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class AgeBasedPolicyConfig(PolicyConfigBase):
|
|
164
|
+
"""Configuration for age-based tier policy.
|
|
165
|
+
|
|
166
|
+
Migrate items based on age.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
after_days: int = Field(
|
|
170
|
+
default=0,
|
|
171
|
+
description="Days before migration",
|
|
172
|
+
ge=0,
|
|
173
|
+
le=3650, # 10 years max
|
|
174
|
+
)
|
|
175
|
+
after_hours: int = Field(
|
|
176
|
+
default=0,
|
|
177
|
+
description="Additional hours before migration",
|
|
178
|
+
ge=0,
|
|
179
|
+
le=23,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
@model_validator(mode="after")
|
|
183
|
+
def validate_at_least_one_time(self) -> "AgeBasedPolicyConfig":
|
|
184
|
+
"""Ensure at least one time unit is specified."""
|
|
185
|
+
if self.after_days == 0 and self.after_hours == 0:
|
|
186
|
+
raise ValueError("At least one time unit (days or hours) must be specified")
|
|
187
|
+
return self
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class AccessBasedPolicyConfig(PolicyConfigBase):
|
|
191
|
+
"""Configuration for access-based tier policy.
|
|
192
|
+
|
|
193
|
+
Migrate based on access patterns.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
inactive_days: int | None = Field(
|
|
197
|
+
default=None,
|
|
198
|
+
description="Days without access for demotion",
|
|
199
|
+
ge=1,
|
|
200
|
+
le=3650,
|
|
201
|
+
)
|
|
202
|
+
min_access_count: int | None = Field(
|
|
203
|
+
default=None,
|
|
204
|
+
description="Minimum accesses for promotion",
|
|
205
|
+
ge=1,
|
|
206
|
+
le=1000000,
|
|
207
|
+
)
|
|
208
|
+
access_window_days: int = Field(
|
|
209
|
+
default=7,
|
|
210
|
+
description="Window for counting accesses (days)",
|
|
211
|
+
ge=1,
|
|
212
|
+
le=365,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
@model_validator(mode="after")
|
|
216
|
+
def validate_at_least_one_condition(self) -> "AccessBasedPolicyConfig":
|
|
217
|
+
"""Ensure at least one condition is specified."""
|
|
218
|
+
if self.inactive_days is None and self.min_access_count is None:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
"At least one condition (inactive_days or min_access_count) must be specified"
|
|
221
|
+
)
|
|
222
|
+
return self
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class SizeBasedPolicyConfig(PolicyConfigBase):
|
|
226
|
+
"""Configuration for size-based tier policy.
|
|
227
|
+
|
|
228
|
+
Migrate based on item size or tier capacity.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
min_size_bytes: int = Field(
|
|
232
|
+
default=0,
|
|
233
|
+
description="Minimum item size in bytes",
|
|
234
|
+
ge=0,
|
|
235
|
+
)
|
|
236
|
+
min_size_kb: int = Field(
|
|
237
|
+
default=0,
|
|
238
|
+
description="Minimum item size in KB",
|
|
239
|
+
ge=0,
|
|
240
|
+
)
|
|
241
|
+
min_size_mb: int = Field(
|
|
242
|
+
default=0,
|
|
243
|
+
description="Minimum item size in MB",
|
|
244
|
+
ge=0,
|
|
245
|
+
)
|
|
246
|
+
min_size_gb: int = Field(
|
|
247
|
+
default=0,
|
|
248
|
+
description="Minimum item size in GB",
|
|
249
|
+
ge=0,
|
|
250
|
+
)
|
|
251
|
+
tier_max_size_bytes: int = Field(
|
|
252
|
+
default=0,
|
|
253
|
+
description="Maximum total tier size in bytes",
|
|
254
|
+
ge=0,
|
|
255
|
+
)
|
|
256
|
+
tier_max_size_gb: int = Field(
|
|
257
|
+
default=0,
|
|
258
|
+
description="Maximum total tier size in GB",
|
|
259
|
+
ge=0,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
@model_validator(mode="after")
|
|
263
|
+
def validate_at_least_one_size(self) -> "SizeBasedPolicyConfig":
|
|
264
|
+
"""Ensure at least one size condition is specified."""
|
|
265
|
+
has_item_size = any([
|
|
266
|
+
self.min_size_bytes > 0,
|
|
267
|
+
self.min_size_kb > 0,
|
|
268
|
+
self.min_size_mb > 0,
|
|
269
|
+
self.min_size_gb > 0,
|
|
270
|
+
])
|
|
271
|
+
has_tier_size = any([
|
|
272
|
+
self.tier_max_size_bytes > 0,
|
|
273
|
+
self.tier_max_size_gb > 0,
|
|
274
|
+
])
|
|
275
|
+
if not has_item_size and not has_tier_size:
|
|
276
|
+
raise ValueError("At least one size condition must be specified")
|
|
277
|
+
return self
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def total_min_size_bytes(self) -> int:
|
|
281
|
+
"""Calculate total minimum size in bytes."""
|
|
282
|
+
return (
|
|
283
|
+
self.min_size_bytes
|
|
284
|
+
+ self.min_size_kb * 1024
|
|
285
|
+
+ self.min_size_mb * 1024 * 1024
|
|
286
|
+
+ self.min_size_gb * 1024 * 1024 * 1024
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def total_tier_max_bytes(self) -> int:
|
|
291
|
+
"""Calculate total tier max size in bytes."""
|
|
292
|
+
return self.tier_max_size_bytes + self.tier_max_size_gb * 1024 * 1024 * 1024
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class ScheduledPolicyConfig(PolicyConfigBase):
|
|
296
|
+
"""Configuration for scheduled tier policy.
|
|
297
|
+
|
|
298
|
+
Migrate on a schedule (specific days/times).
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
on_days: list[int] | None = Field(
|
|
302
|
+
default=None,
|
|
303
|
+
description="Days to run (0=Monday, 6=Sunday)",
|
|
304
|
+
examples=[[0, 1, 2, 3, 4], [5, 6]],
|
|
305
|
+
)
|
|
306
|
+
at_hour: int | None = Field(
|
|
307
|
+
default=None,
|
|
308
|
+
description="Hour to run (0-23)",
|
|
309
|
+
ge=0,
|
|
310
|
+
le=23,
|
|
311
|
+
)
|
|
312
|
+
min_age_days: int = Field(
|
|
313
|
+
default=0,
|
|
314
|
+
description="Minimum item age in days",
|
|
315
|
+
ge=0,
|
|
316
|
+
le=3650,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
@field_validator("on_days")
|
|
320
|
+
@classmethod
|
|
321
|
+
def validate_days(cls, v: list[int] | None) -> list[int] | None:
|
|
322
|
+
"""Validate day values."""
|
|
323
|
+
if v is not None:
|
|
324
|
+
for day in v:
|
|
325
|
+
if day < 0 or day > 6:
|
|
326
|
+
raise ValueError(f"Day must be 0-6, got {day}")
|
|
327
|
+
return v
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class CompositePolicyConfig(PolicyConfigBase):
|
|
331
|
+
"""Configuration for composite tier policy.
|
|
332
|
+
|
|
333
|
+
Combine multiple policies with AND/OR logic.
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
require_all: bool = Field(
|
|
337
|
+
default=True,
|
|
338
|
+
description="True = AND logic (all must match), False = OR logic (any match)",
|
|
339
|
+
)
|
|
340
|
+
child_policy_ids: list[str] = Field(
|
|
341
|
+
default_factory=list,
|
|
342
|
+
description="IDs of child policies to combine",
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
@field_validator("child_policy_ids")
|
|
346
|
+
@classmethod
|
|
347
|
+
def validate_min_children(cls, v: list[str]) -> list[str]:
|
|
348
|
+
"""Ensure at least 2 child policies for composite."""
|
|
349
|
+
if len(v) < 2:
|
|
350
|
+
raise ValueError("Composite policy requires at least 2 child policies")
|
|
351
|
+
return v
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class CustomPolicyConfig(PolicyConfigBase):
|
|
355
|
+
"""Configuration for custom tier policy.
|
|
356
|
+
|
|
357
|
+
Define custom migration logic with a predicate expression.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
predicate_expression: str = Field(
|
|
361
|
+
...,
|
|
362
|
+
description="Python expression for migration predicate",
|
|
363
|
+
min_length=1,
|
|
364
|
+
max_length=1000,
|
|
365
|
+
)
|
|
366
|
+
description: str = Field(
|
|
367
|
+
default="",
|
|
368
|
+
description="Human-readable description of the custom logic",
|
|
369
|
+
max_length=500,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class TierPolicyBase(BaseSchema):
|
|
374
|
+
"""Base tier policy schema."""
|
|
375
|
+
|
|
376
|
+
name: str = Field(
|
|
377
|
+
...,
|
|
378
|
+
description="Policy name",
|
|
379
|
+
min_length=1,
|
|
380
|
+
max_length=255,
|
|
381
|
+
)
|
|
382
|
+
description: str | None = Field(
|
|
383
|
+
default=None,
|
|
384
|
+
description="Policy description",
|
|
385
|
+
max_length=1000,
|
|
386
|
+
)
|
|
387
|
+
policy_type: TierPolicyType = Field(
|
|
388
|
+
...,
|
|
389
|
+
description="Type of policy",
|
|
390
|
+
)
|
|
391
|
+
from_tier_id: str = Field(
|
|
392
|
+
...,
|
|
393
|
+
description="Source tier ID",
|
|
394
|
+
)
|
|
395
|
+
to_tier_id: str = Field(
|
|
396
|
+
...,
|
|
397
|
+
description="Destination tier ID",
|
|
398
|
+
)
|
|
399
|
+
direction: MigrationDirection = Field(
|
|
400
|
+
default=MigrationDirection.DEMOTE,
|
|
401
|
+
description="Migration direction",
|
|
402
|
+
)
|
|
403
|
+
config: dict[str, Any] = Field(
|
|
404
|
+
default_factory=dict,
|
|
405
|
+
description="Policy-specific configuration",
|
|
406
|
+
)
|
|
407
|
+
is_active: bool = Field(
|
|
408
|
+
default=True,
|
|
409
|
+
description="Whether policy is active",
|
|
410
|
+
)
|
|
411
|
+
priority: int = Field(
|
|
412
|
+
default=0,
|
|
413
|
+
description="Execution priority (lower = runs first)",
|
|
414
|
+
ge=0,
|
|
415
|
+
le=1000,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class TierPolicyCreate(TierPolicyBase):
|
|
420
|
+
"""Schema for creating a tier policy."""
|
|
421
|
+
|
|
422
|
+
parent_id: str | None = Field(
|
|
423
|
+
default=None,
|
|
424
|
+
description="Parent composite policy ID (for nested policies)",
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
class TierPolicyUpdate(BaseSchema):
|
|
429
|
+
"""Schema for updating a tier policy."""
|
|
430
|
+
|
|
431
|
+
name: str | None = Field(None, min_length=1, max_length=255)
|
|
432
|
+
description: str | None = Field(None, max_length=1000)
|
|
433
|
+
policy_type: TierPolicyType | None = None
|
|
434
|
+
from_tier_id: str | None = None
|
|
435
|
+
to_tier_id: str | None = None
|
|
436
|
+
direction: MigrationDirection | None = None
|
|
437
|
+
config: dict[str, Any] | None = None
|
|
438
|
+
is_active: bool | None = None
|
|
439
|
+
priority: int | None = Field(None, ge=0, le=1000)
|
|
440
|
+
parent_id: str | None = None
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class TierPolicyResponse(TierPolicyBase, IDMixin, TimestampMixin):
|
|
444
|
+
"""Schema for tier policy response."""
|
|
445
|
+
|
|
446
|
+
parent_id: str | None = Field(
|
|
447
|
+
default=None,
|
|
448
|
+
description="Parent composite policy ID",
|
|
449
|
+
)
|
|
450
|
+
child_count: int = Field(
|
|
451
|
+
default=0,
|
|
452
|
+
description="Number of child policies (for composite)",
|
|
453
|
+
)
|
|
454
|
+
from_tier_name: str | None = Field(
|
|
455
|
+
default=None,
|
|
456
|
+
description="Source tier name",
|
|
457
|
+
)
|
|
458
|
+
to_tier_name: str | None = Field(
|
|
459
|
+
default=None,
|
|
460
|
+
description="Destination tier name",
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
class TierPolicyListResponse(ListResponseWrapper):
|
|
465
|
+
"""Schema for tier policy list response."""
|
|
466
|
+
|
|
467
|
+
items: list[TierPolicyResponse]
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
class TierPolicyWithChildren(TierPolicyResponse):
|
|
471
|
+
"""Schema for tier policy with nested children."""
|
|
472
|
+
|
|
473
|
+
children: list["TierPolicyWithChildren"] = Field(
|
|
474
|
+
default_factory=list,
|
|
475
|
+
description="Nested child policies",
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
# Rebuild for self-referencing model
|
|
480
|
+
TierPolicyWithChildren.model_rebuild()
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
# =============================================================================
|
|
484
|
+
# Tiering Configuration Schemas
|
|
485
|
+
# =============================================================================
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
class TieringConfigBase(BaseSchema):
|
|
489
|
+
"""Base tiering configuration schema."""
|
|
490
|
+
|
|
491
|
+
name: str = Field(
|
|
492
|
+
...,
|
|
493
|
+
description="Configuration name",
|
|
494
|
+
min_length=1,
|
|
495
|
+
max_length=255,
|
|
496
|
+
)
|
|
497
|
+
description: str | None = Field(
|
|
498
|
+
default=None,
|
|
499
|
+
description="Configuration description",
|
|
500
|
+
max_length=1000,
|
|
501
|
+
)
|
|
502
|
+
default_tier_id: str | None = Field(
|
|
503
|
+
default=None,
|
|
504
|
+
description="Default tier for new items",
|
|
505
|
+
)
|
|
506
|
+
enable_promotion: bool = Field(
|
|
507
|
+
default=True,
|
|
508
|
+
description="Whether to auto-promote on frequent access",
|
|
509
|
+
)
|
|
510
|
+
promotion_threshold: int = Field(
|
|
511
|
+
default=10,
|
|
512
|
+
description="Access count to trigger promotion",
|
|
513
|
+
ge=1,
|
|
514
|
+
le=10000,
|
|
515
|
+
)
|
|
516
|
+
check_interval_hours: int = Field(
|
|
517
|
+
default=24,
|
|
518
|
+
description="Hours between auto-checks",
|
|
519
|
+
ge=1,
|
|
520
|
+
le=168, # 1 week max
|
|
521
|
+
)
|
|
522
|
+
batch_size: int = Field(
|
|
523
|
+
default=100,
|
|
524
|
+
description="Items per migration batch",
|
|
525
|
+
ge=1,
|
|
526
|
+
le=10000,
|
|
527
|
+
)
|
|
528
|
+
enable_parallel_migration: bool = Field(
|
|
529
|
+
default=False,
|
|
530
|
+
description="Whether to enable parallel migration",
|
|
531
|
+
)
|
|
532
|
+
max_parallel_migrations: int = Field(
|
|
533
|
+
default=4,
|
|
534
|
+
description="Maximum concurrent migrations",
|
|
535
|
+
ge=1,
|
|
536
|
+
le=100,
|
|
537
|
+
)
|
|
538
|
+
is_active: bool = Field(
|
|
539
|
+
default=True,
|
|
540
|
+
description="Whether configuration is active",
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
class TieringConfigCreate(TieringConfigBase):
|
|
545
|
+
"""Schema for creating a tiering configuration."""
|
|
546
|
+
|
|
547
|
+
pass
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
class TieringConfigUpdate(BaseSchema):
|
|
551
|
+
"""Schema for updating a tiering configuration."""
|
|
552
|
+
|
|
553
|
+
name: str | None = Field(None, min_length=1, max_length=255)
|
|
554
|
+
description: str | None = Field(None, max_length=1000)
|
|
555
|
+
default_tier_id: str | None = None
|
|
556
|
+
enable_promotion: bool | None = None
|
|
557
|
+
promotion_threshold: int | None = Field(None, ge=1, le=10000)
|
|
558
|
+
check_interval_hours: int | None = Field(None, ge=1, le=168)
|
|
559
|
+
batch_size: int | None = Field(None, ge=1, le=10000)
|
|
560
|
+
enable_parallel_migration: bool | None = None
|
|
561
|
+
max_parallel_migrations: int | None = Field(None, ge=1, le=100)
|
|
562
|
+
is_active: bool | None = None
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class TieringConfigResponse(TieringConfigBase, IDMixin, TimestampMixin):
|
|
566
|
+
"""Schema for tiering configuration response."""
|
|
567
|
+
|
|
568
|
+
default_tier_name: str | None = Field(
|
|
569
|
+
default=None,
|
|
570
|
+
description="Default tier name",
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
class TieringConfigListResponse(ListResponseWrapper):
|
|
575
|
+
"""Schema for tiering configuration list response."""
|
|
576
|
+
|
|
577
|
+
items: list[TieringConfigResponse]
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
# =============================================================================
|
|
581
|
+
# Migration History Schemas
|
|
582
|
+
# =============================================================================
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
class MigrationHistoryBase(BaseSchema):
|
|
586
|
+
"""Base migration history schema."""
|
|
587
|
+
|
|
588
|
+
policy_id: str | None = Field(
|
|
589
|
+
default=None,
|
|
590
|
+
description="Policy that triggered migration",
|
|
591
|
+
)
|
|
592
|
+
item_id: str = Field(
|
|
593
|
+
...,
|
|
594
|
+
description="ID of the migrated item",
|
|
595
|
+
min_length=1,
|
|
596
|
+
max_length=255,
|
|
597
|
+
)
|
|
598
|
+
from_tier_id: str = Field(
|
|
599
|
+
...,
|
|
600
|
+
description="Source tier ID",
|
|
601
|
+
)
|
|
602
|
+
to_tier_id: str = Field(
|
|
603
|
+
...,
|
|
604
|
+
description="Destination tier ID",
|
|
605
|
+
)
|
|
606
|
+
size_bytes: int = Field(
|
|
607
|
+
default=0,
|
|
608
|
+
description="Size of migrated item",
|
|
609
|
+
ge=0,
|
|
610
|
+
)
|
|
611
|
+
status: MigrationStatus = Field(
|
|
612
|
+
default=MigrationStatus.PENDING,
|
|
613
|
+
description="Migration status",
|
|
614
|
+
)
|
|
615
|
+
error_message: str | None = Field(
|
|
616
|
+
default=None,
|
|
617
|
+
description="Error message if failed",
|
|
618
|
+
max_length=1000,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
class MigrationHistoryCreate(MigrationHistoryBase):
|
|
623
|
+
"""Schema for creating a migration history entry."""
|
|
624
|
+
|
|
625
|
+
pass
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
class MigrationHistoryResponse(MigrationHistoryBase, IDMixin):
|
|
629
|
+
"""Schema for migration history response."""
|
|
630
|
+
|
|
631
|
+
started_at: datetime = Field(..., description="When migration started")
|
|
632
|
+
completed_at: datetime | None = Field(
|
|
633
|
+
default=None,
|
|
634
|
+
description="When migration completed",
|
|
635
|
+
)
|
|
636
|
+
duration_ms: float | None = Field(
|
|
637
|
+
default=None,
|
|
638
|
+
description="Migration duration in milliseconds",
|
|
639
|
+
)
|
|
640
|
+
from_tier_name: str | None = Field(
|
|
641
|
+
default=None,
|
|
642
|
+
description="Source tier name",
|
|
643
|
+
)
|
|
644
|
+
to_tier_name: str | None = Field(
|
|
645
|
+
default=None,
|
|
646
|
+
description="Destination tier name",
|
|
647
|
+
)
|
|
648
|
+
policy_name: str | None = Field(
|
|
649
|
+
default=None,
|
|
650
|
+
description="Policy name",
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
class MigrationHistoryListResponse(ListResponseWrapper):
|
|
655
|
+
"""Schema for migration history list response."""
|
|
656
|
+
|
|
657
|
+
items: list[MigrationHistoryResponse]
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
# =============================================================================
|
|
661
|
+
# Statistics Schemas
|
|
662
|
+
# =============================================================================
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
class TierStatistics(BaseModel):
|
|
666
|
+
"""Statistics for a single tier."""
|
|
667
|
+
|
|
668
|
+
tier_id: str
|
|
669
|
+
tier_name: str
|
|
670
|
+
tier_type: TierType
|
|
671
|
+
item_count: int = 0
|
|
672
|
+
total_size_bytes: int = 0
|
|
673
|
+
total_size_gb: float = 0.0
|
|
674
|
+
estimated_cost: float | None = None
|
|
675
|
+
policy_count: int = 0
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
class TieringStatistics(BaseModel):
|
|
679
|
+
"""Overall tiering statistics."""
|
|
680
|
+
|
|
681
|
+
total_tiers: int = 0
|
|
682
|
+
active_tiers: int = 0
|
|
683
|
+
total_policies: int = 0
|
|
684
|
+
active_policies: int = 0
|
|
685
|
+
composite_policies: int = 0
|
|
686
|
+
total_migrations: int = 0
|
|
687
|
+
successful_migrations: int = 0
|
|
688
|
+
failed_migrations: int = 0
|
|
689
|
+
total_bytes_migrated: int = 0
|
|
690
|
+
tier_stats: list[TierStatistics] = Field(default_factory=list)
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
class PolicyTypeInfo(BaseModel):
|
|
694
|
+
"""Information about a policy type."""
|
|
695
|
+
|
|
696
|
+
type: TierPolicyType
|
|
697
|
+
name: str
|
|
698
|
+
description: str
|
|
699
|
+
config_schema: dict[str, Any]
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
class PolicyTypesResponse(BaseModel):
|
|
703
|
+
"""Response for available policy types."""
|
|
704
|
+
|
|
705
|
+
policy_types: list[PolicyTypeInfo]
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
# =============================================================================
|
|
709
|
+
# Policy Execution Schemas
|
|
710
|
+
# =============================================================================
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
class PolicyExecutionRequest(BaseModel):
|
|
714
|
+
"""Request for policy execution."""
|
|
715
|
+
|
|
716
|
+
dry_run: bool = Field(
|
|
717
|
+
default=False,
|
|
718
|
+
description="If True, don't actually migrate, just report what would happen",
|
|
719
|
+
)
|
|
720
|
+
batch_size: int = Field(
|
|
721
|
+
default=100,
|
|
722
|
+
description="Maximum items to process",
|
|
723
|
+
ge=1,
|
|
724
|
+
le=10000,
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
class MigrationItemResponse(BaseModel):
|
|
729
|
+
"""Response for a single migration operation."""
|
|
730
|
+
|
|
731
|
+
item_id: str
|
|
732
|
+
from_tier: str
|
|
733
|
+
to_tier: str
|
|
734
|
+
success: bool
|
|
735
|
+
size_bytes: int = 0
|
|
736
|
+
error_message: str | None = None
|
|
737
|
+
duration_ms: float = 0.0
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
class PolicyExecutionResponse(BaseModel):
|
|
741
|
+
"""Response for policy execution."""
|
|
742
|
+
|
|
743
|
+
policy_id: str
|
|
744
|
+
dry_run: bool
|
|
745
|
+
start_time: datetime
|
|
746
|
+
end_time: datetime
|
|
747
|
+
duration_seconds: float
|
|
748
|
+
items_scanned: int = 0
|
|
749
|
+
items_migrated: int = 0
|
|
750
|
+
items_failed: int = 0
|
|
751
|
+
bytes_migrated: int = 0
|
|
752
|
+
success_rate: float = 1.0
|
|
753
|
+
errors: list[str] = Field(default_factory=list)
|
|
754
|
+
migrations: list[MigrationItemResponse] = Field(default_factory=list)
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
class MigrateItemRequest(BaseModel):
|
|
758
|
+
"""Request for migrating a single item."""
|
|
759
|
+
|
|
760
|
+
from_tier_id: str = Field(..., description="Source tier ID")
|
|
761
|
+
to_tier_id: str = Field(..., description="Destination tier ID")
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
class PolicyExecutionSummary(BaseModel):
|
|
765
|
+
"""Summary of a single policy execution."""
|
|
766
|
+
|
|
767
|
+
items_scanned: int = 0
|
|
768
|
+
items_migrated: int = 0
|
|
769
|
+
items_failed: int = 0
|
|
770
|
+
bytes_migrated: int = 0
|
|
771
|
+
duration_seconds: float = 0.0
|
|
772
|
+
success_rate: float = 1.0
|
|
773
|
+
errors: list[str] = Field(default_factory=list)
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
class ProcessPoliciesResponse(BaseModel):
|
|
777
|
+
"""Response for processing all policies."""
|
|
778
|
+
|
|
779
|
+
policies_executed: int = 0
|
|
780
|
+
total_items_scanned: int = 0
|
|
781
|
+
total_items_migrated: int = 0
|
|
782
|
+
total_items_failed: int = 0
|
|
783
|
+
total_bytes_migrated: int = 0
|
|
784
|
+
errors: list[str] = Field(default_factory=list)
|
|
785
|
+
policy_results: list[PolicyExecutionSummary] = Field(default_factory=list)
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
class TieringStatusResponse(BaseModel):
|
|
789
|
+
"""Response for tiering system status."""
|
|
790
|
+
|
|
791
|
+
truthound_available: bool = Field(
|
|
792
|
+
...,
|
|
793
|
+
description="Whether truthound tiering module is available",
|
|
794
|
+
)
|
|
795
|
+
tiering_enabled: bool = Field(
|
|
796
|
+
...,
|
|
797
|
+
description="Whether tiering is enabled via active config",
|
|
798
|
+
)
|
|
799
|
+
active_config_id: str | None = Field(
|
|
800
|
+
default=None,
|
|
801
|
+
description="Active configuration ID",
|
|
802
|
+
)
|
|
803
|
+
active_config_name: str | None = Field(
|
|
804
|
+
default=None,
|
|
805
|
+
description="Active configuration name",
|
|
806
|
+
)
|
|
807
|
+
check_interval_hours: int | None = Field(
|
|
808
|
+
default=None,
|
|
809
|
+
description="Hours between automatic policy checks",
|
|
810
|
+
)
|
|
811
|
+
active_tiers: int = Field(
|
|
812
|
+
default=0,
|
|
813
|
+
description="Number of active storage tiers",
|
|
814
|
+
)
|
|
815
|
+
active_policies: int = Field(
|
|
816
|
+
default=0,
|
|
817
|
+
description="Number of active policies",
|
|
818
|
+
)
|
|
819
|
+
migrations_last_24h: int = Field(
|
|
820
|
+
default=0,
|
|
821
|
+
description="Number of migrations in the last 24 hours",
|
|
822
|
+
)
|