Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-132813 / 25.04 / Convert pool.snapshottask to new API #15164

Merged
merged 8 commits into from
Dec 10, 2024
3 changes: 2 additions & 1 deletion src/middlewared/middlewared/api/v25_04_0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
from .iscsi_extent import * # noqa
from .keychain import * # noqa
from .netdata import * # noqa
from .pool_scrub import * # noqa
from .pool import * # noqa
from .pool_resilver import * # noqa
from .pool_scrub import * # noqa
from .pool_snapshottask import * # noqa
from .privilege import * # noqa
from .reporting import * # noqa
from .reporting_exporters import * # noqa
Expand Down
124 changes: 124 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/pool_snapshottask.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from typing import Annotated, Any, Literal

from pydantic import AfterValidator, Field

from middlewared.api.base import BaseModel, ForUpdateMetaclass, TimeString
from middlewared.validators import ReplicationSnapshotNamingSchema
from .common import CronModel


__all__ = [
"PoolSnapshotTaskEntry", "PoolSnapshotTaskCreateArgs", "PoolSnapshotTaskCreateResult",
"PoolSnapshotTaskUpdateArgs", "PoolSnapshotTaskUpdateResult", "PoolSnapshotTaskDeleteArgs",
"PoolSnapshotTaskDeleteResult", "PoolSnapshotTaskMaxCountArgs", "PoolSnapshotTaskMaxCountResult",
"PoolSnapshotTaskMaxTotalCountArgs", "PoolSnapshotTaskMaxTotalCountResult", "PoolSnapshotTaskRunArgs",
"PoolSnapshotTaskRunResult", "PoolSnapshotTaskUpdateWillChangeRetentionForArgs",
"PoolSnapshotTaskUpdateWillChangeRetentionForResult", "PoolSnapshotTaskDeleteWillChangeRetentionForArgs",
"PoolSnapshotTaskDeleteWillChangeRetentionForResult"
]


SnapshotNameSchema = Annotated[str, AfterValidator(ReplicationSnapshotNamingSchema())]


class PoolSnapshotTaskCron(CronModel):
minute: str = "00"
begin: TimeString = "00:00"
end: TimeString = "23:59"


class PoolSnapshotTaskCreate(BaseModel):
dataset: str
recursive: bool = False
lifetime_value: int = 2
lifetime_unit: Literal["HOUR", "DAY", "WEEK", "MONTH", "YEAR"] = "WEEK"
enabled: bool = True
exclude: list[str] = []
naming_schema: SnapshotNameSchema = "auto-%Y-%m-%d_%H-%M"
allow_empty: bool = True
schedule: PoolSnapshotTaskCron = Field(default_factory=PoolSnapshotTaskCron)


class PoolSnapshotTaskUpdate(PoolSnapshotTaskCreate, metaclass=ForUpdateMetaclass):
fixate_removal_date: bool


class PoolSnapshotTaskUpdateWillChangeRetentionFor(PoolSnapshotTaskCreate, metaclass=ForUpdateMetaclass):
pass


class PoolSnapshotTaskDeleteOptions(BaseModel):
fixate_removal_date: bool = False


class PoolSnapshotTaskEntry(PoolSnapshotTaskCreate):
id: int
vmware_sync: bool
state: Any


class PoolSnapshotTaskCreateArgs(BaseModel):
data: PoolSnapshotTaskCreate


class PoolSnapshotTaskCreateResult(BaseModel):
result: PoolSnapshotTaskEntry


class PoolSnapshotTaskUpdateArgs(BaseModel):
id: int
data: PoolSnapshotTaskUpdate


class PoolSnapshotTaskUpdateResult(BaseModel):
result: PoolSnapshotTaskEntry


class PoolSnapshotTaskDeleteArgs(BaseModel):
id: int
options: PoolSnapshotTaskDeleteOptions = Field(default_factory=PoolSnapshotTaskDeleteOptions)


class PoolSnapshotTaskDeleteResult(BaseModel):
result: Literal[True]


