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.GreaterOrEqual(t, len(vl), 1) assert.Contains(t, vl, "cephfs") } func TestEnumerateVolumes(t *testing.T) { fsa := getFSAdmin(t) ve, err := fsa.EnumerateVolumes() assert.NoError(t, err) found := false for i := range ve { if ve[i].Name == "cephfs" { assert.Equal(t, int64(1), ve[i].ID) found = true break } } assert.True(t, found, "never found a cephfs entry in enumerated volumes") } // 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) assert.GreaterOrEqual(t, len(l), 1) idx := -1 for i := range l { if l[i].Name == "cephfs" { idx = i break } } if assert.NotEqual(t, -1, idx) { assert.Equal(t, "cephfs", l[idx].Name) assert.Equal(t, "cephfs_metadata", l[idx].MetadataPool) assert.Len(t, l[idx].DataPools, 1) assert.Contains(t, l[idx].DataPools, "cephfs_data") } }