Skip to content

Commit

Permalink
feat: Apply api handler in vfolder handler
Browse files Browse the repository at this point in the history
  • Loading branch information
seedspirit committed Feb 5, 2025
1 parent b1dce6c commit 1eece08
Show file tree
Hide file tree
Showing 11 changed files with 869 additions and 85 deletions.
1 change: 1 addition & 0 deletions changes/3493.enhance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add skeleton vFolder handler Interface of manager
234 changes: 150 additions & 84 deletions python.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ python-json-logger~=3.2.0
pyzmq~=26.2
PyJWT~=2.0
PyYAML~=6.0
pydantic~=2.9.2
pydantic[email]~=2.9.2
packaging>=24.1
hiredis>=3.0.0
redis[hiredis]==4.5.5
Expand Down
1 change: 1 addition & 0 deletions src/ai/backend/manager/api/vfolders/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python_sources(name="src")
Empty file.
159 changes: 159 additions & 0 deletions src/ai/backend/manager/api/vfolders/api_schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import uuid
from typing import Any, Mapping, Optional, Self

from aiohttp import web
from pydantic import AliasChoices, BaseModel, Field

from ai.backend.common import typed_validators as tv
from ai.backend.common.api_handlers import BaseResponseModel, MiddlewareParam
from ai.backend.common.types import VFolderUsageMode
from ai.backend.manager.api.vfolders.dtos import (
Keypair,
UserIdentity,
VFolderCreateRequirements,
VFolderList,
VFolderListItem,
VFolderMetadata,
)
from ai.backend.manager.models import (
VFolderOperationStatus,
VFolderPermission,
)


class VFolderCreateRequest(BaseModel):
name: tv.VFolderName = Field(
description="Name of the vfolder",
)
folder_host: Optional[str] = Field(
validation_alias=AliasChoices("host", "folder_host"),
default=None,
)
usage_mode: VFolderUsageMode = Field(default=VFolderUsageMode.GENERAL)
permission: VFolderPermission = Field(default=VFolderPermission.READ_WRITE)
unmanaged_path: Optional[str] = Field(
validation_alias=AliasChoices("unmanaged_path", "unmanagedPath"),
default=None,
)
group_id: Optional[uuid.UUID] = Field(
validation_alias=AliasChoices("group", "groupId", "group_id"),
default=None,
)
cloneable: bool = Field(
default=False,
)

def to_dto(self) -> VFolderCreateRequirements:
return VFolderCreateRequirements(
name=self.name,
folder_host=self.folder_host,
usage_mode=self.usage_mode,
permission=self.permission,
group_id=self.group_id,
cloneable=self.cloneable,
unmanaged_path=self.unmanaged_path,
)


class VFolderListRequest(BaseModel):
group_id: Optional[uuid.UUID] = Field(
default=None, validation_alias=AliasChoices("group_id", "groupId")
)


class RenameVFolderId(BaseModel):
vfolder_id: uuid.UUID = Field(validation_alias=AliasChoices("vfolder_id", "vfolderId", "id"))


class VFolderNewName(BaseModel):
new_name: tv.VFolderName = Field(
description="Name of the vfolder",
)


class VFolderDeleteRequest(BaseModel):
vfolder_id: uuid.UUID = Field(
validation_alias=AliasChoices("vfolder_id", "vfolderId", "id"),
description="Target vfolder id to soft-delete, to go to trash bin",
)


class VFolderCreateResponse(BaseResponseModel):
id: str
name: str
quota_scope_id: str
host: str
usage_mode: VFolderUsageMode
permission: str
max_size: int = 0 # migrated to quota scopes, no longer valid
creator: str
ownership_type: str
user: Optional[str]
group: Optional[str]
cloneable: bool
status: VFolderOperationStatus = Field(default=VFolderOperationStatus.READY)

