2020-09-17 21:10:47 +00:00
|
|
|
package admin
|
|
|
|
|
2020-09-21 21:29:55 +00:00
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
const notProtectedSuffix = "is not protected"
|
|
|
|
|
|
|
|
// NotProtectedError error values will be returned by CloneSubVolumeSnapshot in
|
|
|
|
// the case that the source snapshot needs to be protected but is not. The
|
|
|
|
// requirement for a snapshot to be protected prior to cloning varies by Ceph
|
|
|
|
// version.
|
|
|
|
type NotProtectedError struct {
|
|
|
|
response
|
|
|
|
}
|
|
|
|
|
2020-09-17 21:10:47 +00:00
|
|
|
// CloneOptions are used to specify optional values to be used when creating a
|
|
|
|
// new subvolume clone.
|
|
|
|
type CloneOptions struct {
|
|
|
|
TargetGroup string
|
|
|
|
PoolLayout string
|
|
|
|
}
|
|
|
|
|
2020-10-05 20:05:32 +00:00
|
|
|
// CloneSubVolumeSnapshot clones the specified snapshot from the subvolume.
|
|
|
|
// The group, subvolume, and snapshot parameters specify the source for the
|
|
|
|
// clone, and only the source. Additional properties of the clone, such as the
|
|
|
|
// subvolume group that the clone will be created in and the pool layout may be
|
|
|
|
// specified using the clone options parameter.
|
2020-09-17 21:10:47 +00:00
|
|
|
//
|
|
|
|
// Similar To:
|
2023-02-09 17:23:11 +00:00
|
|
|
//
|
|
|
|
// ceph fs subvolume snapshot clone <volume> --group_name=<group> <subvolume> <snapshot> <name> [...]
|
2020-09-17 21:10:47 +00:00
|
|
|
func (fsa *FSAdmin) CloneSubVolumeSnapshot(volume, group, subvolume, snapshot, name string, o *CloneOptions) error {
|
|
|
|
m := map[string]string{
|
|
|
|
"prefix": "fs subvolume snapshot clone",
|
|
|
|
"vol_name": volume,
|
|
|
|
"sub_name": subvolume,
|
|
|
|
"snap_name": snapshot,
|
|
|
|
"target_sub_name": name,
|
|
|
|
"format": "json",
|
|
|
|
}
|
|
|
|
if group != NoGroup {
|
|
|
|
m["group_name"] = group
|
|
|
|
}
|
|
|
|
if o != nil && o.TargetGroup != NoGroup {
|
|
|
|
m["target_group_name"] = group
|
|
|
|
}
|
|
|
|
if o != nil && o.PoolLayout != "" {
|
|
|
|
m["pool_layout"] = o.PoolLayout
|
|
|
|
}
|
2020-09-21 21:29:55 +00:00
|
|
|
return checkCloneResponse(fsa.marshalMgrCommand(m))
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkCloneResponse(res response) error {
|
2021-03-03 19:45:21 +00:00
|
|
|
if strings.HasSuffix(res.Status(), notProtectedSuffix) {
|
2020-09-21 21:29:55 +00:00
|
|
|
return NotProtectedError{response: res}
|
|
|
|
}
|
2021-03-03 19:45:21 +00:00
|
|
|
return res.NoData().End()
|
2020-09-17 21:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CloneState is used to define constant values used to determine the state of
|
|
|
|
// a clone.
|
|
|
|
type CloneState string
|
|
|
|
|
|
|
|
const (
|
|
|
|
// ClonePending is the state of a pending clone.
|
|
|
|
ClonePending = CloneState("pending")
|
|
|
|
// CloneInProgress is the state of a clone in progress.
|
|
|
|
CloneInProgress = CloneState("in-progress")
|
|
|
|
// CloneComplete is the state of a complete clone.
|
|
|
|
CloneComplete = CloneState("complete")
|
|
|
|
// CloneFailed is the state of a failed clone.
|
|
|
|
CloneFailed = CloneState("failed")
|
|
|
|
)
|
|
|
|
|
|
|
|
// CloneSource contains values indicating the source of a clone.
|
|
|
|
type CloneSource struct {
|
|
|
|
Volume string `json:"volume"`
|
|
|
|
Group string `json:"group"`
|
|
|
|
SubVolume string `json:"subvolume"`
|
|
|
|
Snapshot string `json:"snapshot"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloneStatus reports on the status of a subvolume clone.
|
|
|
|
type CloneStatus struct {
|
|
|
|
State CloneState `json:"state"`
|
|
|
|
Source CloneSource `json:"source"`
|
2022-05-30 07:51:22 +00:00
|
|
|
|
|
|
|
// failure can be obtained through .GetFailure()
|
|
|
|
failure *CloneFailure
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloneFailure reports details of a failure after a subvolume clone failed.
|
|
|
|
type CloneFailure struct {
|
|
|
|
Errno string `json:"errno"`
|
|
|
|
ErrStr string `json:"errstr"`
|
2020-09-17 21:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type cloneStatusWrapper struct {
|
2022-05-30 07:51:22 +00:00
|
|
|
Status CloneStatus `json:"status"`
|
|
|
|
Failure CloneFailure `json:"failure"`
|
2020-09-17 21:10:47 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 18:53:29 +00:00
|
|
|
func parseCloneStatus(res response) (*CloneStatus, error) {
|
2020-09-17 21:10:47 +00:00
|
|
|
var status cloneStatusWrapper
|
2021-03-03 19:45:21 +00:00
|
|
|
if err := res.NoStatus().Unmarshal(&status).End(); err != nil {
|
2020-09-17 21:10:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-30 07:51:22 +00:00
|
|
|
if status.Failure.Errno != "" || status.Failure.ErrStr != "" {
|
|
|
|
status.Status.failure = &status.Failure
|
|
|
|
}
|
2020-09-17 21:10:47 +00:00
|
|
|
return &status.Status, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloneStatus returns data reporting the status of a subvolume clone.
|
|
|
|
//
|
|
|
|
// Similar To:
|
2023-02-09 17:23:11 +00:00
|
|
|
//
|
|
|
|
// ceph fs clone status <volume> --group_name=<group> <clone>
|
2020-09-17 21:10:47 +00:00
|
|
|
func (fsa *FSAdmin) CloneStatus(volume, group, clone string) (*CloneStatus, error) {
|
|
|
|
m := map[string]string{
|
|
|
|
"prefix": "fs clone status",
|
|
|
|
"vol_name": volume,
|
|
|
|
"clone_name": clone,
|
|
|
|
"format": "json",
|
|
|
|
}
|
|
|
|
if group != NoGroup {
|
|
|
|
m["group_name"] = group
|
|
|
|
}
|
|
|
|
return parseCloneStatus(fsa.marshalMgrCommand(m))
|
|
|
|
}
|
|
|
|
|
|
|
|
// CancelClone stops the background processes that populate a clone.
|
|
|
|
// CancelClone does not delete the clone.
|
|
|
|
//
|
|
|
|
// Similar To:
|
2023-02-09 17:23:11 +00:00
|
|
|
//
|
|
|
|
// ceph fs clone cancel <volume> --group_name=<group> <clone>
|
2020-09-17 21:10:47 +00:00
|
|
|
func (fsa *FSAdmin) CancelClone(volume, group, clone string) error {
|
|
|
|
m := map[string]string{
|
|
|
|
"prefix": "fs clone cancel",
|
|
|
|
"vol_name": volume,
|
|
|
|
"clone_name": clone,
|
|
|
|
"format": "json",
|
|
|
|
}
|
|
|
|
if group != NoGroup {
|
|
|
|
m["group_name"] = group
|
|
|
|
}
|
2021-03-03 19:45:21 +00:00
|
|
|
return fsa.marshalMgrCommand(m).NoData().End()
|
2020-09-17 21:10:47 +00:00
|
|
|
}
|