mirror of
https://github.com/SynologyOpenSource/synology-csi.git
synced 2026-08-21 11:46:57 +00:00
403 lines
11 KiB
Go
403 lines
11 KiB
Go
// Copyright 2025 Synology Inc.
|
|
|
|
package webapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/SynologyOpenSource/synology-csi/pkg/utils"
|
|
)
|
|
|
|
type NamespaceInfo struct {
|
|
Uuid string `json:"uuid"`
|
|
Name string `json:"name"`
|
|
Location string `json:"location"`
|
|
Size uint64 `json:"size"`
|
|
IsThinProvision bool `json:"is_thin_provision"`
|
|
IsActionLocked bool `json:"is_action_locked"`
|
|
BufferedIo bool `json:"buffered_io"`
|
|
SupportDiscard bool `json:"support_discard"`
|
|
SupportFua bool `json:"support_fua"`
|
|
SubsystemUuid string `json:"subsystem_uuid"`
|
|
}
|
|
|
|
type SubsystemInfo struct {
|
|
Uuid string `json:"uuid"`
|
|
Name string `json:"name"`
|
|
Nqn string `json:"nqn"`
|
|
Status string `json:"status"`
|
|
Ports []string `json:"ports"`
|
|
}
|
|
|
|
type SubsystemCreateSpec struct {
|
|
Name string
|
|
Nqn string
|
|
}
|
|
|
|
type NamespaceCreateSpec struct {
|
|
Name string
|
|
Location string
|
|
Description string
|
|
Size uint64
|
|
ThinProvisioning bool
|
|
Reclaim bool
|
|
}
|
|
|
|
type NamespaceSetSpec struct {
|
|
Uuid string
|
|
NewSize uint64
|
|
}
|
|
|
|
type NamespaceCloneSpec struct {
|
|
Name string
|
|
SrcUuid string
|
|
Location string
|
|
}
|
|
|
|
func sanErrCodeMapping(errCode int, oriErr error) error {
|
|
switch errCode {
|
|
case 28990002: // Out of free space
|
|
return utils.OutOfFreeSpaceError("")
|
|
case 28992055: // No such namespace
|
|
return utils.NoSuchNamespaceError("")
|
|
case 28992056: // No such snapshot
|
|
return utils.NoSuchSnapshotError("")
|
|
case 28992064: // Duplicated name
|
|
return utils.AlreadyExistError("")
|
|
case 28992067: // Reach max count
|
|
return utils.SanReachMaxCountError("")
|
|
case 28992068:
|
|
return utils.SnapshotReachMaxCountError("")
|
|
case 28993012: // Failed to get subsystem
|
|
return utils.FailedToGetSubsystemError("")
|
|
}
|
|
|
|
if errCode >= 28990000 {
|
|
return utils.SanDefaultError{errCode}
|
|
}
|
|
return oriErr
|
|
}
|
|
|
|
// ----------------------- Namespace APIs -----------------------
|
|
func (dsm *DSM) NamespaceCreate(spec NamespaceCreateSpec) (string, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "create")
|
|
params.Add("version", "1")
|
|
params.Add("name", strconv.Quote(spec.Name))
|
|
params.Add("description", strconv.Quote(spec.Description))
|
|
params.Add("location", spec.Location)
|
|
params.Add("size", strconv.FormatInt(int64(spec.Size), 10))
|
|
params.Add("is_thin_provision", strconv.FormatBool(spec.ThinProvisioning))
|
|
params.Add("support_discard", strconv.FormatBool(spec.Reclaim))
|
|
|
|
type NamespaceCreateResp struct {
|
|
Uuid string `json:"uuid"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &NamespaceCreateResp{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return "", sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
namespaceResp, ok := resp.Data.(*NamespaceCreateResp)
|
|
if !ok {
|
|
return "", fmt.Errorf("failed to assert response to %T", &NamespaceCreateResp{})
|
|
}
|
|
|
|
return namespaceResp.Uuid, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceClone(spec NamespaceCloneSpec) (string, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "clone")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(spec.SrcUuid))
|
|
params.Add("dst_namespace_name", strconv.Quote(spec.Name))
|
|
params.Add("dst_location", strconv.Quote(spec.Location))
|
|
|
|
type NamespaceCloneResp struct {
|
|
Uuid string `json:"dst_namespace_uuid"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &NamespaceCloneResp{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return "", sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
info, ok := resp.Data.(*NamespaceCloneResp)
|
|
if !ok {
|
|
return "", fmt.Errorf("Failed to assert response to %T", &NamespaceCloneResp{})
|
|
}
|
|
|
|
return info.Uuid, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceSet(spec NamespaceSetSpec) error {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "set")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(spec.Uuid))
|
|
|
|
if spec.NewSize > 0 {
|
|
params.Add("new_size", strconv.FormatInt(int64(spec.NewSize), 10))
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &struct{}{}, params, "webapi/entry.cgi")
|
|
|
|
return sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceDelete(uuid string) error {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "delete")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(uuid))
|
|
params.Add("is_soft_feas_ignored", strconv.FormatBool(true))
|
|
|
|
resp, err := dsm.sendRequest("", &struct{}{}, params, "webapi/entry.cgi")
|
|
|
|
return sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceGet(uuidOrName string) (*NamespaceInfo, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "get")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(uuidOrName))
|
|
|
|
type NamespaceGetResp struct {
|
|
Namespace NamespaceInfo `json:"namespace"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &NamespaceGetResp{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return nil, sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
info, ok := resp.Data.(*NamespaceGetResp)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to assert response to %T", &NamespaceGetResp{})
|
|
}
|
|
return &info.Namespace, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceList() ([]NamespaceInfo, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "list")
|
|
params.Add("version", "1")
|
|
|
|
type NamespaceInfos struct {
|
|
Namespaces []NamespaceInfo `json:"namespaces"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &NamespaceInfos{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return nil, sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
infos, ok := resp.Data.(*NamespaceInfos)
|
|
if !ok {
|
|
return nil, fmt.Errorf("Failed to assert response to %T", &NamespaceInfos{})
|
|
}
|
|
|
|
return infos.Namespaces, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceSnapshotList(srcUuid string) ([]SnapshotInfo, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "list_snapshot")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(srcUuid))
|
|
|
|
type Infos struct {
|
|
Snapshots []SnapshotInfo `json:"snapshots"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &Infos{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return nil, sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
infos, ok := resp.Data.(*Infos)
|
|
if !ok {
|
|
return nil, fmt.Errorf("Failed to assert response to %T", &Infos{})
|
|
}
|
|
|
|
return infos.Snapshots, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceSnapshotGet(snapshotUuid string) (*SnapshotInfo, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "get_snapshot")
|
|
params.Add("version", "1")
|
|
params.Add("snapshot_uuid", strconv.Quote(snapshotUuid))
|
|
|
|
type Info struct {
|
|
Snapshot SnapshotInfo `json:"snapshot"`
|
|
}
|
|
info := Info{}
|
|
|
|
resp, err := dsm.sendRequest("", &info, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return nil, sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
return &info.Snapshot, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceSnapshotCreate(spec SnapshotCreateSpec) (string, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "take_snapshot")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(spec.SrcUuid))
|
|
params.Add("snapshot_name", strconv.Quote(spec.Name))
|
|
params.Add("description", strconv.Quote(spec.Description))
|
|
params.Add("taken_by", strconv.Quote(spec.TakenBy))
|
|
params.Add("is_user_locked", strconv.FormatBool(spec.IsLocked))
|
|
params.Add("is_app_consistent", strconv.FormatBool(false))
|
|
|
|
type SnapshotCreateResp struct {
|
|
Uuid string `json:"snapshot_uuid"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &SnapshotCreateResp{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return "", sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
snapshotResp, ok := resp.Data.(*SnapshotCreateResp)
|
|
if !ok {
|
|
return "", fmt.Errorf("Failed to assert response to %T", &SnapshotCreateResp{})
|
|
}
|
|
|
|
return snapshotResp.Uuid, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceSnapshotClone(spec SnapshotCloneSpec) (string, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "clone_snapshot")
|
|
params.Add("version", "1")
|
|
params.Add("snapshot_uuid", strconv.Quote(spec.SrcSnapshotUuid))
|
|
params.Add("cloned_namespace_name", strconv.Quote(spec.Name))
|
|
|
|
type SnapshotCloneResp struct {
|
|
Uuid string `json:"cloned_namespace_uuid"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &SnapshotCloneResp{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return "", sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
snapshotCloneResp, ok := resp.Data.(*SnapshotCloneResp)
|
|
if !ok {
|
|
return "", fmt.Errorf("Failed to assert response to %T", &SnapshotCloneResp{})
|
|
}
|
|
|
|
return snapshotCloneResp.Uuid, nil
|
|
}
|
|
|
|
func (dsm *DSM) NamespaceSnapshotDelete(snapshotUuid string) error {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Namespace")
|
|
params.Add("method", "delete_snapshot")
|
|
params.Add("version", "1")
|
|
params.Add("snapshot_uuid", strconv.Quote(snapshotUuid))
|
|
|
|
resp, err := dsm.sendRequest("", &struct{}{}, params, "webapi/entry.cgi")
|
|
|
|
return sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
// ----------------------- Subsystem APIs -----------------------
|
|
func (dsm *DSM) SubsystemCreate(spec SubsystemCreateSpec) (string, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Subsystem")
|
|
params.Add("method", "create")
|
|
params.Add("version", "1")
|
|
params.Add("name", strconv.Quote(spec.Name))
|
|
params.Add("nqn", strconv.Quote(spec.Nqn))
|
|
|
|
type SubsystemCreateResp struct {
|
|
SubsystemUuid string `json:"uuid"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &SubsystemCreateResp{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return "", sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
ssResp, ok := resp.Data.(*SubsystemCreateResp)
|
|
if !ok {
|
|
return "", fmt.Errorf("failed to assert response to %T", &SubsystemCreateResp{})
|
|
}
|
|
|
|
return ssResp.SubsystemUuid, nil
|
|
}
|
|
|
|
func (dsm *DSM) SubsystemGet(uuid string) (*SubsystemInfo, error) {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Subsystem")
|
|
params.Add("method", "get")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(uuid))
|
|
|
|
type SubsystemGetResp struct {
|
|
Subsystem SubsystemInfo `json:"subsystem"`
|
|
}
|
|
|
|
resp, err := dsm.sendRequest("", &SubsystemGetResp{}, params, "webapi/entry.cgi")
|
|
if err != nil {
|
|
return nil, sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
subsystemResp, ok := resp.Data.(*SubsystemGetResp)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to assert response to %T", &SubsystemGetResp{})
|
|
}
|
|
return &subsystemResp.Subsystem, nil
|
|
}
|
|
|
|
func (dsm *DSM) SubsystemDelete(uuid string) error {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Subsystem")
|
|
params.Add("method", "delete")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(uuid))
|
|
|
|
resp, err := dsm.sendRequest("", &struct{}{}, params, "webapi/entry.cgi")
|
|
|
|
return sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|
|
|
|
func (dsm *DSM) SubsystemSetNamespaces(uuid string, namespaceUuids []string) error {
|
|
params := url.Values{}
|
|
params.Add("api", "SYNO.San.Nvme.Subsystem")
|
|
params.Add("method", "set_namespaces")
|
|
params.Add("version", "1")
|
|
params.Add("uuid", strconv.Quote(uuid))
|
|
|
|
js, err := json.Marshal(namespaceUuids)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
params.Add("namespace_uuids", string(js))
|
|
|
|
resp, err := dsm.sendRequest("", &struct{}{}, params, "webapi/entry.cgi")
|
|
|
|
return sanErrCodeMapping(resp.ErrorCode, err)
|
|
}
|