class PoolSnapshotTaskMaxCountArgs(BaseModel):
pass


class PoolSnapshotTaskMaxCountResult(BaseModel):
result: Literal[512]
creatorcary marked this conversation as resolved.
Show resolved Hide resolved


class PoolSnapshotTaskMaxTotalCountArgs(BaseModel):
pass


class PoolSnapshotTaskMaxTotalCountResult(BaseModel):
result: Literal[10000]
creatorcary marked this conversation as resolved.
Show resolved Hide resolved


class PoolSnapshotTaskRunArgs(BaseModel):
id: int


class PoolSnapshotTaskRunResult(BaseModel):
result: None


class PoolSnapshotTaskUpdateWillChangeRetentionForArgs(BaseModel):
id: int
data: PoolSnapshotTaskUpdateWillChangeRetentionFor


class PoolSnapshotTaskUpdateWillChangeRetentionForResult(BaseModel):
result: dict[str, list[str]]


class PoolSnapshotTaskDeleteWillChangeRetentionForArgs(BaseModel):
id: int


class PoolSnapshotTaskDeleteWillChangeRetentionForResult(BaseModel):
result: dict[str, list[str]]
72 changes: 24 additions & 48 deletions src/middlewared/middlewared/plugins/snapshot.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
from datetime import datetime, time, timedelta
from datetime import time
import os

from middlewared.api import api_method
from middlewared.api.current import (
PoolSnapshotTaskEntry, PoolSnapshotTaskCreateArgs, PoolSnapshotTaskCreateResult, PoolSnapshotTaskUpdateArgs,
PoolSnapshotTaskUpdateResult, PoolSnapshotTaskDeleteArgs, PoolSnapshotTaskDeleteResult,
PoolSnapshotTaskMaxCountArgs, PoolSnapshotTaskMaxCountResult, PoolSnapshotTaskMaxTotalCountArgs,
PoolSnapshotTaskMaxTotalCountResult, PoolSnapshotTaskRunArgs, PoolSnapshotTaskRunResult
)
from middlewared.common.attachment import FSAttachmentDelegate
from middlewared.schema import accepts, returns, Bool, Cron, Dataset, Dict, Int, List, Patch, Str
from middlewared.schema import Cron
from middlewared.service import CallError, CRUDService, item_method, private, ValidationErrors
import middlewared.sqlalchemy as sa
from middlewared.utils.cron import croniter_for_schedule
from middlewared.utils.path import is_child
from middlewared.validators import ReplicationSnapshotNamingSchema


class PeriodicSnapshotTaskModel(sa.Model):
Expand Down Expand Up @@ -40,6 +45,7 @@ class Config:
datastore_extend_context = 'pool.snapshottask.extend_context'
namespace = 'pool.snapshottask'
cli_namespace = 'task.snapshot'
entry = PoolSnapshotTaskEntry

@private
async def extend_context(self, rows, extra):
Expand Down Expand Up @@ -69,29 +75,9 @@ async def extend(self, data, context):

return data

