Skip to content

Commit

Permalink
ceph: improve upgrade procedure
Browse files Browse the repository at this point in the history
When a cluster is updated with a different image version, this triggers
a serialized restart of all the pods. Prior to this commit, no safety
check were performed and rook was hoping for the best outcome.

Now before doing restarting a daemon we check it can be restarted. Once
it's restarted we also check we can pursue with the rest of the
platform. For instance, with monitors we check that they are in quorum,
for OSD we check that PGs are clean and for MDS we make sure they are
 all active.

Fixes: rook#2889
Signed-off-by: Sébastien Han <seb@redhat.com>
  • Loading branch information
leseb committed Jul 4, 2019
1 parent 71e3eef commit d10b4e8
Show file tree
Hide file tree
Showing 13 changed files with 560 additions and 49 deletions.
65 changes: 65 additions & 0 deletions pkg/daemon/ceph/client/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type CephStatus struct {
} `json:"osdmap"`
PgMap PgMap `json:"pgmap"`
MgrMap MgrMap `json:"mgrmap"`
Fsmap Fsmap `json:"fsmap"`
}

type HealthStatus struct {
Expand Down Expand Up @@ -123,6 +124,23 @@ type PgStateEntry struct {
Count int `json:"count"`
}

// Fsmap is a struct representing the filesystem map
type Fsmap struct {
Epoch int `json:"epoch"`
ID int `json:"id"`
Up int `json:"up"`
In int `json:"in"`
Max int `json:"max"`
ByRank []struct {
FilesystemID int `json:"filesystem_id"`
Rank int `json:"rank"`
Name string `json:"name"`
Status string `json:"status"`
Gid int `json:"gid"`
} `json:"by_rank"`
UpStandby int `json:"up:standby"`
}

func Status(context *clusterd.Context, clusterName string, debug bool) (CephStatus, error) {
args := []string{"status"}
cmd := NewCephCommand(context, clusterName, args)
Expand Down Expand Up @@ -171,3 +189,50 @@ func isClusterClean(status CephStatus) error {

return fmt.Errorf("cluster is not fully clean. PGs: %+v", status.PgMap.PgsByState)
}

// getMDSRank returns the rank of a given MDS
func getMDSRank(status CephStatus, clusterName, fsName string) (int, error) {
// dummy rank
mdsRank := -1000
for r := range status.Fsmap.ByRank {
if status.Fsmap.ByRank[r].Name == fsName {
mdsRank = r
}
}
// if the mds is not shown in the map one reason might be because it's in standby
// if not in standby there is something else going wron
if mdsRank < 0 && status.Fsmap.UpStandby < 1 {
// it might seem strange to log an error since this could be a warning too
// it is a warning until we reach the timeout, this should give enough time to the mds to transtion its state
// after the timeout we consider that the mds might be gone or the timeout was not long enough...
return mdsRank, fmt.Errorf("mds %s not found in fsmap, this likely means mdss are transitioning between active and standby states", fsName)
}

return mdsRank, nil
}

// MdsActiveOrStandbyReplay returns wether a given MDS is active or in standby
func MdsActiveOrStandbyReplay(context *clusterd.Context, clusterName, fsName string) error {
status, err := Status(context, clusterName, false)
if err != nil {
return fmt.Errorf("failed to get ceph status. %+v", err)
}

mdsRank, err := getMDSRank(status, clusterName, fsName)
if err != nil {
return fmt.Errorf("%+v", err)
}

// this MDS is in standby so let's return immediatly
if mdsRank < 0 {
logger.Infof("mds %s is in standby, nothing to check", fsName)
return nil
}

if status.Fsmap.ByRank[mdsRank].Status == "up:active" || status.Fsmap.ByRank[mdsRank].Status == "up:standby-replay" || status.Fsmap.ByRank[mdsRank].Status == "up:standby" {
logger.Infof("mds %s is %s", fsName, status.Fsmap.ByRank[mdsRank].Status)
return nil
}

return fmt.Errorf("mds %s is %s, bad state", fsName, status.Fsmap.ByRank[mdsRank].Status)
}
33 changes: 33 additions & 0 deletions pkg/daemon/ceph/client/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ const (
CephStatusResponseRaw = `{"fsid":"613975f3-3025-4802-9de1-a2280b950e75","health":{"checks":{"OSD_DOWN":{"severity":"HEALTH_WARN","summary":{"message":"1 osds down"}},"OSD_HOST_DOWN":{"severity":"HEALTH_WARN","summary":{"message":"1 host (1 osds) down"}},"PG_AVAILABILITY":{"severity":"HEALTH_WARN","summary":{"message":"Reduced data availability: 101 pgs stale"}},"POOL_APP_NOT_ENABLED":{"severity":"HEALTH_WARN","summary":{"message":"application not enabled on 1 pool(s)"}}},"status":"HEALTH_WARN","overall_status":"HEALTH_WARN"},"election_epoch":12,"quorum":[0,1,2],"quorum_names":["rook-ceph-mon0","rook-ceph-mon2","rook-ceph-mon1"],"monmap":{"epoch":3,"fsid":"613975f3-3025-4802-9de1-a2280b950e75","modified":"2017-08-11 20:13:02.075679","created":"2017-08-11 20:12:35.314510","features":{"persistent":["kraken","luminous"],"optional":[]},"mons":[{"rank":0,"name":"rook-ceph-mon0","addr":"10.3.0.45:6789/0","public_addr":"10.3.0.45:6789/0"},{"rank":1,"name":"rook-ceph-mon2","addr":"10.3.0.249:6789/0","public_addr":"10.3.0.249:6789/0"},{"rank":2,"name":"rook-ceph-mon1","addr":"10.3.0.252:6789/0","public_addr":"10.3.0.252:6789/0"}]},"osdmap":{"osdmap":{"epoch":17,"num_osds":2,"num_up_osds":1,"num_in_osds":2,"full":false,"nearfull":true,"num_remapped_pgs":0}},"pgmap":{"pgs_by_state":[{"state_name":"stale+active+clean","count":101},{"state_name":"active+clean","count":99}],"num_pgs":200,"num_pools":2,"num_objects":243,"data_bytes":976793635,"bytes_used":13611479040,"bytes_avail":19825307648,"bytes_total":33436786688},"fsmap":{"epoch":1,"by_rank":[]},"mgrmap":{"epoch":3,"active_gid":14111,"active_name":"rook-ceph-mgr0","active_addr":"10.2.73.6:6800/9","available":true,"standbys":[],"modules":["restful","status"],"available_modules":["dashboard","prometheus","restful","status","zabbix"]},"servicemap":{"epoch":1,"modified":"0.000000","services":{}}}`
)

var (
// this JSON was generated from `ceph status -f json`, using Ceph Nautilus 14.2.1
// It was chopped to only show what the tests are looking for
statusFakeRaw = []byte(`{
"fsmap": {
"epoch": 13,
"id": 1,
"up": 1,
"in": 1,
"max": 1,
"by_rank": [
{
"filesystem_id": 1,
"rank": 0,
"name": "myfs-b",
"status": "up:active",
"gid": 14716
}
],
"up:standby": 1
}
}`)
)

func TestStatusMarshal(t *testing.T) {
var status CephStatus
err := json.Unmarshal([]byte(CephStatusResponseRaw), &status)
Expand Down Expand Up @@ -85,3 +109,12 @@ func TestIsClusterClean(t *testing.T) {
err = isClusterClean(status)
assert.NotNil(t, err)
}

func TestGetMDSRank(t *testing.T) {
var statusFake CephStatus
json.Unmarshal(statusFakeRaw, &statusFake)

mdsRankFake, err := getMDSRank(statusFake, "rook-ceph", "myfs-b")
assert.Nil(t, err)
assert.Equal(t, 0, mdsRankFake)
}
Loading

0 comments on commit d10b4e8

Please sign in to comment.