mirror of
https://github.com/ceph/go-ceph
synced 2025-01-29 09:32:47 +00:00
ae44f69791
It appears the JSON being returned from `ceph fs status` has changed. The field returning the mds version was previously "just" a string. Now its a more complex thing. This of course breaks the old version of the go code which is statically typed to expect a string. In order not to break backwards compatibility we replace the object used for parsing the JSON with a private object that has a custom unmarshaller function for the mds version field. If it sees the string it uses the string. Otherwise, it looks for this new structure and, if possible, extracts the version string from the first item. Signed-off-by: John Mulligan <jmulligan@redhat.com>
394 lines
11 KiB
Go
394 lines
11 KiB
Go
package admin
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestListVolumes(t *testing.T) {
|
|
fsa := getFSAdmin(t)
|
|
|
|
vl, err := fsa.ListVolumes()
|
|
assert.NoError(t, err)
|
|
assert.Len(t, vl, 1)
|
|
assert.Equal(t, "cephfs", vl[0])
|
|
}
|
|
|
|
func TestEnumerateVolumes(t *testing.T) {
|
|
fsa := getFSAdmin(t)
|
|
|
|
ve, err := fsa.EnumerateVolumes()
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, ve, 1) {
|
|
assert.Equal(t, "cephfs", ve[0].Name)
|
|
assert.Equal(t, int64(1), ve[0].ID)
|
|
}
|
|
}
|
|
|
|
// note: some of these dumps are simplified for testing purposes if we add
|
|
// general dump support these samples may need to be expanded upon.
|
|
var sampleDump1 = []byte(`
|
|
{
|
|
"epoch": 5,
|
|
"default_fscid": 1,
|
|
"filesystems": [
|
|
{
|
|
"mdsmap": {
|
|
"epoch": 5,
|
|
"flags": 18,
|
|
"ever_allowed_features": 0,
|
|
"explicitly_allowed_features": 0,
|
|
"created": "2020-08-31T18:37:34.657633+0000",
|
|
"modified": "2020-08-31T18:37:36.700989+0000",
|
|
"tableserver": 0,
|
|
"root": 0,
|
|
"session_timeout": 60,
|
|
"session_autoclose": 300,
|
|
"min_compat_client": "0 (unknown)",
|
|
"max_file_size": 1099511627776,
|
|
"last_failure": 0,
|
|
"last_failure_osd_epoch": 0,
|
|
"compat": {
|
|
"compat": {},
|
|
"ro_compat": {},
|
|
"incompat": {
|
|
"feature_1": "base v0.20",
|
|
"feature_2": "client writeable ranges",
|
|
"feature_3": "default file layouts on dirs",
|
|
"feature_4": "dir inode in separate object",
|
|
"feature_5": "mds uses versioned encoding",
|
|
"feature_6": "dirfrag is stored in omap",
|
|
"feature_8": "no anchor table",
|
|
"feature_9": "file layout v2",
|
|
"feature_10": "snaprealm v2"
|
|
}
|
|
},
|
|
"max_mds": 1,
|
|
"in": [
|
|
0
|
|
],
|
|
"up": {
|
|
"mds_0": 4115
|
|
},
|
|
"failed": [],
|
|
"damaged": [],
|
|
"stopped": [],
|
|
"info": {
|
|
"gid_4115": {
|
|
"gid": 4115,
|
|
"name": "Z",
|
|
"rank": 0,
|
|
"incarnation": 4,
|
|
"state": "up:active",
|
|
"state_seq": 2,
|
|
"addr": "127.0.0.1:6809/2568111595",
|
|
"addrs": {
|
|
"addrvec": [
|
|
{
|
|
"type": "v1",
|
|
"addr": "127.0.0.1:6809",
|
|
"nonce": 2568111595
|
|
}
|
|
]
|
|
},
|
|
"join_fscid": -1,
|
|
"export_targets": [],
|
|
"features": 4540138292836696000,
|
|
"flags": 0
|
|
}
|
|
},
|
|
"data_pools": [
|
|
1
|
|
],
|
|
"metadata_pool": 2,
|
|
"enabled": true,
|
|
"fs_name": "cephfs",
|
|
"balancer": "",
|
|
"standby_count_wanted": 0
|
|
},
|
|
"id": 1
|
|
}
|
|
]
|
|
}
|
|
`)
|
|
|
|
var sampleDump2 = []byte(`
|
|
{
|
|
"epoch": 5,
|
|
"default_fscid": 1,
|
|
"filesystems": [
|
|
{
|
|
"mdsmap": {
|
|
"fs_name": "wiffleball",
|
|
"standby_count_wanted": 0
|
|
},
|
|
"id": 1
|
|
},
|
|
{
|
|
"mdsmap": {
|
|
"fs_name": "beanbag",
|
|
"standby_count_wanted": 0
|
|
},
|
|
"id": 2
|
|
}
|
|
]
|
|
}
|
|
`)
|
|
|
|
func TestParseDumpToIdents(t *testing.T) {
|
|
R := newResponse
|
|
fakePrefix := dumpOkPrefix + " 5"
|
|
t.Run("error", func(t *testing.T) {
|
|
idents, err := parseDumpToIdents(R(nil, "", errors.New("boop")))
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "boop", err.Error())
|
|
assert.Nil(t, idents)
|
|
})
|
|
t.Run("badStatus", func(t *testing.T) {
|
|
_, err := parseDumpToIdents(R(sampleDump1, "unexpected!", nil))
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("oneVolOk", func(t *testing.T) {
|
|
idents, err := parseDumpToIdents(R(sampleDump1, fakePrefix, nil))
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, idents, 1) {
|
|
assert.Equal(t, "cephfs", idents[0].Name)
|
|
assert.Equal(t, int64(1), idents[0].ID)
|
|
}
|
|
})
|
|
t.Run("twoVolOk", func(t *testing.T) {
|
|
idents, err := parseDumpToIdents(R(sampleDump2, fakePrefix, nil))
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, idents, 2) {
|
|
assert.Equal(t, "wiffleball", idents[0].Name)
|
|
assert.Equal(t, int64(1), idents[0].ID)
|
|
assert.Equal(t, "beanbag", idents[1].Name)
|
|
assert.Equal(t, int64(2), idents[1].ID)
|
|
}
|
|
})
|
|
t.Run("unexpectedStatus", func(t *testing.T) {
|
|
idents, err := parseDumpToIdents(R(sampleDump1, "slip-up", nil))
|
|
assert.Error(t, err)
|
|
assert.Nil(t, idents)
|
|
})
|
|
}
|
|
|
|
func TestVolumeStatus(t *testing.T) {
|
|
if serverVersion == cephNautilus {
|
|
t.Skipf("can only execute on octopus/pacific servers")
|
|
}
|
|
fsa := getFSAdmin(t)
|
|
|
|
vs, err := fsa.VolumeStatus("cephfs")
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, vs.MDSVersion, "version")
|
|
}
|
|
|
|
func TestVolumeStatusInvalid(t *testing.T) {
|
|
if serverVersion != cephNautilus {
|
|
t.Skipf("can only excecute on nautilus servers")
|
|
}
|
|
fsa := getFSAdmin(t)
|
|
|
|
vs, err := fsa.VolumeStatus("cephfs")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, vs)
|
|
var notImpl NotImplementedError
|
|
assert.True(t, errors.As(err, ¬Impl))
|
|
}
|
|
|
|
var sampleVolumeStatus1 = []byte(`
|
|
{
|
|
"clients": [{"clients": 1, "fs": "cephfs"}],
|
|
"mds_version": "ceph version 15.2.4 (7447c15c6ff58d7fce91843b705a268a1917325c) octopus (stable)",
|
|
"mdsmap": [{"dns": 76, "inos": 19, "name": "Z", "rank": 0, "rate": 0.0, "state": "active"}],
|
|
"pools": [{"avail": 1017799872, "id": 2, "name": "cephfs_metadata", "type": "metadata", "used": 2204126}, {"avail": 1017799872, "id": 1, "name": "cephfs_data", "type": "data", "used": 0}]
|
|
}
|
|
`)
|
|
|
|
var sampleVolumeStatusQ = []byte(`
|
|
{"clients": [{"clients": 3, "fs": "cephfs"}], "mds_version": [{"daemon": ["Z"], "version": "ceph version 17.1.0 (c675060073a05d40ef404d5921c81178a52af6e0) quincy (dev)"}], "mdsmap": [{"caps": 11, "dirs": 26, "dns": 49, "inos": 30, "name": "Z", "rank": 0, "rate": 0, "state": "active"}], "pools": [{"avail": 1018405056, "id": 2, "name": "cephfs_metadata", "type": "metadata", "used": 467690}, {"avail": 1018405056, "id": 1, "name": "cephfs_data", "type": "data", "used": 8}]}
|
|
`)
|
|
|
|
var sampleVolumeStatusTextJunk = []byte(`cephfs - 2 clients
|
|
======
|
|
+------+--------+-----+---------------+-------+-------+
|
|
| Rank | State | MDS | Activity | dns | inos |
|
|
+------+--------+-----+---------------+-------+-------+
|
|
| 0 | active | Z | Reqs: 98 /s | 254 | 192 |
|
|
+------+--------+-----+---------------+-------+-------+
|
|
+-----------------+----------+-------+-------+
|
|
| Pool | type | used | avail |
|
|
+-----------------+----------+-------+-------+
|
|
| cephfs_metadata | metadata | 62.1M | 910M |
|
|
| cephfs_data | data | 0 | 910M |
|
|
+-----------------+----------+-------+-------+
|
|
+-------------+
|
|
| Standby MDS |
|
|
+-------------+
|
|
+-------------+
|
|
MDS version: ceph version 14.2.11 (f7fdb2f52131f54b891a2ec99d8205561242cdaf) nautilus (stable)
|
|
`)
|
|
|
|
func TestParseVolumeStatus(t *testing.T) {
|
|
R := newResponse
|
|
t.Run("error", func(t *testing.T) {
|
|
_, err := parseVolumeStatus(R(nil, "", errors.New("bonk")))
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "bonk", err.Error())
|
|
})
|
|
t.Run("statusSet", func(t *testing.T) {
|
|
_, err := parseVolumeStatus(R(nil, "unexpected!", nil))
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("badJSON", func(t *testing.T) {
|
|
_, err := parseVolumeStatus(R([]byte("_XxXxX"), "", nil))
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("ok", func(t *testing.T) {
|
|
v, err := parseVolumeStatus(R(sampleVolumeStatus1, "", nil))
|
|
assert.NoError(t, err)
|
|
s := v.volumeStatus()
|
|
if assert.NotNil(t, s) {
|
|
assert.Contains(t, s.MDSVersion, "ceph version 15.2.4")
|
|
assert.Contains(t, s.MDSVersion, "octopus")
|
|
}
|
|
})
|
|
t.Run("notJSONfromServer", func(t *testing.T) {
|
|
_, err := parseVolumeStatus(R(sampleVolumeStatusTextJunk, "", nil))
|
|
if assert.Error(t, err) {
|
|
var notImpl NotImplementedError
|
|
assert.True(t, errors.As(err, ¬Impl))
|
|
}
|
|
})
|
|
t.Run("quincy", func(t *testing.T) {
|
|
v, err := parseVolumeStatus(R(sampleVolumeStatusQ, "", nil))
|
|
assert.NoError(t, err)
|
|
s := v.volumeStatus()
|
|
if assert.NotNil(t, s) {
|
|
assert.Contains(t, s.MDSVersion, "ceph version 17.1.0")
|
|
assert.Contains(t, s.MDSVersion, "quincy")
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
var sampleFsLs1 = []byte(`
|
|
[
|
|
{
|
|
"name": "cephfs",
|
|
"metadata_pool": "cephfs_metadata",
|
|
"metadata_pool_id": 2,
|
|
"data_pool_ids": [
|
|
1
|
|
],
|
|
"data_pools": [
|
|
"cephfs_data"
|
|
]
|
|
}
|
|
]
|
|
`)
|
|
|
|
var sampleFsLs2 = []byte(`
|
|
[
|
|
{
|
|
"name": "cephfs",
|
|
"metadata_pool": "cephfs_metadata",
|
|
"metadata_pool_id": 2,
|
|
"data_pool_ids": [
|
|
1
|
|
],
|
|
"data_pools": [
|
|
"cephfs_data"
|
|
]
|
|
},
|
|
{
|
|
"name": "archivefs",
|
|
"metadata_pool": "archivefs_metadata",
|
|
"metadata_pool_id": 6,
|
|
"data_pool_ids": [
|
|
4,
|
|
5
|
|
],
|
|
"data_pools": [
|
|
"archivefs_data1",
|
|
"archivefs_data2"
|
|
]
|
|
}
|
|
]
|
|
`)
|
|
|
|
func TestParseFsList(t *testing.T) {
|
|
t.Run("error", func(t *testing.T) {
|
|
_, err := parseFsList(
|
|
newResponse(nil, "", errors.New("eek")))
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "eek", err.Error())
|
|
})
|
|
t.Run("statusSet", func(t *testing.T) {
|
|
_, err := parseFsList(
|
|
newResponse(nil, "oof", nil))
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("badJSON", func(t *testing.T) {
|
|
_, err := parseFsList(
|
|
newResponse([]byte("______"), "", nil))
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("ok1", func(t *testing.T) {
|
|
l, err := parseFsList(
|
|
newResponse(sampleFsLs1, "", nil))
|
|
assert.NoError(t, err)
|
|
if assert.NotNil(t, l) && assert.Len(t, l, 1) {
|
|
fs := l[0]
|
|
assert.Equal(t, "cephfs", fs.Name)
|
|
assert.Equal(t, "cephfs_metadata", fs.MetadataPool)
|
|
assert.Equal(t, 2, fs.MetadataPoolID)
|
|
assert.Len(t, fs.DataPools, 1)
|
|
assert.Contains(t, fs.DataPools, "cephfs_data")
|
|
assert.Len(t, fs.DataPoolIDs, 1)
|
|
assert.Contains(t, fs.DataPoolIDs, 1)
|
|
}
|
|
})
|
|
t.Run("ok2", func(t *testing.T) {
|
|
l, err := parseFsList(
|
|
newResponse(sampleFsLs2, "", nil))
|
|
assert.NoError(t, err)
|
|
if assert.NotNil(t, l) && assert.Len(t, l, 2) {
|
|
fs := l[0]
|
|
assert.Equal(t, "cephfs", fs.Name)
|
|
assert.Equal(t, "cephfs_metadata", fs.MetadataPool)
|
|
assert.Equal(t, 2, fs.MetadataPoolID)
|
|
assert.Len(t, fs.DataPools, 1)
|
|
assert.Contains(t, fs.DataPools, "cephfs_data")
|
|
assert.Len(t, fs.DataPoolIDs, 1)
|
|
assert.Contains(t, fs.DataPoolIDs, 1)
|
|
fs = l[1]
|
|
assert.Equal(t, "archivefs", fs.Name)
|
|
assert.Equal(t, "archivefs_metadata", fs.MetadataPool)
|
|
assert.Equal(t, 6, fs.MetadataPoolID)
|
|
assert.Len(t, fs.DataPools, 2)
|
|
assert.Contains(t, fs.DataPools, "archivefs_data1")
|
|
assert.Contains(t, fs.DataPools, "archivefs_data2")
|
|
assert.Len(t, fs.DataPoolIDs, 2)
|
|
assert.Contains(t, fs.DataPoolIDs, 4)
|
|
assert.Contains(t, fs.DataPoolIDs, 5)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestListFileSystems(t *testing.T) {
|
|
fsa := getFSAdmin(t)
|
|
|
|
l, err := fsa.ListFileSystems()
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, l, 1) {
|
|
assert.Equal(t, "cephfs", l[0].Name)
|
|
assert.Equal(t, "cephfs_metadata", l[0].MetadataPool)
|
|
assert.Len(t, l[0].DataPools, 1)
|
|
assert.Contains(t, l[0].DataPools, "cephfs_data")
|
|
}
|
|
}
|