From c2ac588ce65ef8b6ce4152a107427144e44db6d6 Mon Sep 17 00:00:00 2001 From: Luciano Lo Giudice Date: Sun, 10 Nov 2024 20:24:59 -0300 Subject: [PATCH] Set the 'require-osd-release' option on startup Signed-off-by: Luciano Lo Giudice --- microceph/ceph/start.go | 99 ++++++++++++++++++++++++++++++++++++ microceph/ceph/start_test.go | 57 +++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 microceph/ceph/start_test.go diff --git a/microceph/ceph/start.go b/microceph/ceph/start.go index 3450df61..801b65a9 100644 --- a/microceph/ceph/start.go +++ b/microceph/ceph/start.go @@ -3,7 +3,10 @@ package ceph import ( "context" "database/sql" + "encoding/json" + "fmt" "reflect" + "strings" "time" "github.com/canonical/lxd/shared/logger" @@ -12,6 +15,100 @@ import ( "github.com/canonical/microceph/microceph/interfaces" ) +type cephVersionElem map[string]int32 + +type cephVersion struct { + Mon cephVersionElem `json:"mon"` + Mgr cephVersionElem `json:"mgr"` + Osd cephVersionElem `json:"osd"` + Mds cephVersionElem `json:"mds"` + Overall cephVersionElem `json:"overall"` +} + +func checkVersions() (bool, error) { + out, err := processExec.RunCommand("ceph", "versions") + if err != nil { + return false, fmt.Errorf("Failed to get Ceph versions: %w", err) + } + + var cephVer cephVersion + err = json.Unmarshal([]byte(out), &cephVer) + if err != nil { + return false, fmt.Errorf("Failed to unmarshal Ceph versions: %w", err) + } + + if len(cephVer.Overall) > 1 { + logger.Debug("Not all upgrades have completed") + return false, nil + } + + if len(cephVer.Osd) < 1 { + logger.Debug("No OSD versions found") + return false, nil + } + + return true, nil +} + +func osdReleaseRequired(version string) (bool, error) { + out, err := processExec.RunCommand("ceph", "osd", "dump", "-f", "json") + if err != nil { + return false, fmt.Errorf("Failed to get OSD dump: %w", err) + } + + var result map[string]any + err = json.Unmarshal([]byte(out), &result) + if err != nil { + return false, fmt.Errorf("Failed to unmarshal OSD dump: %w", err) + } + + return result["require_osd_release"].(string) != version, nil +} + +func PostRefresh() error { + currentVersion, err := processExec.RunCommand("ceph", "-v") + if err != nil { + return err + } + + lastPos := strings.LastIndex(currentVersion, " ") + if lastPos < 0 { + return fmt.Errorf("invalid version string: %s", currentVersion) + } + + currentVersion = currentVersion[0:lastPos] + lastPos = strings.LastIndex(currentVersion, " ") + if lastPos < 0 { + return fmt.Errorf("invalid version string: %s", currentVersion) + } + + currentVersion = currentVersion[lastPos+1 : len(currentVersion)] + allVersionsEqual, err := checkVersions() + + if err != nil { + return err + } + + if !allVersionsEqual { + return nil + } + + mustUpdate, err := osdReleaseRequired(currentVersion) + if err != nil { + return err + } + + if mustUpdate { + _, err = processExec.RunCommand("ceph", "osd", "require-osd-release", + currentVersion, "--yes-i-really-mean-it") + if err != nil { + return err + } + } + + return nil +} + // Start is run on daemon startup. func Start(ctx context.Context, s interfaces.StateInterface) error { // Start background loop to refresh the config every minute if needed. @@ -67,5 +164,7 @@ func Start(ctx context.Context, s interfaces.StateInterface) error { } }() + go PostRefresh() + return nil } diff --git a/microceph/ceph/start_test.go b/microceph/ceph/start_test.go new file mode 100644 index 00000000..5a01f746 --- /dev/null +++ b/microceph/ceph/start_test.go @@ -0,0 +1,57 @@ +package ceph + +import ( + "testing" + + "github.com/canonical/microceph/microceph/mocks" + "github.com/canonical/microceph/microceph/tests" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type startSuite struct { + tests.BaseSuite +} + +func TestStart(t *testing.T) { + suite.Run(t, new(startSuite)) +} + +func addExpected(r *mocks.Runner) { + version := `ceph version 19.2.0 (e7ad5345525c7aa95470c26863873b581076945d) squid (stable)` + versionsJson := `{ + "mon": { + "ceph version 18.2.4 (e7ad5345525c7aa95470c26863873b581076945d) reef (stable)": 1 + }, + "mgr": { + "ceph version 18.2.4 (e7ad5345525c7aa95470c26863873b581076945d) reef (stable)": 1 + }, + "osd": { + "ceph version 18.2.4 (e7ad5345525c7aa95470c26863873b581076945d) reef (stable)": 4 + }, + "mds": { + "ceph version 18.2.4 (e7ad5345525c7aa95470c26863873b581076945d) reef (stable)": 1 + }, + "overall": { + "ceph version 18.2.4 (e7ad5345525c7aa95470c26863873b581076945d) reef (stable)": 7 + } +}` + osdDump := `{"require_osd_release": "reef"}` + + r.On("RunCommand", "ceph", "-v").Return(version, nil).Once() + r.On("RunCommand", "ceph", "versions").Return(versionsJson, nil).Once() + r.On("RunCommand", "ceph", "osd", "dump", "-f", "json").Return(osdDump, nil).Once() + r.On("RunCommand", "ceph", "osd", "require-osd-release", + "squid", "--yes-i-really-mean-it").Return("ok", nil).Once() +} + +func (s *startSuite) TestStart() { + r := mocks.NewRunner(s.T()) + + addExpected(r) + processExec = r + + err := PostRefresh() + assert.NoError(s.T(), err) +}