@classmethod
def from_vfolder_metadata(cls, data: VFolderMetadata):
return cls(
id=data.id,
name=data.name,
quota_scope_id=str(data.quota_scope_id),
host=data.host,
usage_mode=data.usage_mode,
permission=data.permission,
max_size=data.max_size,
creator=data.creator,
ownership_type=data.ownership_type,
user=data.user,
group=data.group,
cloneable=data.cloneable,
status=data.status,
)


class VFolderListResponse(BaseResponseModel):
root: list[VFolderListItem] = Field(default_factory=list)

@classmethod
def from_dataclass(cls, vfolder_list: VFolderList) -> Self:
return cls(root=vfolder_list.entries)


class UserIdentityModel(MiddlewareParam):
user_uuid: uuid.UUID
user_role: str
user_email: str
domain_name: str

@classmethod
def from_request(cls, request: web.Request) -> Self:
return cls(
user_uuid=request["user"]["uuid"],
user_role=request["user"]["role"],
user_email=request["user"]["email"],
domain_name=request["user"]["domain_name"],
)

def to_dto(self) -> UserIdentity:
return UserIdentity(
user_uuid=self.user_uuid,
user_role=self.user_role,
user_email=self.user_email,
domain_name=self.domain_name,
)


class KeypairModel(MiddlewareParam):
access_key: str
resource_policy: Mapping[str, Any]

@classmethod
def from_request(cls, request: web.Request) -> Self:
return cls(
access_key=request["keypair"]["access_key"],
resource_policy=request["keypair"]["resource_policy"],
)

def to_dto(self) -> Keypair:
return Keypair(access_key=self.access_key, resource_policy=self.resource_policy)
101 changes: 101 additions & 0 deletions src/ai/backend/manager/api/vfolders/dtos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import uuid
from dataclasses import dataclass
from typing import Any, Mapping, Optional

from ai.backend.common.types import QuotaScopeID, VFolderUsageMode
from ai.backend.manager.models import (
ProjectType,
VFolderOperationStatus,
VFolderOwnershipType,
VFolderPermission,
)


@dataclass
class UserIdentity:
user_uuid: uuid.UUID
user_role: str
user_email: str
domain_name: str


@dataclass
class Keypair:
access_key: str
resource_policy: Mapping[str, Any]


@dataclass
class UserScopeInput:
requester_id: uuid.UUID
is_authorized: bool
is_superadmin: bool
delegate_email: Optional[str] = None


@dataclass
class VFolderCreateRequirements:
name: str
folder_host: Optional[str]
usage_mode: VFolderUsageMode
permission: VFolderPermission
group_id: Optional[uuid.UUID]
cloneable: bool
unmanaged_path: Optional[str]


@dataclass
class VFolderMetadata:
id: str
name: str
quota_scope_id: QuotaScopeID
host: str
usage_mode: VFolderUsageMode
created_at: str
permission: VFolderPermission
max_size: int # migrated to quota scopes, no longer valid
creator: str
ownership_type: VFolderOwnershipType
user: Optional[str]
group: Optional[str]
cloneable: bool
status: VFolderOperationStatus


@dataclass
class VFolderListItem:
id: str
name: str
quota_scope_id: str
host: str
usage_mode: VFolderUsageMode
created_at: str
permission: VFolderPermission
max_size: int
creator: str
ownership_type: VFolderOwnershipType
user: Optional[str]
group: Optional[str]
cloneable: bool
status: VFolderOperationStatus
is_owner: bool
user_email: str
group_name: str
type: str # legacy
max_files: int
cur_size: int


@dataclass
class VFolderList:
entries: list[VFolderListItem]


@dataclass
class VFolderCapabilityInfo:
max_vfolder_count: int
max_quota_scope_size: int
ownership_type: str
quota_scope_id: QuotaScopeID
group_uuid: Optional[uuid.UUID] = None
group_type: Optional[ProjectType] = None
Loading

0 comments on commit 1eece08

Please sign in to comment.