Fixes for PT-66

This commit is contained in:
Carlos Salguero
2017-02-09 20:51:39 -03:00
parent 5c16c42ae0
commit 68700a844c
8 changed files with 388 additions and 279 deletions

View File

@@ -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
}

View File

@@ -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 :=

View File

@@ -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 {

View File

@@ -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}}

View File

@@ -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