mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-09 16:00:22 +00:00
rebase: vendor ceph/go-ceph/cephfs/admin
The CephFS Admin package has been made available with go-ceph v0.6. This will be used for provisioning and managing CephFS volumes. Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
parent
0f108edc7b
commit
1fcd1ed7c7
68
vendor/github.com/ceph/go-ceph/cephfs/admin/bytecount.go
generated
vendored
Normal file
68
vendor/github.com/ceph/go-ceph/cephfs/admin/bytecount.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ByteCount represents the size of a volume in bytes.
|
||||
type ByteCount uint64
|
||||
|
||||
// SI byte size constants. keep these private for now.
|
||||
const (
|
||||
kibiByte ByteCount = 1024
|
||||
mebiByte = 1024 * kibiByte
|
||||
gibiByte = 1024 * mebiByte
|
||||
tebiByte = 1024 * gibiByte
|
||||
)
|
||||
|
||||
// resizeValue returns a size value as a string, as needed by the subvolume
|
||||
// resize command json.
|
||||
func (bc ByteCount) resizeValue() string {
|
||||
return uint64String(uint64(bc))
|
||||
}
|
||||
|
||||
// QuotaSize interface values can be used to change the size of a volume.
|
||||
type QuotaSize interface {
|
||||
resizeValue() string
|
||||
}
|
||||
|
||||
// specialSize is a custom non-numeric quota size value.
|
||||
type specialSize string
|
||||
|
||||
// resizeValue for a specialSize returns the original string value.
|
||||
func (s specialSize) resizeValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Infinite is a special QuotaSize value that can be used to clear size limits
|
||||
// on a subvolume.
|
||||
const Infinite = specialSize("infinite")
|
||||
|
||||
// quotaSizePlaceholder types are helpful to extract QuotaSize typed values
|
||||
// from JSON responses.
|
||||
type quotaSizePlaceholder struct {
|
||||
Value QuotaSize
|
||||
}
|
||||
|
||||
func (p *quotaSizePlaceholder) UnmarshalJSON(b []byte) error {
|
||||
var val interface{}
|
||||
if err := json.Unmarshal(b, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
if v == string(Infinite) {
|
||||
p.Value = Infinite
|
||||
} else {
|
||||
return fmt.Errorf("quota size: invalid string value: %q", v)
|
||||
}
|
||||
case float64:
|
||||
p.Value = ByteCount(v)
|
||||
default:
|
||||
return fmt.Errorf("quota size: invalid type, string or number required: %v (%T)", val, val)
|
||||
}
|
||||
return nil
|
||||
}
|
136
vendor/github.com/ceph/go-ceph/cephfs/admin/clone.go
generated
vendored
Normal file
136
vendor/github.com/ceph/go-ceph/cephfs/admin/clone.go
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// CloneOptions are used to specify optional values to be used when creating a
|
||||
// new subvolume clone.
|
||||
type CloneOptions struct {
|
||||
TargetGroup string
|
||||
PoolLayout string
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot clone <volume> --group_name=<group> <subvolume> <snapshot> <name> [...]
|
||||
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
|
||||
}
|
||||
return checkCloneResponse(fsa.marshalMgrCommand(m))
|
||||
}
|
||||
|
||||
func checkCloneResponse(res response) error {
|
||||
if strings.HasSuffix(res.status, notProtectedSuffix) {
|
||||
return NotProtectedError{response: res}
|
||||
}
|
||||
return res.noData().End()
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type cloneStatusWrapper struct {
|
||||
Status CloneStatus `json:"status"`
|
||||
}
|
||||
|
||||
func parseCloneStatus(res response) (*CloneStatus, error) {
|
||||
var status cloneStatusWrapper
|
||||
if err := res.noStatus().unmarshal(&status).End(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &status.Status, nil
|
||||
}
|
||||
|
||||
// CloneStatus returns data reporting the status of a subvolume clone.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs clone status <volume> --group_name=<group> <clone>
|
||||
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:
|
||||
// ceph fs clone cancel <volume> --group_name=<group> <clone>
|
||||
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
|
||||
}
|
||||
return fsa.marshalMgrCommand(m).noData().End()
|
||||
}
|
11
vendor/github.com/ceph/go-ceph/cephfs/admin/doc.go
generated
vendored
Normal file
11
vendor/github.com/ceph/go-ceph/cephfs/admin/doc.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
Package admin is a convenience layer to support the administration of
|
||||
CephFS volumes, subvolumes, etc.
|
||||
|
||||
Unlike the cephfs package this API does not map to APIs provided by
|
||||
ceph libraries themselves. This API is not yet stable and is subject
|
||||
to change.
|
||||
|
||||
This package only supports ceph "nautilus" and "octopus" at this time.
|
||||
*/
|
||||
package admin
|
160
vendor/github.com/ceph/go-ceph/cephfs/admin/fsadmin.go
generated
vendored
Normal file
160
vendor/github.com/ceph/go-ceph/cephfs/admin/fsadmin.go
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/ceph/go-ceph/rados"
|
||||
)
|
||||
|
||||
// RadosCommander provides an interface to execute JSON-formatted commands that
|
||||
// allow the cephfs administrative functions to interact with the Ceph cluster.
|
||||
type RadosCommander interface {
|
||||
MgrCommand(buf [][]byte) ([]byte, string, error)
|
||||
MonCommand(buf []byte) ([]byte, string, error)
|
||||
}
|
||||
|
||||
// FSAdmin is used to administrate CephFS within a ceph cluster.
|
||||
type FSAdmin struct {
|
||||
conn RadosCommander
|
||||
}
|
||||
|
||||
// New creates an FSAdmin automatically based on the default ceph
|
||||
// configuration file. If more customization is needed, create a
|
||||
// *rados.Conn as you see fit and use NewFromConn to use that
|
||||
// connection with these administrative functions.
|
||||
func New() (*FSAdmin, error) {
|
||||
conn, err := rados.NewConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = conn.ReadDefaultConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = conn.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewFromConn(conn), nil
|
||||
}
|
||||
|
||||
// NewFromConn creates an FSAdmin management object from a preexisting
|
||||
// rados connection. The existing connection can be rados.Conn or any
|
||||
// type implementing the RadosCommander interface. This may be useful
|
||||
// if the calling layer needs to inject additional logging, error handling,
|
||||
// fault injection, etc.
|
||||
func NewFromConn(conn RadosCommander) *FSAdmin {
|
||||
return &FSAdmin{conn}
|
||||
}
|
||||
|
||||
func (fsa *FSAdmin) validate() error {
|
||||
if fsa.conn == nil {
|
||||
return rados.ErrNotConnected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rawMgrCommand takes a byte buffer and sends it to the MGR as a command.
|
||||
// The buffer is expected to contain preformatted JSON.
|
||||
func (fsa *FSAdmin) rawMgrCommand(buf []byte) response {
|
||||
if err := fsa.validate(); err != nil {
|
||||
return response{err: err}
|
||||
}
|
||||
return newResponse(fsa.conn.MgrCommand([][]byte{buf}))
|
||||
}
|
||||
|
||||
// marshalMgrCommand takes an generic interface{} value, converts it to JSON and
|
||||
// sends the json to the MGR as a command.
|
||||
func (fsa *FSAdmin) marshalMgrCommand(v interface{}) response {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return response{err: err}
|
||||
}
|
||||
return fsa.rawMgrCommand(b)
|
||||
}
|
||||
|
||||
// rawMonCommand takes a byte buffer and sends it to the MON as a command.
|
||||
// The buffer is expected to contain preformatted JSON.
|
||||
func (fsa *FSAdmin) rawMonCommand(buf []byte) response {
|
||||
if err := fsa.validate(); err != nil {
|
||||
return response{err: err}
|
||||
}
|
||||
return newResponse(fsa.conn.MonCommand(buf))
|
||||
}
|
||||
|
||||
// marshalMonCommand takes an generic interface{} value, converts it to JSON and
|
||||
// sends the json to the MGR as a command.
|
||||
func (fsa *FSAdmin) marshalMonCommand(v interface{}) response {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return response{err: err}
|
||||
}
|
||||
return fsa.rawMonCommand(b)
|
||||
}
|
||||
|
||||
type listNamedResult struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func parseListNames(res response) ([]string, error) {
|
||||
var r []listNamedResult
|
||||
if err := res.noStatus().unmarshal(&r).End(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vl := make([]string, len(r))
|
||||
for i := range r {
|
||||
vl[i] = r[i].Name
|
||||
}
|
||||
return vl, nil
|
||||
}
|
||||
|
||||
// parsePathResponse returns a cleaned up path from requests that get a path
|
||||
// unless an error is encountered, then an error is returned.
|
||||
func parsePathResponse(res response) (string, error) {
|
||||
if res2 := res.noStatus(); !res2.Ok() {
|
||||
return "", res.End()
|
||||
}
|
||||
b := res.body
|
||||
// if there's a trailing newline in the buffer strip it.
|
||||
// ceph assumes a CLI wants the output of the buffer and there's
|
||||
// no format=json mode available currently.
|
||||
for len(b) >= 1 && b[len(b)-1] == '\n' {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// modeString converts a unix-style mode value to a string-ified version in an
|
||||
// octal representation (e.g. "777", "700", etc). This format is expected by
|
||||
// some of the ceph JSON command inputs.
|
||||
func modeString(m int, force bool) string {
|
||||
if force || m != 0 {
|
||||
return strconv.FormatInt(int64(m), 8)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// uint64String converts a uint64 to a string. Some of the ceph json commands
|
||||
// can take a string or "int" (as a string). This is a common function for
|
||||
// doing that conversion.
|
||||
func uint64String(v uint64) string {
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
type rmFlags struct {
|
||||
force bool
|
||||
}
|
||||
|
||||
func (f rmFlags) Update(m map[string]string) map[string]interface{} {
|
||||
o := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
o[k] = v
|
||||
}
|
||||
if f.force {
|
||||
o["force"] = true
|
||||
}
|
||||
return o
|
||||
}
|
142
vendor/github.com/ceph/go-ceph/cephfs/admin/response.go
generated
vendored
Normal file
142
vendor/github.com/ceph/go-ceph/cephfs/admin/response.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrStatusNotEmpty may be returned if a call should not have a status
|
||||
// string set but one is.
|
||||
ErrStatusNotEmpty = errors.New("response status not empty")
|
||||
// ErrBodyNotEmpty may be returned if a call should have an empty body but
|
||||
// a body value is present.
|
||||
ErrBodyNotEmpty = errors.New("response body not empty")
|
||||
)
|
||||
|
||||
const (
|
||||
deprecatedSuffix = "call is deprecated and will be removed in a future release"
|
||||
missingPrefix = "No handler found"
|
||||
einval = -22
|
||||
)
|
||||
|
||||
type cephError interface {
|
||||
ErrorCode() int
|
||||
}
|
||||
|
||||
// NotImplementedError error values will be returned in the case that an API
|
||||
// call is not available in the version of Ceph that is running in the target
|
||||
// cluster.
|
||||
type NotImplementedError struct {
|
||||
response
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e NotImplementedError) Error() string {
|
||||
return fmt.Sprintf("API call not implemented server-side: %s", e.status)
|
||||
}
|
||||
|
||||
// response encapsulates the data returned by ceph and supports easy processing
|
||||
// pipelines.
|
||||
type response struct {
|
||||
body []byte
|
||||
status string
|
||||
err error
|
||||
}
|
||||
|
||||
// Ok returns true if the response contains no error.
|
||||
func (r response) Ok() bool {
|
||||
return r.err == nil
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (r response) Error() string {
|
||||
if r.status == "" {
|
||||
return r.err.Error()
|
||||
}
|
||||
return fmt.Sprintf("%s: %q", r.err, r.status)
|
||||
}
|
||||
|
||||
// Unwrap returns the error this response contains.
|
||||
func (r response) Unwrap() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// Status returns the status string value.
|
||||
func (r response) Status() string {
|
||||
return r.status
|
||||
}
|
||||
|
||||
// End returns an error if the response contains an error or nil, indicating
|
||||
// that response is no longer needed for processing.
|
||||
func (r response) End() error {
|
||||
if !r.Ok() {
|
||||
if ce, ok := r.err.(cephError); ok {
|
||||
if ce.ErrorCode() == einval && strings.HasPrefix(r.status, missingPrefix) {
|
||||
return NotImplementedError{response: r}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// noStatus asserts that the input response has no status value.
|
||||
func (r response) noStatus() response {
|
||||
if !r.Ok() {
|
||||
return r
|
||||
}
|
||||
if r.status != "" {
|
||||
return response{r.body, r.status, ErrStatusNotEmpty}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// noBody asserts that the input response has no body value.
|
||||
func (r response) noBody() response {
|
||||
if !r.Ok() {
|
||||
return r
|
||||
}
|
||||
if len(r.body) != 0 {
|
||||
return response{r.body, r.status, ErrBodyNotEmpty}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// noData asserts that the input response has no status or body values.
|
||||
func (r response) noData() response {
|
||||
return r.noStatus().noBody()
|
||||
}
|
||||
|
||||
// filterDeprecated removes deprecation warnings from the response status.
|
||||
// Use it when checking the response from calls that may be deprecated in ceph
|
||||
// if you want those calls to continue working if the warning is present.
|
||||
func (r response) filterDeprecated() response {
|
||||
if !r.Ok() {
|
||||
return r
|
||||
}
|
||||
if strings.HasSuffix(r.status, deprecatedSuffix) {
|
||||
return response{r.body, "", r.err}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// unmarshal data from the response body into v.
|
||||
func (r response) unmarshal(v interface{}) response {
|
||||
if !r.Ok() {
|
||||
return r
|
||||
}
|
||||
if err := json.Unmarshal(r.body, v); err != nil {
|
||||
return response{body: r.body, err: err}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// newResponse returns a response.
|
||||
func newResponse(b []byte, s string, e error) response {
|
||||
return response{b, s, e}
|
||||
}
|
379
vendor/github.com/ceph/go-ceph/cephfs/admin/subvolume.go
generated
vendored
Normal file
379
vendor/github.com/ceph/go-ceph/cephfs/admin/subvolume.go
generated
vendored
Normal file
@ -0,0 +1,379 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
// this is the internal type used to create JSON for ceph.
|
||||
// See SubVolumeOptions for the type that users of the library
|
||||
// interact with.
|
||||
// note that the ceph json takes mode as a string.
|
||||
type subVolumeFields struct {
|
||||
Prefix string `json:"prefix"`
|
||||
Format string `json:"format"`
|
||||
VolName string `json:"vol_name"`
|
||||
GroupName string `json:"group_name,omitempty"`
|
||||
SubName string `json:"sub_name"`
|
||||
Size ByteCount `json:"size,omitempty"`
|
||||
Uid int `json:"uid,omitempty"`
|
||||
Gid int `json:"gid,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
PoolLayout string `json:"pool_layout,omitempty"`
|
||||
NamespaceIsolated bool `json:"namespace_isolated"`
|
||||
}
|
||||
|
||||
// SubVolumeOptions are used to specify optional, non-identifying, values
|
||||
// to be used when creating a new subvolume.
|
||||
type SubVolumeOptions struct {
|
||||
Size ByteCount
|
||||
Uid int
|
||||
Gid int
|
||||
Mode int
|
||||
PoolLayout string
|
||||
NamespaceIsolated bool
|
||||
}
|
||||
|
||||
func (s *SubVolumeOptions) toFields(v, g, n string) *subVolumeFields {
|
||||
return &subVolumeFields{
|
||||
Prefix: "fs subvolume create",
|
||||
Format: "json",
|
||||
VolName: v,
|
||||
GroupName: g,
|
||||
SubName: n,
|
||||
Size: s.Size,
|
||||
Uid: s.Uid,
|
||||
Gid: s.Gid,
|
||||
Mode: modeString(s.Mode, false),
|
||||
PoolLayout: s.PoolLayout,
|
||||
NamespaceIsolated: s.NamespaceIsolated,
|
||||
}
|
||||
}
|
||||
|
||||
// NoGroup should be used when an optional subvolume group name is not
|
||||
// specified.
|
||||
const NoGroup = ""
|
||||
|
||||
// CreateSubVolume sends a request to create a CephFS subvolume in a volume,
|
||||
// belonging to an optional subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume create <volume> --group-name=<group> <name> ...
|
||||
func (fsa *FSAdmin) CreateSubVolume(volume, group, name string, o *SubVolumeOptions) error {
|
||||
if o == nil {
|
||||
o = &SubVolumeOptions{}
|
||||
}
|
||||
f := o.toFields(volume, group, name)
|
||||
return fsa.marshalMgrCommand(f).noData().End()
|
||||
}
|
||||
|
||||
// ListSubVolumes returns a list of subvolumes belonging to the volume and
|
||||
// optional subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume ls <volume> --group-name=<group>
|
||||
func (fsa *FSAdmin) ListSubVolumes(volume, group string) ([]string, error) {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume ls",
|
||||
"vol_name": volume,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return parseListNames(fsa.marshalMgrCommand(m))
|
||||
}
|
||||
|
||||
// RemoveSubVolume will delete a CephFS subvolume in a volume and optional
|
||||
// subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume rm <volume> --group-name=<group> <name>
|
||||
func (fsa *FSAdmin) RemoveSubVolume(volume, group, name string) error {
|
||||
return fsa.rmSubVolume(volume, group, name, rmFlags{})
|
||||
}
|
||||
|
||||
// ForceRemoveSubVolume will delete a CephFS subvolume in a volume and optional
|
||||
// subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume rm <volume> --group-name=<group> <name> --force
|
||||
func (fsa *FSAdmin) ForceRemoveSubVolume(volume, group, name string) error {
|
||||
return fsa.rmSubVolume(volume, group, name, rmFlags{force: true})
|
||||
}
|
||||
|
||||
func (fsa *FSAdmin) rmSubVolume(volume, group, name string, o rmFlags) error {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume rm",
|
||||
"vol_name": volume,
|
||||
"sub_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return fsa.marshalMgrCommand(o.Update(m)).noData().End()
|
||||
}
|
||||
|
||||
type subVolumeResizeFields struct {
|
||||
Prefix string `json:"prefix"`
|
||||
Format string `json:"format"`
|
||||
VolName string `json:"vol_name"`
|
||||
GroupName string `json:"group_name,omitempty"`
|
||||
SubName string `json:"sub_name"`
|
||||
NewSize string `json:"new_size"`
|
||||
NoShrink bool `json:"no_shrink"`
|
||||
}
|
||||
|
||||
// SubVolumeResizeResult reports the size values returned by the
|
||||
// ResizeSubVolume function, as reported by Ceph.
|
||||
type SubVolumeResizeResult struct {
|
||||
BytesUsed ByteCount `json:"bytes_used"`
|
||||
BytesQuota ByteCount `json:"bytes_quota"`
|
||||
BytesPercent string `json:"bytes_pcent"`
|
||||
}
|
||||
|
||||
// ResizeSubVolume will resize a CephFS subvolume. The newSize value may be a
|
||||
// ByteCount or the special Infinite constant. Setting noShrink to true will
|
||||
// prevent reducing the size of the volume below the current used size.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume resize <volume> --group-name=<group> <name> ...
|
||||
func (fsa *FSAdmin) ResizeSubVolume(
|
||||
volume, group, name string,
|
||||
newSize QuotaSize, noShrink bool) (*SubVolumeResizeResult, error) {
|
||||
|
||||
f := &subVolumeResizeFields{
|
||||
Prefix: "fs subvolume resize",
|
||||
Format: "json",
|
||||
VolName: volume,
|
||||
GroupName: group,
|
||||
SubName: name,
|
||||
NewSize: newSize.resizeValue(),
|
||||
NoShrink: noShrink,
|
||||
}
|
||||
var result []*SubVolumeResizeResult
|
||||
res := fsa.marshalMgrCommand(f)
|
||||
if err := res.noStatus().unmarshal(&result).End(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result[0], nil
|
||||
}
|
||||
|
||||
// SubVolumePath returns the path to the subvolume from the root of the file system.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume getpath <volume> --group-name=<group> <name>
|
||||
func (fsa *FSAdmin) SubVolumePath(volume, group, name string) (string, error) {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume getpath",
|
||||
"vol_name": volume,
|
||||
"sub_name": name,
|
||||
// ceph doesn't respond in json for this cmd (even if you ask)
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return parsePathResponse(fsa.marshalMgrCommand(m))
|
||||
}
|
||||
|
||||
// Feature is used to define constant values for optional features on
|
||||
// subvolumes.
|
||||
type Feature string
|
||||
|
||||
const (
|
||||
// SnapshotCloneFeature indicates a subvolume supports cloning.
|
||||
SnapshotCloneFeature = Feature("snapshot-clone")
|
||||
// SnapshotAutoprotectFeature indicates a subvolume does not require
|
||||
// manually protecting a subvolume before cloning.
|
||||
SnapshotAutoprotectFeature = Feature("snapshot-autoprotect")
|
||||
// SnapshotRetentionFeature indicates a subvolume supports retaining
|
||||
// snapshots on subvolume removal.
|
||||
SnapshotRetentionFeature = Feature("snapshot-retention")
|
||||
)
|
||||
|
||||
// SubVolumeInfo reports various informational values about a subvolume.
|
||||
type SubVolumeInfo struct {
|
||||
Type string `json:"type"`
|
||||
Path string `json:"path"`
|
||||
Uid int `json:"uid"`
|
||||
Gid int `json:"gid"`
|
||||
Mode int `json:"mode"`
|
||||
BytesPercent string `json:"bytes_pcent"`
|
||||
BytesUsed ByteCount `json:"bytes_used"`
|
||||
BytesQuota QuotaSize `json:"-"`
|
||||
DataPool string `json:"data_pool"`
|
||||
PoolNamespace string `json:"pool_namespace"`
|
||||
Atime TimeStamp `json:"atime"`
|
||||
Mtime TimeStamp `json:"mtime"`
|
||||
Ctime TimeStamp `json:"ctime"`
|
||||
CreatedAt TimeStamp `json:"created_at"`
|
||||
Features []Feature `json:"features"`
|
||||
}
|
||||
|
||||
type subVolumeInfoWrapper struct {
|
||||
SubVolumeInfo
|
||||
VBytesQuota *quotaSizePlaceholder `json:"bytes_quota"`
|
||||
}
|
||||
|
||||
func parseSubVolumeInfo(res response) (*SubVolumeInfo, error) {
|
||||
var info subVolumeInfoWrapper
|
||||
if err := res.noStatus().unmarshal(&info).End(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.VBytesQuota != nil {
|
||||
info.BytesQuota = info.VBytesQuota.Value
|
||||
}
|
||||
return &info.SubVolumeInfo, nil
|
||||
}
|
||||
|
||||
// SubVolumeInfo returns information about the specified subvolume.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume info <volume> --group-name=<group> <name>
|
||||
func (fsa *FSAdmin) SubVolumeInfo(volume, group, name string) (*SubVolumeInfo, error) {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume info",
|
||||
"vol_name": volume,
|
||||
"sub_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return parseSubVolumeInfo(fsa.marshalMgrCommand(m))
|
||||
}
|
||||
|
||||
// CreateSubVolumeSnapshot creates a new snapshot from the source subvolume.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot create <volume> --group-name=<group> <source> <name>
|
||||
func (fsa *FSAdmin) CreateSubVolumeSnapshot(volume, group, source, name string) error {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume snapshot create",
|
||||
"vol_name": volume,
|
||||
"sub_name": source,
|
||||
"snap_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return fsa.marshalMgrCommand(m).noData().End()
|
||||
}
|
||||
|
||||
// RemoveSubVolumeSnapshot removes the specified snapshot from the subvolume.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot rm <volume> --group-name=<group> <subvolume> <name>
|
||||
func (fsa *FSAdmin) RemoveSubVolumeSnapshot(volume, group, subvolume, name string) error {
|
||||
return fsa.rmSubVolumeSnapshot(volume, group, subvolume, name, rmFlags{})
|
||||
}
|
||||
|
||||
// ForceRemoveSubVolumeSnapshot removes the specified snapshot from the subvolume.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot rm <volume> --group-name=<group> <subvolume> <name> --force
|
||||
func (fsa *FSAdmin) ForceRemoveSubVolumeSnapshot(volume, group, subvolume, name string) error {
|
||||
return fsa.rmSubVolumeSnapshot(volume, group, subvolume, name, rmFlags{force: true})
|
||||
}
|
||||
|
||||
func (fsa *FSAdmin) rmSubVolumeSnapshot(volume, group, subvolume, name string, o rmFlags) error {
|
||||
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume snapshot rm",
|
||||
"vol_name": volume,
|
||||
"sub_name": subvolume,
|
||||
"snap_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return fsa.marshalMgrCommand(o.Update(m)).noData().End()
|
||||
}
|
||||
|
||||
// ListSubVolumeSnapshots returns a listing of snapshots for a given subvolume.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot ls <volume> --group-name=<group> <name>
|
||||
func (fsa *FSAdmin) ListSubVolumeSnapshots(volume, group, name string) ([]string, error) {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume snapshot ls",
|
||||
"vol_name": volume,
|
||||
"sub_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return parseListNames(fsa.marshalMgrCommand(m))
|
||||
}
|
||||
|
||||
// SubVolumeSnapshotInfo reports various informational values about a subvolume.
|
||||
type SubVolumeSnapshotInfo struct {
|
||||
CreatedAt TimeStamp `json:"created_at"`
|
||||
DataPool string `json:"data_pool"`
|
||||
HasPendingClones string `json:"has_pending_clones"`
|
||||
Protected string `json:"protected"`
|
||||
Size ByteCount `json:"size"`
|
||||
}
|
||||
|
||||
func parseSubVolumeSnapshotInfo(res response) (*SubVolumeSnapshotInfo, error) {
|
||||
var info SubVolumeSnapshotInfo
|
||||
if err := res.noStatus().unmarshal(&info).End(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
// SubVolumeSnapshotInfo returns information about the specified subvolume snapshot.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot info <volume> --group-name=<group> <subvolume> <name>
|
||||
func (fsa *FSAdmin) SubVolumeSnapshotInfo(volume, group, subvolume, name string) (*SubVolumeSnapshotInfo, error) {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume snapshot info",
|
||||
"vol_name": volume,
|
||||
"sub_name": subvolume,
|
||||
"snap_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return parseSubVolumeSnapshotInfo(fsa.marshalMgrCommand(m))
|
||||
}
|
||||
|
||||
// ProtectSubVolumeSnapshot protects the specified snapshot.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot protect <volume> --group-name=<group> <subvolume> <name>
|
||||
func (fsa *FSAdmin) ProtectSubVolumeSnapshot(volume, group, subvolume, name string) error {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume snapshot protect",
|
||||
"vol_name": volume,
|
||||
"sub_name": subvolume,
|
||||
"snap_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return fsa.marshalMgrCommand(m).filterDeprecated().noData().End()
|
||||
}
|
||||
|
||||
// UnprotectSubVolumeSnapshot removes protection from the specified snapshot.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolume snapshot unprotect <volume> --group-name=<group> <subvolume> <name>
|
||||
func (fsa *FSAdmin) UnprotectSubVolumeSnapshot(volume, group, subvolume, name string) error {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolume snapshot unprotect",
|
||||
"vol_name": volume,
|
||||
"sub_name": subvolume,
|
||||
"snap_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
if group != NoGroup {
|
||||
m["group_name"] = group
|
||||
}
|
||||
return fsa.marshalMgrCommand(m).filterDeprecated().noData().End()
|
||||
}
|
161
vendor/github.com/ceph/go-ceph/cephfs/admin/subvolumegroup.go
generated
vendored
Normal file
161
vendor/github.com/ceph/go-ceph/cephfs/admin/subvolumegroup.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
// this is the internal type used to create JSON for ceph.
|
||||
// See SubVolumeGroupOptions for the type that users of the library
|
||||
// interact with.
|
||||
// note that the ceph json takes mode as a string.
|
||||
type subVolumeGroupFields struct {
|
||||
Prefix string `json:"prefix"`
|
||||
Format string `json:"format"`
|
||||
VolName string `json:"vol_name"`
|
||||
GroupName string `json:"group_name"`
|
||||
Uid int `json:"uid,omitempty"`
|
||||
Gid int `json:"gid,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
PoolLayout string `json:"pool_layout,omitempty"`
|
||||
}
|
||||
|
||||
// SubVolumeGroupOptions are used to specify optional, non-identifying, values
|
||||
// to be used when creating a new subvolume group.
|
||||
type SubVolumeGroupOptions struct {
|
||||
Uid int
|
||||
Gid int
|
||||
Mode int
|
||||
PoolLayout string
|
||||
}
|
||||
|
||||
func (s *SubVolumeGroupOptions) toFields(v, g string) *subVolumeGroupFields {
|
||||
return &subVolumeGroupFields{
|
||||
Prefix: "fs subvolumegroup create",
|
||||
Format: "json",
|
||||
VolName: v,
|
||||
GroupName: g,
|
||||
Uid: s.Uid,
|
||||
Gid: s.Gid,
|
||||
Mode: modeString(s.Mode, false),
|
||||
PoolLayout: s.PoolLayout,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSubVolumeGroup sends a request to create a subvolume group in a volume.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup create <volume> <group_name> ...
|
||||
func (fsa *FSAdmin) CreateSubVolumeGroup(volume, name string, o *SubVolumeGroupOptions) error {
|
||||
if o == nil {
|
||||
o = &SubVolumeGroupOptions{}
|
||||
}
|
||||
res := fsa.marshalMgrCommand(o.toFields(volume, name))
|
||||
return res.noData().End()
|
||||
}
|
||||
|
||||
// ListSubVolumeGroups returns a list of subvolume groups belonging to the
|
||||
// specified volume.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup ls cephfs <volume>
|
||||
func (fsa *FSAdmin) ListSubVolumeGroups(volume string) ([]string, error) {
|
||||
res := fsa.marshalMgrCommand(map[string]string{
|
||||
"prefix": "fs subvolumegroup ls",
|
||||
"vol_name": volume,
|
||||
"format": "json",
|
||||
})
|
||||
return parseListNames(res)
|
||||
}
|
||||
|
||||
// RemoveSubVolumeGroup will delete a subvolume group in a volume.
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup rm <volume> <group_name>
|
||||
func (fsa *FSAdmin) RemoveSubVolumeGroup(volume, name string) error {
|
||||
return fsa.rmSubVolumeGroup(volume, name, rmFlags{})
|
||||
}
|
||||
|
||||
// ForceRemoveSubVolumeGroup will delete a subvolume group in a volume.
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup rm <volume> <group_name> --force
|
||||
func (fsa *FSAdmin) ForceRemoveSubVolumeGroup(volume, name string) error {
|
||||
return fsa.rmSubVolumeGroup(volume, name, rmFlags{force: true})
|
||||
}
|
||||
|
||||
func (fsa *FSAdmin) rmSubVolumeGroup(volume, name string, o rmFlags) error {
|
||||
res := fsa.marshalMgrCommand(o.Update(map[string]string{
|
||||
"prefix": "fs subvolumegroup rm",
|
||||
"vol_name": volume,
|
||||
"group_name": name,
|
||||
"format": "json",
|
||||
}))
|
||||
return res.noData().End()
|
||||
}
|
||||
|
||||
// SubVolumeGroupPath returns the path to the subvolume from the root of the
|
||||
// file system.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup getpath <volume> <group_name>
|
||||
func (fsa *FSAdmin) SubVolumeGroupPath(volume, name string) (string, error) {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolumegroup getpath",
|
||||
"vol_name": volume,
|
||||
"group_name": name,
|
||||
// ceph doesn't respond in json for this cmd (even if you ask)
|
||||
}
|
||||
return parsePathResponse(fsa.marshalMgrCommand(m))
|
||||
}
|
||||
|
||||
// CreateSubVolumeGroupSnapshot creates a new snapshot from the source subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup snapshot create <volume> <group> <name>
|
||||
func (fsa *FSAdmin) CreateSubVolumeGroupSnapshot(volume, group, name string) error {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolumegroup snapshot create",
|
||||
"vol_name": volume,
|
||||
"group_name": group,
|
||||
"snap_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
return fsa.marshalMgrCommand(m).noData().End()
|
||||
}
|
||||
|
||||
// RemoveSubVolumeGroupSnapshot removes the specified snapshot from the subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup snapshot rm <volume> <group> <name>
|
||||
func (fsa *FSAdmin) RemoveSubVolumeGroupSnapshot(volume, group, name string) error {
|
||||
return fsa.rmSubVolumeGroupSnapshot(volume, group, name, rmFlags{})
|
||||
}
|
||||
|
||||
// ForceRemoveSubVolumeGroupSnapshot removes the specified snapshot from the subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup snapshot rm <volume> <group> <name> --force
|
||||
func (fsa *FSAdmin) ForceRemoveSubVolumeGroupSnapshot(volume, group, name string) error {
|
||||
return fsa.rmSubVolumeGroupSnapshot(volume, group, name, rmFlags{force: true})
|
||||
}
|
||||
|
||||
func (fsa *FSAdmin) rmSubVolumeGroupSnapshot(volume, group, name string, o rmFlags) error {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolumegroup snapshot rm",
|
||||
"vol_name": volume,
|
||||
"group_name": group,
|
||||
"snap_name": name,
|
||||
"format": "json",
|
||||
}
|
||||
return fsa.marshalMgrCommand(o.Update(m)).noData().End()
|
||||
}
|
||||
|
||||
// ListSubVolumeGroupSnapshots returns a listing of snapshots for a given subvolume group.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs subvolumegroup snapshot ls <volume> <group>
|
||||
func (fsa *FSAdmin) ListSubVolumeGroupSnapshots(volume, group string) ([]string, error) {
|
||||
m := map[string]string{
|
||||
"prefix": "fs subvolumegroup snapshot ls",
|
||||
"vol_name": volume,
|
||||
"group_name": group,
|
||||
"format": "json",
|
||||
}
|
||||
return parseListNames(fsa.marshalMgrCommand(m))
|
||||
}
|
39
vendor/github.com/ceph/go-ceph/cephfs/admin/timestamp.go
generated
vendored
Normal file
39
vendor/github.com/ceph/go-ceph/cephfs/admin/timestamp.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// golang's date parsing approach is rather bizarre
|
||||
var cephTSLayout = "2006-01-02 15:04:05"
|
||||
|
||||
// TimeStamp abstracts some of the details about date+time stamps
|
||||
// returned by ceph via JSON.
|
||||
type TimeStamp struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// String returns a string representing the date+time as presented
|
||||
// by ceph.
|
||||
func (ts TimeStamp) String() string {
|
||||
return ts.Format(cephTSLayout)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json Unmarshaler interface.
|
||||
func (ts *TimeStamp) UnmarshalJSON(b []byte) error {
|
||||
var raw string
|
||||
if err := json.Unmarshal(b, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
// AFAICT, ceph always returns the time in UTC so Parse, as opposed to
|
||||
// ParseInLocation, is appropriate here.
|
||||
t, err := time.Parse(cephTSLayout, raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*ts = TimeStamp{t}
|
||||
return nil
|
||||
}
|
152
vendor/github.com/ceph/go-ceph/cephfs/admin/volume.go
generated
vendored
Normal file
152
vendor/github.com/ceph/go-ceph/cephfs/admin/volume.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
// +build !luminous,!mimic
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
var (
|
||||
listVolumesCmd = []byte(`{"prefix":"fs volume ls"}`)
|
||||
dumpVolumesCmd = []byte(`{"prefix":"fs dump","format":"json"}`)
|
||||
listFsCmd = []byte(`{"prefix":"fs ls","format":"json"}`)
|
||||
)
|
||||
|
||||
// ListVolumes return a list of volumes in this Ceph cluster.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs volume ls
|
||||
func (fsa *FSAdmin) ListVolumes() ([]string, error) {
|
||||
res := fsa.rawMgrCommand(listVolumesCmd)
|
||||
return parseListNames(res)
|
||||
}
|
||||
|
||||
// FSPoolInfo contains the name of a file system as well as the metadata and
|
||||
// data pools. Pool information is available by ID or by name.
|
||||
type FSPoolInfo struct {
|
||||
Name string `json:"name"`
|
||||
MetadataPool string `json:"metadata_pool"`
|
||||
MetadataPoolID int `json:"metadata_pool_id"`
|
||||
DataPools []string `json:"data_pools"`
|
||||
DataPoolIDs []int `json:"data_pool_ids"`
|
||||
}
|
||||
|
||||
// ListFileSystems lists file systems along with the pools occupied by those
|
||||
// file systems.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs ls
|
||||
func (fsa *FSAdmin) ListFileSystems() ([]FSPoolInfo, error) {
|
||||
res := fsa.rawMonCommand(listFsCmd)
|
||||
return parseFsList(res)
|
||||
}
|
||||
|
||||
func parseFsList(res response) ([]FSPoolInfo, error) {
|
||||
var listing []FSPoolInfo
|
||||
if err := res.noStatus().unmarshal(&listing).End(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listing, nil
|
||||
}
|
||||
|
||||
// VolumeIdent contains a pair of file system identifying values: the volume
|
||||
// name and the volume ID.
|
||||
type VolumeIdent struct {
|
||||
Name string
|
||||
ID int64
|
||||
}
|
||||
|
||||
type cephFileSystem struct {
|
||||
ID int64 `json:"id"`
|
||||
MDSMap struct {
|
||||
FSName string `json:"fs_name"`
|
||||
} `json:"mdsmap"`
|
||||
}
|
||||
|
||||
type fsDump struct {
|
||||
FileSystems []cephFileSystem `json:"filesystems"`
|
||||
}
|
||||
|
||||
const (
|
||||
dumpOkPrefix = "dumped fsmap epoch"
|
||||
dumpOkLen = len(dumpOkPrefix)
|
||||
|
||||
invalidTextualResponse = "this ceph version returns a non-parsable volume status response"
|
||||
)
|
||||
|
||||
func parseDumpToIdents(res response) ([]VolumeIdent, error) {
|
||||
if !res.Ok() {
|
||||
return nil, res.End()
|
||||
}
|
||||
if len(res.status) >= dumpOkLen && res.status[:dumpOkLen] == dumpOkPrefix {
|
||||
// Unhelpfully, ceph drops a status string on success responses for this
|
||||
// call. this hacks around that by ignoring its typical prefix
|
||||
res.status = ""
|
||||
}
|
||||
var dump fsDump
|
||||
if err := res.noStatus().unmarshal(&dump).End(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// copy the dump json into the simpler enumeration list
|
||||
idents := make([]VolumeIdent, len(dump.FileSystems))
|
||||
for i := range dump.FileSystems {
|
||||
idents[i].ID = dump.FileSystems[i].ID
|
||||
idents[i].Name = dump.FileSystems[i].MDSMap.FSName
|
||||
}
|
||||
return idents, nil
|
||||
}
|
||||
|
||||
// EnumerateVolumes returns a list of volume-name volume-id pairs.
|
||||
func (fsa *FSAdmin) EnumerateVolumes() ([]VolumeIdent, error) {
|
||||
// We base our enumeration on the ceph fs dump json. This may not be the
|
||||
// only way to do it, but it's the only one I know of currently. Because of
|
||||
// this and to keep our initial implementation simple, we expose our own
|
||||
// simplified type only, rather do a partial implementation of dump.
|
||||
return parseDumpToIdents(fsa.rawMonCommand(dumpVolumesCmd))
|
||||
}
|
||||
|
||||
// VolumePool reports on the pool status for a CephFS volume.
|
||||
type VolumePool struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Available uint64 `json:"avail"`
|
||||
Used uint64 `json:"used"`
|
||||
}
|
||||
|
||||
// VolumeStatus reports various properties of a CephFS volume.
|
||||
// TODO: Fill in.
|
||||
type VolumeStatus struct {
|
||||
MDSVersion string `json:"mds_version"`
|
||||
Pools []VolumePool `json:"pools"`
|
||||
}
|
||||
|
||||
func parseVolumeStatus(res response) (*VolumeStatus, error) {
|
||||
var vs VolumeStatus
|
||||
res = res.noStatus()
|
||||
if !res.Ok() {
|
||||
return nil, res.End()
|
||||
}
|
||||
res = res.unmarshal(&vs)
|
||||
if !res.Ok() {
|
||||
if bytes.HasPrefix(res.body, []byte("ceph")) {
|
||||
res.status = invalidTextualResponse
|
||||
return nil, NotImplementedError{response: res}
|
||||
}
|
||||
return nil, res.End()
|
||||
}
|
||||
return &vs, nil
|
||||
}
|
||||
|
||||
// VolumeStatus returns a VolumeStatus object for the given volume name.
|
||||
//
|
||||
// Similar To:
|
||||
// ceph fs status cephfs <name>
|
||||
func (fsa *FSAdmin) VolumeStatus(name string) (*VolumeStatus, error) {
|
||||
res := fsa.marshalMgrCommand(map[string]string{
|
||||
"fs": name,
|
||||
"prefix": "fs status",
|
||||
"format": "json",
|
||||
})
|
||||
return parseVolumeStatus(res)
|
||||
}
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -3,6 +3,7 @@ github.com/beorn7/perks/quantile
|
||||
# github.com/blang/semver v3.5.0+incompatible
|
||||
github.com/blang/semver
|
||||
# github.com/ceph/go-ceph v0.6.0
|
||||
github.com/ceph/go-ceph/cephfs/admin
|
||||
github.com/ceph/go-ceph/internal/callbacks
|
||||
github.com/ceph/go-ceph/internal/cutil
|
||||
github.com/ceph/go-ceph/internal/errutil
|
||||
|
Loading…
Reference in New Issue
Block a user