@accepts(
Dict(
'periodic_snapshot_create',
Dataset('dataset', required=True),
Bool('recursive', required=True),
List('exclude', items=[Dataset('item')]),
Int('lifetime_value', required=True),
Str('lifetime_unit', enum=['HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR'], required=True),
Str('naming_schema', required=True, validators=[ReplicationSnapshotNamingSchema()]),
Cron(
'schedule',
defaults={
'minute': '00',
'begin': '00:00',
'end': '23:59',
},
required=True,
begin_end=True
),
Bool('allow_empty', default=True),
Bool('enabled', default=True),
register=True
),
@api_method(
PoolSnapshotTaskCreateArgs,
PoolSnapshotTaskCreateResult,
audit='Snapshot task create:',
audit_extended=lambda data: data['dataset']
)
Expand Down Expand Up @@ -158,16 +144,11 @@ async def do_create(self, data):

return await self.get_instance(data['id'])

@accepts(
Int('id', required=True),
Patch(
'periodic_snapshot_create',
'periodic_snapshot_update',
('add', {'name': 'fixate_removal_date', 'type': 'bool'}),
('attr', {'update': True})
),
@api_method(
PoolSnapshotTaskUpdateArgs,
PoolSnapshotTaskUpdateResult,
audit='Snapshot task update:',
audit_callback=True,
audit_callback=True
)
async def do_update(self, audit_callback, id_, data):
"""
Expand Down Expand Up @@ -255,14 +236,11 @@ async def do_update(self, audit_callback, id_, data):

return await self.get_instance(id_)

@accepts(
Int('id'),
Dict(
'options',
Bool('fixate_removal_date', default=False),
),
@api_method(
PoolSnapshotTaskDeleteArgs,
PoolSnapshotTaskDeleteResult,
audit='Snapshot task delete:',
audit_callback=True,
audit_callback=True
)
async def do_delete(self, audit_callback, id_, options):
"""
Expand Down Expand Up @@ -318,8 +296,7 @@ async def do_delete(self, audit_callback, id_, options):

return response

@accepts()
@returns(Int())
@api_method(PoolSnapshotTaskMaxCountArgs, PoolSnapshotTaskMaxCountResult)
def max_count(self):
"""
Returns a maximum amount of snapshots (per-dataset) the system can sustain.
Expand All @@ -329,8 +306,7 @@ def max_count(self):
# with too many, then File Explorer will show no snapshots available.
return 512

@accepts()
@returns(Int())
@api_method(PoolSnapshotTaskMaxTotalCountArgs, PoolSnapshotTaskMaxTotalCountResult)
def max_total_count(self):
"""
Returns a maximum amount of snapshots (total) the system can sustain.
Expand All @@ -341,7 +317,7 @@ def max_total_count(self):
return 10000

@item_method
@accepts(Int("id"))
@api_method(PoolSnapshotTaskRunArgs, PoolSnapshotTaskRunResult)
async def run(self, id_):
"""
Execute a Periodic Snapshot Task of `id`.
Expand Down
2 changes: 0 additions & 2 deletions src/middlewared/middlewared/plugins/snapshot_/removal_date.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import hashlib

from middlewared.service import Service, job, private


Expand Down
23 changes: 8 additions & 15 deletions src/middlewared/middlewared/plugins/snapshot_/task_retention.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from collections import defaultdict

from middlewared.schema import Dict, Int, Patch, returns
from middlewared.service import accepts, item_method, Service
from middlewared.api import api_method
from middlewared.api.current import (
PoolSnapshotTaskUpdateWillChangeRetentionForArgs, PoolSnapshotTaskUpdateWillChangeRetentionForResult,
PoolSnapshotTaskDeleteWillChangeRetentionForArgs, PoolSnapshotTaskDeleteWillChangeRetentionForResult
)
from middlewared.service import item_method, Service


class PeriodicSnapshotTaskService(Service):
Expand All @@ -10,15 +14,7 @@ class Config:
namespace = "pool.snapshottask"

@item_method
@accepts(
Int("id"),
Patch(
"periodic_snapshot_create",
"periodic_snapshot_update_will_change_retention",
("attr", {"update": True}),
),
)
@returns(Dict("snapshots", additional_attrs=True))
@api_method(PoolSnapshotTaskUpdateWillChangeRetentionForArgs, PoolSnapshotTaskUpdateWillChangeRetentionForResult)
async def update_will_change_retention_for(self, id_, data):
"""
Returns a list of snapshots which will change the retention if periodic snapshot task `id` is updated
Expand All @@ -40,10 +36,7 @@ async def update_will_change_retention_for(self, id_, data):
return result

@item_method
@accepts(
Int("id"),
)
@returns(Dict("snapshots", additional_attrs=True))
@api_method(PoolSnapshotTaskDeleteWillChangeRetentionForArgs, PoolSnapshotTaskDeleteWillChangeRetentionForResult)
async def delete_will_change_retention_for(self, id_):
"""
Returns a list of snapshots which will change the retention if periodic snapshot task `id` is deleted.
Expand Down
Loading
Loading