diff --git a/src/go/mongolib/proto/master_doc.go b/src/go/mongolib/proto/master_doc.go index 77d530d5c..5d889e944 100644 --- a/src/go/mongolib/proto/master_doc.go +++ b/src/go/mongolib/proto/master_doc.go @@ -14,7 +14,25 @@ package proto type MasterDoc struct { - SetName interface{} `bson:"setName"` - Hosts interface{} `bson:"hosts"` - Msg string `bson:"msg"` + SetName interface{} `bson:"setName"` + Hosts interface{} `bson:"hosts"` + Msg string `bson:"msg"` + ArbiterOnly bool `bson:"arbiterOnly"` + Me string `bson:"me"` + Arbiters []string `bson:"arbiters"` +} + +func (md MasterDoc) IsArbiter() bool { + if md.ArbiterOnly { + return true + } + if md.Me == "" { + return false + } + for _, a := range md.Arbiters { + if a == md.Me { + return true + } + } + return false } diff --git a/src/go/mongolib/util/util.go b/src/go/mongolib/util/util.go index 5e0af6982..d4e58a8ad 100644 --- a/src/go/mongolib/util/util.go +++ b/src/go/mongolib/util/util.go @@ -106,6 +106,17 @@ func GetReplicasetMembers(ctx context.Context, clientOptions *options.ClientOpti } m.StateStr = strings.ToUpper(cmdOpts.Parsed.Sharding.ClusterRole) + if md, mdErr := GetMasterDoc(ctx, client); mdErr == nil && md.IsArbiter() { + if m.StateStr != "" { + m.StateStr += "/ARBITER" + } else { + m.StateStr = "ARBITER" + } + if setName, ok := md.SetName.(string); ok { + m.Set = setName + } + } + if serverStatus, err := GetServerStatus(ctx, client); err == nil { m.ID = serverStatus.Pid m.StorageEngine = serverStatus.StorageEngine @@ -537,9 +548,16 @@ func fillProcInfo(pid int32, procInfo *proto.ProcInfo) error { return nil } -func getNodeType(ctx context.Context, client *mongo.Client) (string, error) { +func GetMasterDoc(ctx context.Context, client *mongo.Client) (proto.MasterDoc, error) { md := proto.MasterDoc{} - if err := client.Database("admin").RunCommand(ctx, primitive.M{"isMaster": 1}).Decode(&md); err != nil { + err := client.Database("admin").RunCommand(ctx, primitive.M{"isMaster": 1}).Decode(&md) + + return md, err +} + +func getNodeType(ctx context.Context, client *mongo.Client) (string, error) { + md, err := GetMasterDoc(ctx, client) + if err != nil { return "", err } diff --git a/src/go/pt-mongodb-summary/main.go b/src/go/pt-mongodb-summary/main.go index 020356521..97d9c47c9 100644 --- a/src/go/pt-mongodb-summary/main.go +++ b/src/go/pt-mongodb-summary/main.go @@ -118,6 +118,7 @@ type hostInfo struct { ReplicasetName string Version string NodeType string + IsArbiter bool } type procInfo struct { @@ -365,12 +366,14 @@ func formatResults(ci *collectedInfo, format string) ([]byte, error) { default: buf = new(bytes.Buffer) - t := template.Must(template.New("mongos").Parse(templates.MongosInfo)) - if err := t.Execute(buf, ci.MongosInfo); err != nil { - return nil, errors.Wrap(err, "cannot parse mongos section of the output template") + if ci.MongosInfo != nil { + t := template.Must(template.New("mongos").Parse(templates.MongosInfo)) + if err := t.Execute(buf, ci.MongosInfo); err != nil { + return nil, errors.Wrap(err, "cannot parse mongos section of the output template") + } } - t = template.Must(template.New("replicas").Parse(templates.Replicas)) + t := template.Must(template.New("replicas").Parse(templates.Replicas)) if err := t.Execute(buf, ci.ReplicaMembers); err != nil { return nil, errors.Wrap(err, "cannot parse replicas section of the output template") } @@ -385,14 +388,18 @@ func formatResults(ci *collectedInfo, format string) ([]byte, error) { return nil, errors.Wrap(err, "cannot parse the command line args section of the output template") } - t = template.Must(template.New("runningOps").Parse(templates.RunningOps)) - if err := t.Execute(buf, ci.RunningOps); err != nil { - return nil, errors.Wrap(err, "cannot parse runningOps section of the output template") + if ci.RunningOps != nil { + t = template.Must(template.New("runningOps").Parse(templates.RunningOps)) + if err := t.Execute(buf, ci.RunningOps); err != nil { + return nil, errors.Wrap(err, "cannot parse runningOps section of the output template") + } } - t = template.Must(template.New("ssl").Parse(templates.Security)) - if err := t.Execute(buf, ci.SecuritySettings); err != nil { - return nil, errors.Wrap(err, "cannot parse ssl section of the output template") + if ci.SecuritySettings != nil { + t = template.Must(template.New("ssl").Parse(templates.Security)) + if err := t.Execute(buf, ci.SecuritySettings); err != nil { + return nil, errors.Wrap(err, "cannot parse ssl section of the output template") + } } if ci.OplogInfo != nil && len(ci.OplogInfo) > 0 { @@ -416,24 +423,43 @@ func formatResults(ci *collectedInfo, format string) ([]byte, error) { return buf.Bytes(), nil } +func newArbiterHostInfo(md proto.MasterDoc) *hostInfo { + setName, _ := md.SetName.(string) + + return &hostInfo{ + NodeType: "ARBITER", + Hostname: md.Me, + ReplicasetName: setName, + IsArbiter: true, + } +} + func getHostInfo(ctx context.Context, client *mongo.Client) (*hostInfo, error) { + var i *hostInfo + hi := proto.HostInfo{} if err := client.Database("admin").RunCommand(ctx, primitive.M{"hostInfo": 1}).Decode(&hi); err != nil { log.Debugf("run('hostInfo') error: %s", err) - return nil, errors.Wrap(err, "GetHostInfo.hostInfo") - } - - nodeType, _ := getNodeType(ctx, client) - procCount, _ := countMongodProcesses() + md, mdErr := util.GetMasterDoc(ctx, client) + if mdErr != nil || !md.IsArbiter() { + return nil, errors.Wrap(err, "GetHostInfo.hostInfo") + } - i := &hostInfo{ - Hostname: hi.System.Hostname, - HostOsType: hi.Os.Type, - HostSystemCPUArch: hi.System.CpuArch, - ProcProcessCount: procCount, - NodeType: nodeType, - CmdlineArgs: nil, + i = newArbiterHostInfo(md) + i.ProcProcessCount, _ = countMongodProcesses() + } else { + nodeType, _ := getNodeType(ctx, client) + procCount, _ := countMongodProcesses() + + i = &hostInfo{ + Hostname: hi.System.Hostname, + HostOsType: hi.Os.Type, + HostSystemCPUArch: hi.System.CpuArch, + ProcProcessCount: procCount, + NodeType: nodeType, + CmdlineArgs: nil, + } } var cmdOpts proto.CommandLineOptions @@ -454,7 +480,7 @@ func getHostInfo(ctx context.Context, client *mongo.Client) (*hostInfo, error) { if err == nil { i.ProcessName = ss.Process i.Version = ss.Version - if ss.Repl != nil { + if ss.Repl != nil && ss.Repl.SetName != "" { i.ReplicasetName = ss.Repl.SetName } diff --git a/src/go/pt-mongodb-summary/templates/hostinfo.go b/src/go/pt-mongodb-summary/templates/hostinfo.go index 3e31f3a6b..6a6771354 100644 --- a/src/go/pt-mongodb-summary/templates/hostinfo.go +++ b/src/go/pt-mongodb-summary/templates/hostinfo.go @@ -26,7 +26,9 @@ const HostInfo = `# This host Hostname | {{.Hostname}} Version | {{.Version}} Built On | {{.HostOsType}} {{.HostSystemCPUArch}} +{{- if not .ProcCreateTime.IsZero }} Started | {{.ProcCreateTime}} +{{- end }} {{- if .DBPath }} Datadir | {{.DBPath}} {{- end }} @@ -35,5 +37,11 @@ const HostInfo = `# This host {{- if .ReplicasetName }} ReplSet | {{.ReplicasetName}} Repl Status | +{{- end }} +{{- if .IsArbiter }} + +# Arbiter ############################################################################################### +This node is a replica-set ARBITER: it holds no data, so host, security, storage and +running operations details are unavailable from this member. {{- end -}} `