//go:build !luminous && !mimic // +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() } var dump fsDump if err := res.FilterPrefix(dumpOkPrefix).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")) { return nil, NotImplementedError{ Response: newResponse(res.Body(), invalidTextualResponse, res.Unwrap()), } } return nil, res.End() } return &vs, nil } // VolumeStatus returns a VolumeStatus object for the given volume name. // // Similar To: // ceph fs status cephfs func (fsa *FSAdmin) VolumeStatus(name string) (*VolumeStatus, error) { res := fsa.marshalMgrCommand(map[string]string{ "fs": name, "prefix": "fs status", "format": "json", }) return parseVolumeStatus(res) }