diff --git a/src/go/lib/util/util.go b/src/go/lib/util/util.go index a99af609..61664f0c 100644 --- a/src/go/lib/util/util.go +++ b/src/go/lib/util/util.go @@ -2,6 +2,8 @@ package util import ( "encoding/json" + "io/ioutil" + "os" "os/exec" "strings" ) @@ -18,3 +20,36 @@ func Pretty(value interface{}) string { bytes, _ := json.MarshalIndent(value, "", " ") return string(bytes) } + +func LoadJson(filename string, destination interface{}) error { + file, err := os.Open(filename) + if err != nil { + return err + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return err + } + + err = json.Unmarshal(buf, &destination) + if err != nil { + return err + } + + return nil +} + +func WriteJson(filename string, data interface{}) error { + + buf, err := json.Marshal(data) + if err != nil { + return err + } + err = ioutil.WriteFile(filename, buf, 0) + if err != nil { + return err + } + return nil + +} diff --git a/src/go/mongolib/util/main_test.go b/src/go/mongolib/util/main_test.go new file mode 100644 index 00000000..696715c7 --- /dev/null +++ b/src/go/mongolib/util/main_test.go @@ -0,0 +1,205 @@ +package util + +import ( + "reflect" + "testing" + + mgo "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" + + "github.com/golang/mock/gomock" + lutil "github.com/percona/percona-toolkit/src/go/lib/util" + "github.com/percona/percona-toolkit/src/go/mongolib/proto" + "github.com/percona/pmgo/pmgomock" +) + +// OK +func TestGetReplicasetMembers(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + dialer := pmgomock.NewMockDialer(ctrl) + + session := pmgomock.NewMockSessionManager(ctrl) + + mockrss := proto.ReplicaSetStatus{ + Date: "", + MyState: 1, + Term: 0, + HeartbeatIntervalMillis: 0, + Members: []proto.Members{ + proto.Members{ + Optime: nil, + OptimeDate: "", + InfoMessage: "", + ID: 0, + Name: "localhost:17001", + Health: 1, + StateStr: "PRIMARY", + Uptime: 113287, + ConfigVersion: 1, + Self: true, + State: 1, + ElectionTime: 6340960613392449537, + ElectionDate: "", + Set: ""}, + proto.Members{ + Optime: nil, + OptimeDate: "", + InfoMessage: "", + ID: 1, + Name: "localhost:17002", + Health: 1, + StateStr: "SECONDARY", + Uptime: 113031, + ConfigVersion: 1, + Self: false, + State: 2, + ElectionTime: 0, + ElectionDate: "", + Set: ""}, + proto.Members{ + Optime: nil, + OptimeDate: "", + InfoMessage: "", + ID: 2, + Name: "localhost:17003", + Health: 1, + StateStr: "SECONDARY", + Uptime: 113031, + ConfigVersion: 1, + Self: false, + State: 2, + ElectionTime: 0, + ElectionDate: "", + Set: ""}}, + Ok: 1, + Set: "r1", + } + expect := []proto.Members{ + proto.Members{ + Optime: nil, + OptimeDate: "", + InfoMessage: "", + ID: 0, + Name: "localhost:17001", + Health: 1, + StateStr: "PRIMARY", + Uptime: 113287, + ConfigVersion: 1, + Self: true, + State: 1, + ElectionTime: 6340960613392449537, + ElectionDate: "", + Set: "r1"}, + proto.Members{Optime: (*proto.Optime)(nil), + OptimeDate: "", + InfoMessage: "", + ID: 1, + Name: "localhost:17002", + Health: 1, + StateStr: "SECONDARY", + Uptime: 113031, + ConfigVersion: 1, + Self: false, + State: 2, + ElectionTime: 0, + ElectionDate: "", + Set: "r1"}, + proto.Members{Optime: (*proto.Optime)(nil), + OptimeDate: "", + InfoMessage: "", + ID: 2, + Name: "localhost:17003", + Health: 1, + StateStr: "SECONDARY", + Uptime: 113031, + ConfigVersion: 1, + Self: false, + State: 2, + ElectionTime: 0, + ElectionDate: "", + Set: "r1", + }} + + database := pmgomock.NewMockDatabaseManager(ctrl) + ss := proto.ServerStatus{} + lutil.LoadJson("test/sample/serverstatus.json", &ss) + + dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) + session.EXPECT().Run(bson.M{"replSetGetStatus": 1}, gomock.Any()).SetArg(1, mockrss) + + dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) + session.EXPECT().DB("admin").Return(database) + database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) + session.EXPECT().Close() + + dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) + session.EXPECT().DB("admin").Return(database) + database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) + session.EXPECT().Close() + + dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) + session.EXPECT().DB("admin").Return(database) + database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) + session.EXPECT().Close() + + session.EXPECT().Close() + + di := &mgo.DialInfo{Addrs: []string{"localhost"}} + rss, err := GetReplicasetMembers(dialer, di) + if err != nil { + t.Errorf("getReplicasetMembers: %v", err) + } + if !reflect.DeepEqual(rss, expect) { + t.Errorf("getReplicasetMembers:\ngot %#v\nwant: %#v\n", rss, expect) + } + +} + +//OK +func TestGetHostnames(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + dialer := pmgomock.NewMockDialer(ctrl) + session := pmgomock.NewMockSessionManager(ctrl) + + mockShardsInfo := proto.ShardsInfo{ + Shards: []proto.Shard{ + proto.Shard{ + ID: "r1", + Host: "r1/localhost:17001,localhost:17002,localhost:17003", + }, + proto.Shard{ + ID: "r2", + Host: "r2/localhost:18001,localhost:18002,localhost:18003", + }, + }, + OK: 1, + } + + dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) + session.EXPECT().Run("getShardMap", gomock.Any()).SetArg(1, mockShardsInfo) + session.EXPECT().Close() + + expect := []string{"localhost", "localhost:17001", "localhost:18001"} + di := &mgo.DialInfo{Addrs: []string{"localhost"}} + rss, err := GetHostnames(dialer, di) + if err != nil { + t.Errorf("getHostnames: %v", err) + } + if !reflect.DeepEqual(rss, expect) { + t.Errorf("getHostnames: got %+v, expected: %+v\n", rss, expect) + } +} + +func addToCounters(ss proto.ServerStatus, increment int64) proto.ServerStatus { + ss.Opcounters.Command += increment + ss.Opcounters.Delete += increment + ss.Opcounters.GetMore += increment + ss.Opcounters.Insert += increment + ss.Opcounters.Query += increment + ss.Opcounters.Update += increment + return ss +} diff --git a/src/go/mongolib/util/util.go b/src/go/mongolib/util/util.go index 607965da..338d9a32 100644 --- a/src/go/mongolib/util/util.go +++ b/src/go/mongolib/util/util.go @@ -3,6 +3,7 @@ package util import ( "fmt" "strings" + "time" "github.com/bradfitz/slice" "github.com/percona/percona-toolkit/src/go/mongolib/proto" @@ -12,50 +13,22 @@ import ( "gopkg.in/mgo.v2/bson" ) -func GetReplicasetMembersNew(dialer pmgo.Dialer, di *mgo.DialInfo) ([]proto.Members, error) { - hostnames, err := GetHostnames(dialer, di) - if err != nil { - return nil, err - } - replicaMembers := []proto.Members{} - for _, hostname := range hostnames { - if serverStatus, err := GetServerStatus(dialer, di, hostname); err == nil { - - m := proto.Members{ - ID: serverStatus.Pid, - Name: hostname, - StorageEngine: serverStatus.StorageEngine, - Set: serverStatus.Repl.SetName, - } - if serverStatus.Repl.IsMaster != nil && serverStatus.Repl.IsMaster.(bool) { - m.StateStr = "PRIMARY" - } - if serverStatus.Repl.Secondary != nil && serverStatus.Repl.Secondary.(bool) { - m.StateStr = "SECONDARY" - } - replicaMembers = append(replicaMembers, m) - } - - } - - return replicaMembers, nil -} - func GetReplicasetMembers(dialer pmgo.Dialer, di *mgo.DialInfo) ([]proto.Members, error) { hostnames, err := GetHostnames(dialer, di) if err != nil { + fmt.Println("PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP") return nil, err } membersMap := make(map[string]proto.Members) members := []proto.Members{} for _, hostname := range hostnames { - tmpdi := *di - tmpdi.Addrs = []string{hostname} - session, err := dialer.DialWithInfo(&tmpdi) + session, err := dialer.DialWithInfo(getTmpDI(di, hostname)) if err != nil { - return nil, errors.Wrapf(err, "getReplicasetMembers. cannot connect to %s", hostname) + continue } + defer session.Close() + session.SetMode(mgo.Monotonic, true) cmdOpts := proto.CommandLineOptions{} session.DB("admin").Run(bson.D{{"getCmdLineOpts", 1}, {"recordStats", 1}}, &cmdOpts) @@ -101,18 +74,65 @@ func GetReplicasetMembers(dialer pmgo.Dialer, di *mgo.DialInfo) ([]proto.Members func GetHostnames(dialer pmgo.Dialer, di *mgo.DialInfo) ([]string, error) { hostnames := []string{di.Addrs[0]} + di.Direct = true + di.Timeout = 2 * time.Second + session, err := dialer.DialWithInfo(di) if err != nil { return hostnames, err } defer session.Close() + session.SetMode(mgo.Monotonic, true) + // Try getShardMap first. If we are connected to a mongos it will return + // all hosts, including config hosts var shardsMap proto.ShardsMap err = session.Run("getShardMap", &shardsMap) - if err != nil { - return hostnames, errors.Wrap(err, "cannot list shards") + if err == nil && len(shardsMap.Map) > 0 { + // if the only element getShardMap returns is the list of config servers, + // it means we are connected to a replicaSet member and getShardMap is not + // the answer we want. + _, ok := shardsMap.Map["config"] + if ok && len(shardsMap.Map) > 1 { + return buildHostsListFromShardMap(shardsMap), nil + } } + // Probably we are connected to an individual member of a replica set + rss := proto.ReplicaSetStatus{} + if err := session.Run(bson.M{"replSetGetStatus": 1}, &rss); err == nil { + return buildHostsListFromReplStatus(rss), nil + } + return hostnames, nil +} + +func buildHostsListFromReplStatus(replStatus proto.ReplicaSetStatus) []string { + /* + "members" : [ + { + "_id" : 0, + "name" : "localhost:17001", + "health" : 1, + "state" : 1, + "stateStr" : "PRIMARY", + "uptime" : 4700, + "optime" : Timestamp(1486554836, 1), + "optimeDate" : ISODate("2017-02-08T11:53:56Z"), + "electionTime" : Timestamp(1486651810, 1), + "electionDate" : ISODate("2017-02-09T14:50:10Z"), + "configVersion" : 1, + "self" : true + }, + */ + + hostnames := []string{} + for _, member := range replStatus.Members { + hostnames = append(hostnames, member.Name) + } + return hostnames +} + +func buildHostsListFromShardMap(shardsMap proto.ShardsMap) []string { /* Example mongos> db.getSiblingDB('admin').runCommand('getShardMap') { @@ -126,7 +146,12 @@ func GetHostnames(dialer pmgo.Dialer, di *mgo.DialInfo) ([]string, error) { } */ + hostnames := []string{} hm := make(map[string]bool) + + // Since shardMap can return repeated hosts in different rows, we need a Set + // but there is no Set in Go so, we are going to create a map and the loop + // through the keys to get a list of unique host names if shardsMap.Map != nil { for _, val := range shardsMap.Map { m := strings.Split(val, "/") @@ -143,17 +168,16 @@ func GetHostnames(dialer pmgo.Dialer, di *mgo.DialInfo) ([]string, error) { hm[host] = false } } - hostnames = []string{} // re-init because it has di.Addr[0] for host := range hm { hostnames = append(hostnames, host) } } - return hostnames, nil + return hostnames } // This function is like GetHostnames but it uses listShards instead of getShardMap // so it won't include config servers in the returned list -func GetShardsHosts(dialer pmgo.Dialer, di *mgo.DialInfo) ([]string, error) { +func GetShardedHosts(dialer pmgo.Dialer, di *mgo.DialInfo) ([]string, error) { hostnames := []string{di.Addrs[0]} session, err := dialer.DialWithInfo(di) if err != nil { @@ -177,24 +201,27 @@ func GetShardsHosts(dialer pmgo.Dialer, di *mgo.DialInfo) ([]string, error) { return hostnames, nil } +func getTmpDI(di *mgo.DialInfo, hostname string) *mgo.DialInfo { + tmpdi := *di + tmpdi.Addrs = []string{hostname} + tmpdi.Direct = true + tmpdi.Timeout = 2 * time.Second + + return &tmpdi +} + func GetServerStatus(dialer pmgo.Dialer, di *mgo.DialInfo, hostname string) (proto.ServerStatus, error) { ss := proto.ServerStatus{} - tmpdi := *di - tmpdi.Addrs = []string{hostname} - // tmpdi.Direct = true - // tmpdi.Timeout = 5 * time.Second - // tmpdi.FailFast = false - - session, err := dialer.DialWithInfo(&tmpdi) + tmpdi := getTmpDI(di, hostname) + session, err := dialer.DialWithInfo(tmpdi) if err != nil { - fmt.Printf("error %s\n", err.Error()) return ss, errors.Wrapf(err, "getReplicasetMembers. cannot connect to %s", hostname) } defer session.Close() + session.SetMode(mgo.Monotonic, true) if err := session.DB("admin").Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, &ss); err != nil { - fmt.Printf("error 2%s\n", err.Error()) return ss, errors.Wrap(err, "GetHostInfo.serverStatus") } diff --git a/src/go/pt-mongodb-summary/main.go b/src/go/pt-mongodb-summary/main.go index aecf0afc..e4be39e9 100644 --- a/src/go/pt-mongodb-summary/main.go +++ b/src/go/pt-mongodb-summary/main.go @@ -52,7 +52,6 @@ type opCounters struct { SampleRate time.Duration } type hostInfo struct { - ThisHostID int Hostname string HostOsType string HostSystemCPUArch string @@ -115,6 +114,7 @@ type clusterwideInfo struct { } type options struct { + Help bool Host string User string Password string @@ -129,32 +129,9 @@ type options struct { func main() { - opts := options{ - Host: "localhost:27017", - LogLevel: "error", - RunningOpsSamples: 5, - RunningOpsInterval: 1000, // milliseconds - } - help := getopt.BoolLong("help", '?', "Show help") - getopt.BoolVarLong(&opts.Version, "version", 'v', "", "Show version & exit") - getopt.BoolVarLong(&opts.NoVersionCheck, "no-version-check", 'c', "", "Don't check for updates") + opts := parseFlags() - getopt.StringVarLong(&opts.User, "user", 'u', "", "User name") - getopt.StringVarLong(&opts.Password, "password", 'p', "", "Password").SetOptional() - getopt.StringVarLong(&opts.AuthDB, "authenticationDatabase", 'a', "admin", - "Database used to establish credentials and privileges with a MongoDB server") - getopt.StringVarLong(&opts.LogLevel, "log-level", 'l', "error", "Log level:, panic, fatal, error, warn, info, debug") - - getopt.IntVarLong(&opts.RunningOpsSamples, "running-ops-samples", 's', - fmt.Sprintf("Number of samples to collect for running ops. Default: %d", opts.RunningOpsSamples)) - - getopt.IntVarLong(&opts.RunningOpsInterval, "running-ops-interval", 'i', - fmt.Sprintf("Interval to wait betwwen running ops samples in milliseconds. Default %d milliseconds", opts.RunningOpsInterval)) - - getopt.SetParameters("host[:port]") - - getopt.Parse() - if *help { + if opts.Help { getopt.Usage() return } @@ -166,11 +143,6 @@ func main() { log.SetLevel(logLevel) - args := getopt.Args() // positional arg - if len(args) > 0 { - opts.Host = args[0] - } - if opts.Version { fmt.Println(TOOLNAME) fmt.Printf("Version %s\n", Version) @@ -216,26 +188,29 @@ func main() { session, err := dialer.DialWithInfo(di) if err != nil { - log.Errorf("cannot connect to the db: %s", err) + message := fmt.Sprintf("Cannot connect to %q", di.Addrs[0]) + if di.Username != "" || di.Password != "" { + message += fmt.Sprintf(" using user: %q, password: %q", di.Username, di.Password) + } + message += fmt.Sprintf(". %s", err.Error()) + log.Errorf(message) os.Exit(1) } defer session.Close() if replicaMembers, err := util.GetReplicasetMembers(dialer, di); err != nil { - log.Printf("[Error] cannot get replicaset members: %v\n", err) + log.Warnf("[Error] cannot get replicaset members: %v\n", err) + os.Exit(2) } else { log.Debugf("replicaMembers:\n%+v\n", replicaMembers) t := template.Must(template.New("replicas").Parse(templates.Replicas)) t.Execute(os.Stdout, replicaMembers) } - // - hostInfo, err := GetHostinfo(session) if err != nil { - log.Printf("[Error] cannot get host info: %v\n", err) + log.Warnf("[Error] cannot get host info: %v\n", err) } else { - log.Debugf("hostInfo:\n%+v\n", hostInfo) t := template.Must(template.New("hosttemplateData").Parse(templates.HostInfo)) t.Execute(os.Stdout, hostInfo) } @@ -249,19 +224,26 @@ func main() { } } - if security, err := GetSecuritySettings(session, hostInfo.Version); err != nil { - log.Printf("[Error] cannot get security settings: %v\n", err) + if hostInfo != nil { + if security, err := GetSecuritySettings(session, hostInfo.Version); err != nil { + log.Printf("[Error] cannot get security settings: %v\n", err) + } else { + t := template.Must(template.New("ssl").Parse(templates.Security)) + t.Execute(os.Stdout, security) + } } else { - t := template.Must(template.New("ssl").Parse(templates.Security)) - t.Execute(os.Stdout, security) + log.Warn("Cannot check security settings since host info is not available (permissions?)") } if oplogInfo, err := oplog.GetOplogInfo(hostnames, di); err != nil { - log.Printf("[Error] cannot get Oplog info: %v\n", err) + log.Info("Cannot get Oplog info: %v\n", err) } else { if len(oplogInfo) > 0 { t := template.Must(template.New("oplogInfo").Parse(templates.Oplog)) t.Execute(os.Stdout, oplogInfo[0]) + } else { + + log.Info("oplog info is empty. Skipping") } } @@ -285,6 +267,7 @@ func GetHostinfo(session pmgo.SessionManager) (*hostInfo, error) { hi := proto.HostInfo{} if err := session.Run(bson.M{"hostInfo": 1}, &hi); err != nil { + log.Debugf("run('hostInfo') error: %s", err.Error()) return nil, errors.Wrap(err, "GetHostInfo.hostInfo") } @@ -780,3 +763,38 @@ func externalIP() (string, error) { } return "", errors.New("are you connected to the network?") } + +func parseFlags() options { + opts := options{ + Host: "localhost:27017", + LogLevel: "warn", + RunningOpsSamples: 5, + RunningOpsInterval: 1000, // milliseconds + } + + getopt.BoolVarLong(&opts.Help, "help", 'h', "Show help") + getopt.BoolVarLong(&opts.Version, "version", 'v', "", "Show version & exit") + getopt.BoolVarLong(&opts.NoVersionCheck, "no-version-check", 'c', "", "Don't check for updates") + + getopt.StringVarLong(&opts.User, "user", 'u', "", "User name") + getopt.StringVarLong(&opts.Password, "password", 'p', "", "Password").SetOptional() + getopt.StringVarLong(&opts.AuthDB, "authenticationDatabase", 'a', "admin", + "Database used to establish credentials and privileges with a MongoDB server") + getopt.StringVarLong(&opts.LogLevel, "log-level", 'l', "error", "Log level:, panic, fatal, error, warn, info, debug") + + getopt.IntVarLong(&opts.RunningOpsSamples, "running-ops-samples", 's', + fmt.Sprintf("Number of samples to collect for running ops. Default: %d", opts.RunningOpsSamples)) + + getopt.IntVarLong(&opts.RunningOpsInterval, "running-ops-interval", 'i', + fmt.Sprintf("Interval to wait betwwen running ops samples in milliseconds. Default %d milliseconds", opts.RunningOpsInterval)) + + getopt.SetParameters("host[:port]") + + var gop = getopt.CommandLine + gop.Parse(os.Args) + if gop.NArgs() > 0 { + opts.Host = gop.Arg(0) + gop.Parse(gop.Args()) + } + return opts +} diff --git a/src/go/pt-mongodb-summary/main_test.go b/src/go/pt-mongodb-summary/main_test.go index 5d91c5fa..b3c2beef 100644 --- a/src/go/pt-mongodb-summary/main_test.go +++ b/src/go/pt-mongodb-summary/main_test.go @@ -6,13 +6,12 @@ import ( "testing" "time" - mgo "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" "github.com/golang/mock/gomock" + lutil "github.com/percona/percona-toolkit/src/go/lib/util" "github.com/percona/percona-toolkit/src/go/mongolib/proto" "github.com/percona/pmgo/pmgomock" - "github.com/percona/toolkit-go-old/pt-mongodb-summary/test" ) func TestGetOpCounterStats(t *testing.T) { @@ -24,7 +23,7 @@ func TestGetOpCounterStats(t *testing.T) { database := pmgomock.NewMockDatabaseManager(ctrl) ss := proto.ServerStatus{} - test.LoadJson("test/sample/serverstatus.json", &ss) + lutil.LoadJson("test/sample/serverstatus.json", &ss) session.EXPECT().DB("admin").Return(database) database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) @@ -45,7 +44,7 @@ func TestGetOpCounterStats(t *testing.T) { session.EXPECT().DB("admin").Return(database) database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) - var sampleCount int64 = 5 + var sampleCount int = 5 var sampleRate time.Duration = 10 * time.Millisecond // in seconds expect := TimedStats{Min: 0, Max: 0, Total: 0, Avg: 0} @@ -255,185 +254,6 @@ func TestGetNodeType(t *testing.T) { } -func TestGetReplicasetMembers(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - dialer := pmgomock.NewMockDialer(ctrl) - - session := pmgomock.NewMockSessionManager(ctrl) - - mockrss := proto.ReplicaSetStatus{ - Date: "", - MyState: 1, - Term: 0, - HeartbeatIntervalMillis: 0, - Members: []proto.Members{ - proto.Members{ - Optime: nil, - OptimeDate: "", - InfoMessage: "", - Id: 0, - Name: "localhost:17001", - Health: 1, - StateStr: "PRIMARY", - Uptime: 113287, - ConfigVersion: 1, - Self: true, - State: 1, - ElectionTime: 6340960613392449537, - ElectionDate: "", - Set: ""}, - proto.Members{ - Optime: nil, - OptimeDate: "", - InfoMessage: "", - Id: 1, - Name: "localhost:17002", - Health: 1, - StateStr: "SECONDARY", - Uptime: 113031, - ConfigVersion: 1, - Self: false, - State: 2, - ElectionTime: 0, - ElectionDate: "", - Set: ""}, - proto.Members{ - Optime: nil, - OptimeDate: "", - InfoMessage: "", - Id: 2, - Name: "localhost:17003", - Health: 1, - StateStr: "SECONDARY", - Uptime: 113031, - ConfigVersion: 1, - Self: false, - State: 2, - ElectionTime: 0, - ElectionDate: "", - Set: ""}}, - Ok: 1, - Set: "r1", - } - expect := []proto.Members{ - proto.Members{ - Optime: nil, - OptimeDate: "", - InfoMessage: "", - Id: 0, - Name: "localhost:17001", - Health: 1, - StateStr: "PRIMARY", - Uptime: 113287, - ConfigVersion: 1, - Self: true, - State: 1, - ElectionTime: 6340960613392449537, - ElectionDate: "", - Set: "r1"}, - proto.Members{Optime: (*proto.Optime)(nil), - OptimeDate: "", - InfoMessage: "", - Id: 1, - Name: "localhost:17002", - Health: 1, - StateStr: "SECONDARY", - Uptime: 113031, - ConfigVersion: 1, - Self: false, - State: 2, - ElectionTime: 0, - ElectionDate: "", - Set: "r1"}, - proto.Members{Optime: (*proto.Optime)(nil), - OptimeDate: "", - InfoMessage: "", - Id: 2, - Name: "localhost:17003", - Health: 1, - StateStr: "SECONDARY", - Uptime: 113031, - ConfigVersion: 1, - Self: false, - State: 2, - ElectionTime: 0, - ElectionDate: "", - Set: "r1", - }} - - database := pmgomock.NewMockDatabaseManager(ctrl) - ss := proto.ServerStatus{} - test.LoadJson("test/sample/serverstatus.json", &ss) - - dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) - session.EXPECT().Run(bson.M{"replSetGetStatus": 1}, gomock.Any()).SetArg(1, mockrss) - - dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) - session.EXPECT().DB("admin").Return(database) - database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) - session.EXPECT().Close() - - dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) - session.EXPECT().DB("admin").Return(database) - database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) - session.EXPECT().Close() - - dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) - session.EXPECT().DB("admin").Return(database) - database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss) - session.EXPECT().Close() - - session.EXPECT().Close() - - di := &mgo.DialInfo{Addrs: []string{"localhost"}} - rss, err := GetReplicasetMembers(dialer, []string{"localhost"}, di) - if err != nil { - t.Errorf("getReplicasetMembers: %v", err) - } - if !reflect.DeepEqual(rss, expect) { - t.Errorf("getReplicasetMembers:\ngot %#v\nwant: %#v\n", rss, expect) - } - -} - -func TestGetHostnames(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - dialer := pmgomock.NewMockDialer(ctrl) - session := pmgomock.NewMockSessionManager(ctrl) - - mockShardsInfo := proto.ShardsInfo{ - Shards: []proto.Shard{ - proto.Shard{ - ID: "r1", - Host: "r1/localhost:17001,localhost:17002,localhost:17003", - }, - proto.Shard{ - ID: "r2", - Host: "r2/localhost:18001,localhost:18002,localhost:18003", - }, - }, - OK: 1, - } - - dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil) - session.EXPECT().Run("listShards", gomock.Any()).SetArg(1, mockShardsInfo) - session.EXPECT().Close() - - expect := []string{"localhost", "localhost:17001", "localhost:18001"} - di := &mgo.DialInfo{Addrs: []string{"localhost"}} - rss, err := getHostnames(dialer, di) - if err != nil { - t.Errorf("getHostnames: %v", err) - } - if !reflect.DeepEqual(rss, expect) { - t.Errorf("getHostnames: got %+v, expected: %+v\n", rss, expect) - } -} - func TestIsPrivateNetwork(t *testing.T) { //privateCIDRs := []string{"10.0.0.0/24", "172.16.0.0/20", "192.168.0.0/16"} testdata := diff --git a/src/go/pt-mongodb-summary/oplog/oplog.go b/src/go/pt-mongodb-summary/oplog/oplog.go index d5c3973e..4130b2c8 100644 --- a/src/go/pt-mongodb-summary/oplog/oplog.go +++ b/src/go/pt-mongodb-summary/oplog/oplog.go @@ -22,7 +22,7 @@ func GetOplogInfo(hostnames []string, di *mgo.DialInfo) ([]proto.OplogInfo, erro di.Addrs = []string{hostname} session, err := mgo.DialWithInfo(di) if err != nil { - return nil, errors.Wrapf(err, "cannot connect to %s", hostname) + continue } defer session.Close() @@ -74,7 +74,7 @@ func GetOplogInfo(hostnames []string, di *mgo.DialInfo) ([]proto.OplogInfo, erro replSetStatus := proto.ReplicaSetStatus{} err = session.Run(bson.M{"replSetGetStatus": 1}, &replSetStatus) if err != nil { - return nil, errors.Wrap(err, "cannot get ReplicaSetStatus") + continue } for _, member := range replSetStatus.Members { diff --git a/src/go/pt-mongodb-summary/templates/hostinfo.go b/src/go/pt-mongodb-summary/templates/hostinfo.go index 8bdff0c7..cc4b3f81 100644 --- a/src/go/pt-mongodb-summary/templates/hostinfo.go +++ b/src/go/pt-mongodb-summary/templates/hostinfo.go @@ -1,10 +1,14 @@ package templates const HostInfo = `# This host +{{ if .ProcPath -}} # Mongo Executable ####################################################################################### - Path to executable | {{.ProcPath}} -# Report On {{.ThisHostID}} ######################################## - User | {{.ProcUserName}} + Path to executable | {{.ProcPath }} +{{ end -}} +# Report On {{.Hostname}} ######################################## +{{- if .ProcUserName }} + User | {{.ProcUserName }} +{{- end }} PID Owner | {{.ProcessName}} Hostname | {{.Hostname}} Version | {{.Version}} diff --git a/src/go/pt-mongodb-summary/templates/replicaset.go b/src/go/pt-mongodb-summary/templates/replicaset.go index b32ae648..13c25f4b 100644 --- a/src/go/pt-mongodb-summary/templates/replicaset.go +++ b/src/go/pt-mongodb-summary/templates/replicaset.go @@ -2,10 +2,10 @@ package templates const Replicas = ` # Instances ############################################################################################## - PID Host Type ReplSet Engine + PID Host Type ReplSet Engine {{- if . -}} {{- range . }} -{{printf "% 3d" .ID}} {{printf "%-30s" .Name}} {{printf "%-30s" .StateStr}} {{ if .Set }}{{printf "%-10s" .Set }}{{else}}- {{end}} {{printf "%20s" .StorageEngine.Name -}} +{{printf "% 6d" .ID}} {{printf "%-30s" .Name}} {{printf "%-25s" .StateStr}} {{ if .Set }}{{printf "%-10s" .Set }}{{else}}- {{end}} {{printf "%20s" .StorageEngine.Name -}} {{end}} {{else}} no replica sets found