-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add mounts API #32
Add mounts API #32
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
"""Models for Supervisor mounts.""" | ||
|
||
from abc import ABC | ||
from dataclasses import dataclass, field | ||
from enum import StrEnum | ||
from pathlib import PurePath | ||
from typing import Literal | ||
|
||
from .base import Request, ResponseData | ||
|
||
# --- ENUMS ---- | ||
|
||
|
||
class MountType(StrEnum): | ||
"""MountType type.""" | ||
|
||
CIFS = "cifs" | ||
NFS = "nfs" | ||
|
||
|
||
class MountUsage(StrEnum): | ||
"""MountUsage type.""" | ||
|
||
BACKUP = "backup" | ||
MEDIA = "media" | ||
SHARE = "share" | ||
|
||
|
||
class MountState(StrEnum): | ||
"""MountState type.""" | ||
|
||
ACTIVE = "active" | ||
ACTIVATING = "activating" | ||
DEACTIVATING = "deactivating" | ||
FAILED = "failed" | ||
INACTIVE = "inactive" | ||
MAINTENANCE = "maintenance" | ||
RELOADING = "reloading" | ||
|
||
|
||
class MountCifsVersion(StrEnum): | ||
"""Mount CIFS version.""" | ||
|
||
LEGACY_1_0 = "1.0" | ||
LEGACY_2_0 = "2.0" | ||
|
||
|
||
# --- OBJECTS ---- | ||
|
||
|
||
@dataclass(frozen=True) | ||
class Mount(ABC): | ||
"""Mount ABC type.""" | ||
|
||
usage: MountUsage | ||
server: str | ||
port: int | None = field(kw_only=True, default=None) | ||
|
||
|
||
@dataclass(frozen=True) | ||
class CIFSMount(ABC): | ||
"""CIFSMount ABC type.""" | ||
|
||
share: str | ||
version: MountCifsVersion | None = field(kw_only=True, default=None) | ||
|
||
|
||
@dataclass(frozen=True) | ||
class NFSMount(ABC): | ||
"""NFSMount ABC type.""" | ||
|
||
path: PurePath | ||
|
||
|
||
@dataclass(frozen=True) | ||
class MountResponse(ABC): | ||
"""MountResponse model.""" | ||
|
||
name: str | ||
read_only: bool | ||
state: MountState | None | ||
|
||
|
||
@dataclass(frozen=True) | ||
class MountRequest(ABC): # noqa: B024 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to disable the ruff rule here but not in the other ABC classes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe its because the only field it has is optional. As long as there is at least one required field that doesn't show up. |
||
"""MountRequest model.""" | ||
|
||
read_only: bool | None = field(kw_only=True, default=None) | ||
|
||
|
||
@dataclass(frozen=True, slots=True) | ||
class CIFSMountResponse(Mount, MountResponse, CIFSMount, ResponseData): | ||
"""CIFSMountResponse model.""" | ||
|
||
type: Literal[MountType.CIFS] | ||
|
||
|
||
@dataclass(frozen=True, slots=True) | ||
class NFSMountResponse(Mount, MountResponse, NFSMount, ResponseData): | ||
"""NFSMountResponse model.""" | ||
|
||
type: Literal[MountType.NFS] | ||
|
||
|
||
@dataclass(frozen=True, slots=True) | ||
class CIFSMountRequest(Mount, MountRequest, CIFSMount, Request): | ||
"""CIFSMountRequest model.""" | ||
|
||
type: Literal[MountType.CIFS] = field(init=False, default=MountType.CIFS) | ||
username: str | None = field(kw_only=True, default=None) | ||
password: str | None = field(kw_only=True, default=None) | ||
|
||
|
||
@dataclass(frozen=True, slots=True) | ||
class NFSMountRequest(Mount, MountRequest, NFSMount, Request): | ||
"""NFSMountRequest model.""" | ||
|
||
type: Literal[MountType.NFS] = field(init=False, default=MountType.NFS) | ||
|
||
|
||
@dataclass(frozen=True, slots=True) | ||
class MountsInfo(ResponseData): | ||
"""MountsInfo model.""" | ||
|
||
default_backup_mount: str | None | ||
mounts: list[CIFSMountResponse | NFSMountResponse] | ||
|
||
|
||
@dataclass(frozen=True, slots=True) | ||
class MountsOptions(Request): | ||
"""MountsOptions model.""" | ||
|
||
default_backup_mount: str | None |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
"""Mounts client for Supervisor.""" | ||
|
||
from .client import _SupervisorComponentClient | ||
from .models.mounts import CIFSMountRequest, MountsInfo, MountsOptions, NFSMountRequest | ||
|
||
|
||
class MountsClient(_SupervisorComponentClient): | ||
"""Handle mounts access in supervisor.""" | ||
|
||
async def info(self) -> MountsInfo: | ||
"""Get mounts info.""" | ||
result = await self._client.get("mounts") | ||
return MountsInfo.from_dict(result.data) | ||
|
||
async def options(self, options: MountsOptions) -> None: | ||
"""Set mounts options.""" | ||
await self._client.post("mounts/options", json=options.to_dict()) | ||
|
||
async def create_mount( | ||
self, name: str, config: CIFSMountRequest | NFSMountRequest | ||
) -> None: | ||
"""Create a new mount.""" | ||
await self._client.post("mounts", json={"name": name, **config.to_dict()}) | ||
|
||
async def update_mount( | ||
self, name: str, config: CIFSMountRequest | NFSMountRequest | ||
) -> None: | ||
"""Update an existing mount.""" | ||
await self._client.put(f"mounts/{name}", json=config.to_dict()) | ||
|
||
async def delete_mount(self, name: str) -> None: | ||
"""Delete an existing mount.""" | ||
await self._client.delete(f"mounts/{name}") | ||
|
||
async def reload_mount(self, name: str) -> None: | ||
"""Reload details of an existing mount.""" | ||
await self._client.post(f"mounts/{name}/reload") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"result": "ok", | ||
"data": { | ||
"default_backup_mount": "Test", | ||
"mounts": [ | ||
{ | ||
"share": "backup", | ||
"server": "test.local", | ||
"name": "Test", | ||
"type": "cifs", | ||
"usage": "backup", | ||
"read_only": false, | ||
"version": null, | ||
"state": "active" | ||
}, | ||
{ | ||
"share": "share", | ||
"server": "test2.local", | ||
"name": "Test2", | ||
"type": "cifs", | ||
"usage": "share", | ||
"read_only": true, | ||
"version": "2.0", | ||
"port": 12345, | ||
"state": "active" | ||
}, | ||
{ | ||
"server": "test3.local", | ||
"name": "Test2", | ||
"type": "nfs", | ||
"usage": "media", | ||
"read_only": false, | ||
"path": "media", | ||
"state": "active" | ||
} | ||
] | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to reviewers: the
user_path
field added in home-assistant/supervisor#5438 was specifically excluded from here. The implementation plan for cloud backup is still under debate and so this field may be removed before next Supervisor beta. I'll add the field in a follow-up if it remains.