package zones import ( "net/netip" "os" "strings" "darvaza.org/core" "git.jpi.io/amery/jpictl/pkg/ceph" ) // CephMissingMonitorError is an error that contains ceph // monitors present in ceph.conf but not found on the cluster type CephMissingMonitorError struct { Names []string Addrs []netip.Addr } func (err *CephMissingMonitorError) appendName(name string) { err.Names = append(err.Names, name) } func (err *CephMissingMonitorError) appendAddr(addr netip.Addr) { err.Addrs = append(err.Addrs, addr) } // OK tells if this instance actual shouldn't be treated as an error func (err CephMissingMonitorError) OK() bool { switch { case len(err.Names) > 0: return false case len(err.Addrs) > 0: return false default: return true } } func (err CephMissingMonitorError) Error() string { if !err.OK() { var buf strings.Builder _, _ = buf.WriteString("missing:") err.writeNames(&buf) err.writeAddrs(&buf) return buf.String() } // no error return "" } func (err *CephMissingMonitorError) writeNames(w *strings.Builder) { if len(err.Names) > 0 { _, _ = w.WriteString(" mon_initial_members:") for i, name := range err.Names { if i != 0 { _, _ = w.WriteRune(',') } _, _ = w.WriteString(name) } } } func (err *CephMissingMonitorError) writeAddrs(w *strings.Builder) { if len(err.Addrs) > 0 { _, _ = w.WriteString(" mon_host:") for i, addr := range err.Addrs { if i != 0 { _, _ = w.WriteRune(',') } _, _ = w.WriteString(addr.String()) } } } // AsError returns nil if the instance is actually OK func (err *CephMissingMonitorError) AsError() error { if err == nil || err.OK() { return nil } return err } type cephScanTODO struct { names map[string]bool addrs map[string]bool } func (todo *cephScanTODO) checkMachine(p *Machine) bool { // on ceph all addresses are ring1 ring1, _ := RingOneAddress(p.Zone(), p.ID) addr := ring1.String() if _, found := todo.names[p.Name]; found { // found on the TODO by name todo.names[p.Name] = true todo.addrs[addr] = true return true } if _, found := todo.addrs[addr]; found { // found on the TODO by address todo.names[p.Name] = true todo.addrs[addr] = true return true } return false } func (todo *cephScanTODO) Missing() error { var check CephMissingMonitorError for name, found := range todo.names { if !found { check.appendName(name) } } for addr, found := range todo.addrs { if !found { var a netip.Addr // it wouldn't be on the map if it wasn't valid _ = a.UnmarshalText([]byte(addr)) check.appendAddr(a) } } return check.AsError() } func newCephScanTODO(cfg *ceph.Config) *cephScanTODO { todo := &cephScanTODO{ names: make(map[string]bool), addrs: make(map[string]bool), } for _, name := range cfg.Global.Monitors { todo.names[name] = false } for _, addr := range cfg.Global.MonitorsAddr { todo.addrs[addr.String()] = false } return todo } func (m *Zones) scanCephMonitors(_ *ScanOptions) error { cfg, err := m.GetCephConfig() switch { case os.IsNotExist(err): err = nil case err != nil: return err } if cfg != nil { // store FSID m.CephFSID = cfg.Global.FSID // flag monitors based on config todo := newCephScanTODO(cfg) m.ForEachMachine(func(p *Machine) bool { p.CephMonitor = todo.checkMachine(p) return false }) if err := todo.Missing(); err != nil { return core.Wrap(err, "ceph") } } // make sure every zone has one m.ForEachZone(func(z *Zone) bool { _ = z.GetCephMonitors() return false }) return nil }