moving mongodb code into toolkit repo

This commit is contained in:
Carlos Salguero
2016-12-11 23:18:56 -03:00
parent 12d65fc180
commit 47c0db3650
67 changed files with 4546 additions and 0 deletions

12
src/go/pt-mongodb-summary/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
pt-mongodb-summary
pt-mongodb-summary_darwin_amd64
pt-mongodb-summary_freebsd_386
pt-mongodb-summary_freebsd_amd64
pt-mongodb-summary_linux_386
pt-mongodb-summary_linux_amd64
pt-mongodb-summary_linux_arm
pt-mongodb-summary_windows_386.exe
pt-mongodb-summary_windows_amd64.exe
coverage.out
vendor/*
!vendor/vendor.json

View File

@@ -0,0 +1,91 @@
#pt-mongodb-summary
pt-mongodb-summary collects information about a MongoDB cluster.
##Usage
pt-mongodb-summary [options] [host:[port]]
Default host:port is `localhost:27017`.
For better results, host must be a **mongos** server.
##Binaries
Please check the [releases](https://github.com/percona/toolkit-go/releases) tab to download the binaries.
###Paramters
|Short|Long|default||
|---|---|---|---|
|u|user|empty|user name to use when connecting if DB auth is enabled|
|p|password|empty|password to use when connecting if DB auth is enabled|
|a|auth-db|admin|database used to establish credentials and privileges with a MongoDB server|
`-p` is an optional parameter. If it is used it shouldn't have a blank between the parameter and its value: `-p<password>`
It can be also used as `-p` without specifying a password; in that case, the program will ask the password to avoid using a password in the command line.
###Output example
```
# Instances ####################################################################################
ID Host Type ReplSet Engine Status
0 localhost:17001 PRIMARY r1
1 localhost:17002 SECONDARY r1
2 localhost:17003 SECONDARY r1
0 localhost:18001 PRIMARY r2
1 localhost:18002 SECONDARY r2
2 localhost:18003 SECONDARY r2
# This host
# Mongo Executable #############################################################################
Path to executable | /home/karl/tmp/MongoDB32Labs/3.0/bin/mongos
Has symbols | No
# Report On 0 ########################################
User | karl
PID Owner | mongos
Time | 2016-10-30 00:18:49 -0300 ART
Hostname | karl-HP-ENVY
Version | 3.0.11
Built On | Linux x86_64
Started | 2016-10-30 00:18:49 -0300 ART
Databases | 0
Collections | 0
Datadir | /data/db
Processes | 0
Process Type | mongos
# Running Ops ##################################################################################
Type Min Max Avg
Insert 0 0 0/5s
Query 0 0 0/5s
Update 0 0 0/5s
Delete 0 0 0/5s
GetMore 0 0 0/5s
Command 0 22 16/5s
# Security #####################################################################################
Users 0
Roles 0
Auth disabled
SSL disabled
# Oplog ########################################################################################
Oplog Size 18660 Mb
Oplog Used 55 Mb
Oplog Length 0.91 hours
Last Election 2016-10-30 00:18:44 -0300 ART
# Cluster wide #################################################################################
Databases: 3
Collections: 17
Sharded Collections: 1
Unsharded Collections: 16
Sharded Data Size: 68 GB
Unsharded Data Size: 0 KB
# Balancer (per day)
Success: 6
Failed: 0
Splits: 0
Drops: 0
```

View File

@@ -0,0 +1,658 @@
package main
import (
"fmt"
"html/template"
"log"
"os"
"strings"
"time"
"github.com/howeyc/gopass"
"github.com/pborman/getopt"
"github.com/percona/pmgo"
"github.com/percona/toolkit-go/mongolib/proto"
"github.com/percona/toolkit-go/pt-mongodb-summary/templates"
"github.com/pkg/errors"
"github.com/shirou/gopsutil/process"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
var (
Version string
Build string
)
type hostInfo struct {
ThisHostID int
Hostname string
HostOsType string
HostSystemCPUArch string
HostDatabases int
HostCollections int
DBPath string
ProcPath string
ProcUserName string
ProcCreateTime time.Time
ProcProcessCount int
// Server Status
ProcessName string
ReplicasetName string
Version string
NodeType string
}
type procInfo struct {
CreateTime time.Time
Path string
UserName string
Error error
}
type security struct {
Users int
Roles int
Auth string
SSL string
}
type timedStats struct {
Min int64
Max int64
Total int64
Avg int64
}
type opCounters struct {
Insert timedStats
Query timedStats
Update timedStats
Delete timedStats
GetMore timedStats
Command timedStats
SampleRate time.Duration
}
type databases struct {
Databases []struct {
Name string `bson:"name"`
SizeOnDisk int64 `bson:"sizeOnDisk"`
Empty bool `bson:"empty"`
Shards map[string]int64 `bson:"shards"`
} `bson:"databases"`
TotalSize int64 `bson:"totalSize"`
TotalSizeMb int64 `bson:"totalSizeMb"`
OK bool `bson:"ok"`
}
type clusterwideInfo struct {
TotalDBsCount int
TotalCollectionsCount int
ShardedColsCount int
UnshardedColsCount int
ShardedDataSize int64 // bytes
ShardedDataSizeScaled float64
ShardedDataSizeScale string
UnshardedDataSize int64 // bytes
UnshardedDataSizeScaled float64
UnshardedDataSizeScale string
}
type options struct {
Host string
User string
Password string
AuthDB string
Debug bool
Version bool
}
func main() {
opts := options{Host: "localhost:27017"}
help := getopt.BoolLong("help", '?', "Show help")
getopt.BoolVarLong(&opts.Version, "version", 'v', "", "show version & exit")
getopt.StringVarLong(&opts.User, "user", 'u', "", "username")
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.SetParameters("host[:port]")
getopt.Parse()
if *help {
getopt.Usage()
return
}
args := getopt.Args() // positional arg
if len(args) > 0 {
opts.Host = args[0]
}
if opts.Version {
fmt.Println("pt-mongodb-summary")
fmt.Printf("Version %s\n", Version)
fmt.Printf("Build: %s\n", Build)
return
}
if getopt.IsSet("password") && opts.Password == "" {
print("Password: ")
pass, err := gopass.GetPasswd()
if err != nil {
fmt.Println(err)
os.Exit(2)
}
opts.Password = string(pass)
}
di := &mgo.DialInfo{
Username: opts.User,
Password: opts.Password,
Addrs: []string{opts.Host},
FailFast: true,
Source: opts.AuthDB,
}
dialer := pmgo.NewDialer()
hostnames, err := getHostnames(dialer, di)
session, err := dialer.DialWithInfo(di)
if err != nil {
log.Printf("cannot connect to the db: %s", err)
os.Exit(1)
}
defer session.Close()
if replicaMembers, err := GetReplicasetMembers(dialer, hostnames, di); err != nil {
log.Printf("[Error] cannot get replicaset members: %v\n", err)
} else {
t := template.Must(template.New("replicas").Parse(templates.Replicas))
t.Execute(os.Stdout, replicaMembers)
}
//
if hostInfo, err := GetHostinfo(session); err != nil {
log.Printf("[Error] cannot get host info: %v\n", err)
} else {
t := template.Must(template.New("hosttemplateData").Parse(templates.HostInfo))
t.Execute(os.Stdout, hostInfo)
}
var sampleCount int64 = 5
var sampleRate time.Duration = 1 // in seconds
if rops, err := GetOpCountersStats(session, sampleCount, sampleRate); err != nil {
log.Printf("[Error] cannot get Opcounters stats: %v\n", err)
} else {
t := template.Must(template.New("runningOps").Parse(templates.RunningOps))
t.Execute(os.Stdout, rops)
}
if security, err := GetSecuritySettings(session); 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)
}
if oplogInfo, err := GetOplogInfo(hostnames, di); err != nil {
log.Printf("[Error] 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])
}
}
if cwi, err := GetClusterwideInfo(session); err != nil {
log.Printf("[Error] cannot get cluster wide info: %v\n", err)
} else {
t := template.Must(template.New("clusterwide").Parse(templates.Clusterwide))
t.Execute(os.Stdout, cwi)
}
if bs, err := GetBalancerStats(session); err != nil {
log.Printf("[Error] cannot get balancer stats: %v\n", err)
} else {
t := template.Must(template.New("balancer").Parse(templates.BalancerStats))
t.Execute(os.Stdout, bs)
}
}
func GetHostinfo2(session pmgo.SessionManager) (*hostInfo, error) {
hi := proto.HostInfo{}
if err := session.Run(bson.M{"hostInfo": 1}, &hi); err != nil {
return nil, errors.Wrap(err, "GetHostInfo.hostInfo")
}
cmdOpts := proto.CommandLineOptions{}
err := session.DB("admin").Run(bson.D{{"getCmdLineOpts", 1}, {"recordStats", 1}}, &cmdOpts)
if err != nil {
return nil, errors.Wrap(err, "cannot get command line options")
}
ss := proto.ServerStatus{}
if err := session.DB("admin").Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, &ss); err != nil {
return nil, errors.Wrap(err, "GetHostInfo.serverStatus")
}
pi := procInfo{}
if err := getProcInfo(int32(ss.Pid), &pi); err != nil {
pi.Error = err
}
nodeType, _ := getNodeType(session)
i := &hostInfo{
Hostname: hi.System.Hostname,
HostOsType: hi.Os.Type,
HostSystemCPUArch: hi.System.CpuArch,
HostDatabases: hi.DatabasesCount,
HostCollections: hi.CollectionsCount,
DBPath: "", // Sets default. It will be overriden later if necessary
ProcessName: ss.Process,
Version: ss.Version,
NodeType: nodeType,
ProcPath: pi.Path,
ProcUserName: pi.UserName,
ProcCreateTime: pi.CreateTime,
}
if ss.Repl != nil {
i.ReplicasetName = ss.Repl.SetName
}
if cmdOpts.Parsed.Storage.DbPath != "" {
i.DBPath = cmdOpts.Parsed.Storage.DbPath
}
return i, nil
}
func GetHostinfo(session pmgo.SessionManager) (*hostInfo, error) {
hi := proto.HostInfo{}
if err := session.Run(bson.M{"hostInfo": 1}, &hi); err != nil {
return nil, errors.Wrap(err, "GetHostInfo.hostInfo")
}
cmdOpts := proto.CommandLineOptions{}
err := session.DB("admin").Run(bson.D{{"getCmdLineOpts", 1}, {"recordStats", 1}}, &cmdOpts)
if err != nil {
return nil, errors.Wrap(err, "cannot get command line options")
}
ss := proto.ServerStatus{}
if err := session.DB("admin").Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, &ss); err != nil {
return nil, errors.Wrap(err, "GetHostInfo.serverStatus")
}
pi := procInfo{}
if err := getProcInfo(int32(ss.Pid), &pi); err != nil {
pi.Error = err
}
nodeType, _ := getNodeType(session)
i := &hostInfo{
Hostname: hi.System.Hostname,
HostOsType: hi.Os.Type,
HostSystemCPUArch: hi.System.CpuArch,
HostDatabases: hi.DatabasesCount,
HostCollections: hi.CollectionsCount,
DBPath: "", // Sets default. It will be overriden later if necessary
ProcessName: ss.Process,
Version: ss.Version,
NodeType: nodeType,
ProcPath: pi.Path,
ProcUserName: pi.UserName,
ProcCreateTime: pi.CreateTime,
}
if ss.Repl != nil {
i.ReplicasetName = ss.Repl.SetName
}
if cmdOpts.Parsed.Storage.DbPath != "" {
i.DBPath = cmdOpts.Parsed.Storage.DbPath
}
return i, nil
}
func getHostnames(dialer pmgo.Dialer, di *mgo.DialInfo) ([]string, error) {
session, err := dialer.DialWithInfo(di)
if err != nil {
return nil, err
}
defer session.Close()
shardsInfo := &proto.ShardsInfo{}
err = session.Run("listShards", shardsInfo)
if err != nil {
return nil, errors.Wrap(err, "cannot list shards")
}
hostnames := []string{di.Addrs[0]}
if shardsInfo != nil {
for _, shardInfo := range shardsInfo.Shards {
m := strings.Split(shardInfo.Host, "/")
h := strings.Split(m[1], ",")
hostnames = append(hostnames, h[0])
}
}
return hostnames, nil
}
func GetClusterwideInfo(session pmgo.SessionManager) (*clusterwideInfo, error) {
var databases databases
err := session.Run(bson.M{"listDatabases": 1}, &databases)
if err != nil {
return nil, errors.Wrap(err, "GetClusterwideInfo.listDatabases ")
}
cwi := &clusterwideInfo{
TotalDBsCount: len(databases.Databases),
}
configDB := session.DB("config")
for _, db := range databases.Databases {
collections, err := session.DB(db.Name).CollectionNames()
if err != nil {
continue
}
cwi.TotalCollectionsCount += len(collections)
if len(db.Shards) == 1 {
cwi.UnshardedDataSize += db.SizeOnDisk
continue
}
cwi.ShardedDataSize += db.SizeOnDisk
colsCount, _ := configDB.C("collections").Find(bson.M{"_id": bson.RegEx{"^" + db.Name, ""}}).Count()
cwi.ShardedColsCount += colsCount
}
cwi.UnshardedColsCount = cwi.TotalCollectionsCount - cwi.ShardedColsCount
cwi.ShardedDataSizeScaled, cwi.ShardedDataSizeScale = sizeAndUnit(cwi.ShardedDataSize)
cwi.UnshardedDataSizeScaled, cwi.UnshardedDataSizeScale = sizeAndUnit(cwi.UnshardedDataSize)
return cwi, nil
}
func sizeAndUnit(size int64) (float64, string) {
unit := []string{"KB", "MB", "GB", "TB"}
idx := 0
newSize := float64(size)
for newSize > 1024 {
newSize /= 1024
idx++
}
newSize = float64(int64(newSize*100) / 1000)
return newSize, unit[idx]
}
func GetReplicasetMembers(dialer pmgo.Dialer, hostnames []string, di *mgo.DialInfo) ([]proto.Members, error) {
replicaMembers := []proto.Members{}
for _, hostname := range hostnames {
di.Addrs = []string{hostname}
session, err := dialer.DialWithInfo(di)
if err != nil {
return nil, errors.Wrapf(err, "getReplicasetMembers. cannot connect to %s", hostname)
}
defer session.Close()
rss := proto.ReplicaSetStatus{}
err = session.Run(bson.M{"replSetGetStatus": 1}, &rss)
if err != nil {
continue // If a host is a mongos we cannot get info but is not a real error
}
for _, m := range rss.Members {
m.Set = rss.Set
replicaMembers = append(replicaMembers, m)
}
}
return replicaMembers, nil
}
func GetSecuritySettings(session pmgo.SessionManager) (*security, error) {
s := security{
Auth: "disabled",
SSL: "disabled",
}
cmdOpts := proto.CommandLineOptions{}
err := session.DB("admin").Run(bson.D{{"getCmdLineOpts", 1}, {"recordStats", 1}}, &cmdOpts)
if err != nil {
return nil, errors.Wrap(err, "cannot get command line options")
}
if cmdOpts.Security.Authorization != "" || cmdOpts.Security.KeyFile != "" {
s.Auth = "enabled"
}
if cmdOpts.Parsed.Net.SSL.Mode != "" && cmdOpts.Parsed.Net.SSL.Mode != "disabled" {
s.SSL = cmdOpts.Parsed.Net.SSL.Mode
}
s.Users, err = session.DB("admin").C("system.users").Count()
if err != nil {
return nil, errors.Wrap(err, "cannot get users count")
}
s.Roles, err = session.DB("admin").C("system.roles").Count()
if err != nil {
return nil, errors.Wrap(err, "cannot get roles count")
}
return &s, nil
}
func getNodeType(session pmgo.SessionManager) (string, error) {
md := proto.MasterDoc{}
err := session.Run("isMaster", &md)
if err != nil {
return "", err
}
if md.SetName != nil || md.Hosts != nil {
return "replset", nil
} else if md.Msg == "isdbgrid" {
// isdbgrid is always the msg value when calling isMaster on a mongos
// see http://docs.mongodb.org/manual/core/sharded-cluster-query-router/
return "mongos", nil
}
return "mongod", nil
}
func GetOpCountersStats(session pmgo.SessionManager, count int64, sleep time.Duration) (*opCounters, error) {
oc := &opCounters{}
ss := proto.ServerStatus{}
err := session.DB("admin").Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, &ss)
if err != nil {
return nil, errors.Wrap(err, "GetOpCountersStats.ServerStatus")
}
ticker := time.NewTicker(sleep)
for i := int64(0); i < count-1; i++ {
<-ticker.C
err := session.DB("admin").Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, &ss)
if err != nil {
continue
}
// Insert
if ss.Opcounters.Insert > oc.Insert.Max {
oc.Insert.Max = ss.Opcounters.Insert
}
if ss.Opcounters.Insert < oc.Insert.Min {
oc.Insert.Min = ss.Opcounters.Insert
}
oc.Insert.Total += ss.Opcounters.Insert
// Query ---------------------------------------
if ss.Opcounters.Query > oc.Query.Max {
oc.Query.Max = ss.Opcounters.Query
}
if ss.Opcounters.Query < oc.Query.Min {
oc.Query.Min = ss.Opcounters.Query
}
oc.Query.Total += ss.Opcounters.Query
// Command -------------------------------------
if ss.Opcounters.Command > oc.Command.Max {
oc.Command.Max = ss.Opcounters.Command
}
if ss.Opcounters.Command < oc.Command.Min {
oc.Command.Min = ss.Opcounters.Command
}
oc.Command.Total += ss.Opcounters.Command
// Update --------------------------------------
if ss.Opcounters.Update > oc.Update.Max {
oc.Update.Max = ss.Opcounters.Update
}
if ss.Opcounters.Update < oc.Update.Min {
oc.Update.Min = ss.Opcounters.Update
}
oc.Update.Total += ss.Opcounters.Update
// Delete --------------------------------------
if ss.Opcounters.Delete > oc.Delete.Max {
oc.Delete.Max = ss.Opcounters.Delete
}
if ss.Opcounters.Delete < oc.Delete.Min {
oc.Delete.Min = ss.Opcounters.Delete
}
oc.Delete.Total += ss.Opcounters.Delete
// GetMore -------------------------------------
if ss.Opcounters.GetMore > oc.GetMore.Max {
oc.GetMore.Max = ss.Opcounters.GetMore
}
if ss.Opcounters.GetMore < oc.GetMore.Min {
oc.GetMore.Min = ss.Opcounters.GetMore
}
oc.GetMore.Total += ss.Opcounters.GetMore
}
ticker.Stop()
oc.Insert.Avg = oc.Insert.Total / count
oc.Query.Avg = oc.Query.Total / count
oc.Update.Avg = oc.Update.Total / count
oc.Delete.Avg = oc.Delete.Total / count
oc.GetMore.Avg = oc.GetMore.Total / count
oc.Command.Avg = oc.Command.Total / count
//
oc.SampleRate = time.Duration(count) * time.Second * sleep
return oc, nil
}
func getProcInfo(pid int32, templateData *procInfo) error {
//proc, err := process.NewProcess(templateData.ServerStatus.Pid)
proc, err := process.NewProcess(pid)
if err != nil {
return fmt.Errorf("cannot get process %d\n", pid)
}
ct, err := proc.CreateTime()
if err != nil {
return err
}
templateData.CreateTime = time.Unix(ct/1000, 0)
templateData.Path, err = proc.Exe()
if err != nil {
return err
}
templateData.UserName, err = proc.Username()
if err != nil {
return err
}
return nil
}
func getDbsAndCollectionsCount(hostnames []string) (int, int, error) {
dbnames := make(map[string]bool)
colnames := make(map[string]bool)
for _, hostname := range hostnames {
session, err := mgo.Dial(hostname)
if err != nil {
continue
}
dbs, err := session.DatabaseNames()
if err != nil {
continue
}
for _, dbname := range dbs {
dbnames[dbname] = true
cols, err := session.DB(dbname).CollectionNames()
if err != nil {
continue
}
for _, colname := range cols {
colnames[dbname+"."+colname] = true
}
}
}
return len(dbnames), len(colnames), nil
}
func GetBalancerStats(session pmgo.SessionManager) (*proto.BalancerStats, error) {
scs, err := GetShardingChangelogStatus(session)
if err != nil {
return nil, err
}
s := &proto.BalancerStats{}
for _, item := range *scs.Items {
event := item.Id.Event
note := item.Id.Note
count := item.Count
switch event {
case "moveChunk.to", "moveChunk.from", "moveChunk.commit":
if note == "success" || note == "" {
s.Success += int64(count)
} else {
s.Failed += int64(count)
}
case "split", "multi-split":
s.Splits += int64(count)
case "dropCollection", "dropCollection.start", "dropDatabase", "dropDatabase.start":
s.Drops++
}
}
return s, nil
}
func GetShardingChangelogStatus(session pmgo.SessionManager) (*proto.ShardingChangelogStats, error) {
var qresults []proto.ShardingChangelogSummary
coll := session.DB("config").C("changelog")
match := bson.M{"time": bson.M{"$gt": time.Now().Add(-240 * time.Hour)}}
group := bson.M{"_id": bson.M{"event": "$what", "note": "$details.note"}, "count": bson.M{"$sum": 1}}
err := coll.Pipe([]bson.M{{"$match": match}, {"$group": group}}).All(&qresults)
if err != nil {
return nil, errors.Wrap(err, "GetShardingChangelogStatus.changelog.find")
}
return &proto.ShardingChangelogStats{
Items: &qresults,
}, nil
}

View File

@@ -0,0 +1,369 @@
package main
import (
"fmt"
"reflect"
"testing"
"time"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"github.com/golang/mock/gomock"
"github.com/percona/pmgo/pmgomock"
"github.com/percona/toolkit-go/mongolib/proto"
"github.com/percona/toolkit-go/pt-mongodb-summary/test"
)
func TestGetOpCounterStats(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
session := pmgomock.NewMockSessionManager(ctrl)
database := pmgomock.NewMockDatabaseManager(ctrl)
ss := proto.ServerStatus{}
test.LoadJson("test/sample/serverstatus.json", &ss)
// serverStatus for getOpCountersStats
session.EXPECT().DB("admin").Return(database)
database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss)
session.EXPECT().DB("admin").Return(database)
database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss)
session.EXPECT().DB("admin").Return(database)
database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss)
session.EXPECT().DB("admin").Return(database)
database.EXPECT().Run(bson.D{{"serverStatus", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, ss)
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 sampleRate time.Duration = 10 * time.Millisecond // in seconds
expect := timedStats{Min: 0, Max: 473, Total: 1892, Avg: 378}
os, err := GetOpCountersStats(session, sampleCount, sampleRate)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(expect, os.Command) {
t.Errorf("getOpCountersStats. got: %+v\nexpect: %+v\n", os.Command, expect)
}
}
func TestSecurityOpts(t *testing.T) {
cmdopts := []proto.CommandLineOptions{
// 1
proto.CommandLineOptions{
Parsed: proto.Parsed{
Net: proto.Net{
SSL: proto.SSL{
Mode: "",
},
},
},
Security: proto.Security{
KeyFile: "",
Authorization: "",
},
},
// 2
proto.CommandLineOptions{
Parsed: proto.Parsed{
Net: proto.Net{
SSL: proto.SSL{
Mode: "",
},
},
},
Security: proto.Security{
KeyFile: "a file",
Authorization: "",
},
},
// 3
proto.CommandLineOptions{
Parsed: proto.Parsed{
Net: proto.Net{
SSL: proto.SSL{
Mode: "",
},
},
},
Security: proto.Security{
KeyFile: "",
Authorization: "something here",
},
},
// 4
proto.CommandLineOptions{
Parsed: proto.Parsed{
Net: proto.Net{
SSL: proto.SSL{
Mode: "super secure",
},
},
},
Security: proto.Security{
KeyFile: "",
Authorization: "",
},
},
}
expect := []*security{
// 1
&security{
Users: 1,
Roles: 2,
Auth: "disabled",
SSL: "disabled",
},
// 2
&security{
Users: 1,
Roles: 2,
Auth: "enabled",
SSL: "disabled",
},
// 3
&security{
Users: 1,
Roles: 2,
Auth: "enabled",
SSL: "disabled",
},
// 4
&security{
Users: 1,
Roles: 2,
Auth: "disabled",
SSL: "super secure",
},
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
session := pmgomock.NewMockSessionManager(ctrl)
database := pmgomock.NewMockDatabaseManager(ctrl)
usersCol := pmgomock.NewMockCollectionManager(ctrl)
rolesCol := pmgomock.NewMockCollectionManager(ctrl)
for i, cmd := range cmdopts {
session.EXPECT().DB("admin").Return(database)
database.EXPECT().Run(bson.D{{"getCmdLineOpts", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, cmd)
session.EXPECT().DB("admin").Return(database)
database.EXPECT().C("system.users").Return(usersCol)
usersCol.EXPECT().Count().Return(1, nil)
session.EXPECT().DB("admin").Return(database)
database.EXPECT().C("system.roles").Return(rolesCol)
rolesCol.EXPECT().Count().Return(2, nil)
got, err := GetSecuritySettings(session)
if err != nil {
t.Errorf("cannot get sec settings: %v", err)
}
if !reflect.DeepEqual(got, expect[i]) {
t.Errorf("got: %+v, expect: %+v\n", got, expect[i])
}
}
}
func TestGetNodeType(t *testing.T) {
md := []struct {
in proto.MasterDoc
out string
}{
{proto.MasterDoc{SetName: "name"}, "replset"},
{proto.MasterDoc{Msg: "isdbgrid"}, "mongos"},
{proto.MasterDoc{Msg: "a msg"}, "mongod"},
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
session := pmgomock.NewMockSessionManager(ctrl)
for _, m := range md {
session.EXPECT().Run("isMaster", gomock.Any()).SetArg(1, m.in)
nodeType, err := getNodeType(session)
if err != nil {
t.Errorf("cannot get node type: %+v, error: %s\n", m.in, err)
}
if nodeType != m.out {
t.Errorf("invalid node type. got %s, expect: %s\n", nodeType, m.out)
}
}
session.EXPECT().Run("isMaster", gomock.Any()).Return(fmt.Errorf("some fake error"))
nodeType, err := getNodeType(session)
if err == nil {
t.Errorf("error expected, got nil")
}
if nodeType != "" {
t.Errorf("expected blank node type, got %s", nodeType)
}
}
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",
}}
dialer.EXPECT().DialWithInfo(gomock.Any()).Return(session, nil)
session.EXPECT().Run(bson.M{"replSetGetStatus": 1}, gomock.Any()).SetArg(1, mockrss)
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: got %+v, expected: %+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)
}
}

View File

@@ -0,0 +1,121 @@
package main
import (
"fmt"
"sort"
"time"
"github.com/percona/toolkit-go/mongolib/proto"
"github.com/pkg/errors"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
func GetOplogInfo(hostnames []string, di *mgo.DialInfo) ([]proto.OplogInfo, error) {
results := proto.OpLogs{}
for _, hostname := range hostnames {
result := proto.OplogInfo{
Hostname: hostname,
}
di.Addrs = []string{hostname}
session, err := mgo.DialWithInfo(di)
if err != nil {
return nil, errors.Wrapf(err, "cannot connect to %s", hostname)
}
defer session.Close()
oplogCol, err := getOplogCollection(session)
if err != nil {
continue
}
olEntry, err := getOplogEntry(session, oplogCol)
if err != nil {
return nil, errors.Wrap(err, "getOplogInfo -> GetOplogEntry")
}
result.Size = olEntry.Options.Size / (1024 * 1024)
var colStats proto.OplogColStats
err = session.DB("local").Run(bson.M{"collStats": oplogCol}, &colStats)
if err != nil {
return nil, errors.Wrapf(err, "cannot get collStats for collection %s", oplogCol)
}
result.UsedMB = colStats.Size / (1024 * 1024)
var firstRow, lastRow proto.OplogRow
err = session.DB("local").C(oplogCol).Find(nil).Sort("$natural").One(&firstRow)
if err != nil {
return nil, errors.Wrap(err, "cannot read first oplog row")
}
err = session.DB("local").C(oplogCol).Find(nil).Sort("-$natural").One(&lastRow)
if err != nil {
return nil, errors.Wrap(err, "cannot read last oplog row")
}
// https://docs.mongodb.com/manual/reference/bson-types/#timestamps
tfirst := firstRow.Ts >> 32
tlast := lastRow.Ts >> 32
result.TimeDiff = tlast - tfirst
result.TimeDiffHours = float64(result.TimeDiff) / 3600
result.TFirst = time.Unix(tfirst, 0)
result.TLast = time.Unix(tlast, 0)
result.Now = time.Now().UTC()
if result.TimeDiffHours > 24 {
result.Running = fmt.Sprintf("%0.2f days", result.TimeDiffHours/24)
} else {
result.Running = fmt.Sprintf("%0.2f hours", result.TimeDiffHours)
}
replSetStatus := proto.ReplicaSetStatus{}
err = session.Run(bson.M{"replSetGetStatus": 1}, &replSetStatus)
if err != nil {
return nil, errors.Wrap(err, "cannot get ReplicaSetStatus")
}
for _, member := range replSetStatus.Members {
if member.State == 1 {
result.ElectionTime = time.Unix(member.ElectionTime>>32, 0)
break
}
}
results = append(results, result)
}
sort.Sort(results)
return results, nil
}
func getOplogCollection(session *mgo.Session) (string, error) {
oplog := "oplog.rs"
db := session.DB("local")
nsCol := db.C("system.namespaces")
var res interface{}
if err := nsCol.Find(bson.M{"name": "local." + oplog}).One(&res); err == nil {
return oplog, nil
}
oplog = "oplog.$main"
if err := nsCol.Find(bson.M{"name": "local." + oplog}).One(&res); err != nil {
return "", fmt.Errorf("neither master/slave nor replica set replication detected")
}
return oplog, nil
}
func getOplogEntry(session *mgo.Session, oplogCol string) (*proto.OplogEntry, error) {
olEntry := &proto.OplogEntry{}
err := session.DB("local").C("system.namespaces").Find(bson.M{"name": "local." + oplogCol}).One(&olEntry)
if err != nil {
return nil, fmt.Errorf("local.%s, or its options, not found in system.namespaces collection", oplogCol)
}
return olEntry, nil
}

View File

@@ -0,0 +1,9 @@
package templates
const BalancerStats = `
# Balancer (per day)
Success: {{.Success}}
Failed: {{.Failed}}
Splits: {{.Splits}}
Drops: {{.Drops}}
`

View File

@@ -0,0 +1,10 @@
package templates
const Clusterwide = `
# Cluster wide #################################################################################
Databases: {{.TotalDBsCount}}
Collections: {{.TotalCollectionsCount}}
Sharded Collections: {{.ShardedColsCount}}
Unsharded Collections: {{.UnshardedColsCount}}
Sharded Data Size: {{.ShardedDataSizeScaled}} {{.ShardedDataSizeScale}}
Unsharded Data Size: {{.UnshardedDataSizeScaled}} {{.UnshardedDataSizeScale}}`

View File

@@ -0,0 +1,26 @@
package templates
const HostInfo = `# This host
# Mongo Executable #############################################################################
Path to executable | {{.ProcPath}}
Has symbols | No
# Report On {{.ThisHostID}} ########################################
User | {{.ProcUserName}}
PID Owner | {{.ProcessName}}
Time | {{.ProcCreateTime}}
Hostname | {{.Hostname}}
Version | {{.Version}}
Built On | {{.HostOsType}} {{.HostSystemCPUArch}}
Started | {{.ProcCreateTime}}
Databases | {{.HostDatabases}}
Collections | {{.HostCollections}}
{{- if .DBPath }}
Datadir | {{.DBPath}}
{{- end }}
Processes | {{.ProcProcessCount}}
Process Type | {{.NodeType}}
{{ if .ReplicaSetName -}}
ReplSet | {{.ReplicasetName}}
Repl Status |
{{- end -}}
`

View File

@@ -0,0 +1,10 @@
package templates
const Oplog = `
# Oplog ########################################################################################
Oplog Size {{.Size}} Mb
Oplog Used {{.UsedMB}} Mb
Oplog Length {{.Running}}
Last Election {{.ElectionTime}}
`

View File

@@ -0,0 +1,13 @@
package templates
const Replicas = `
# Instances ####################################################################################
ID Host Type ReplSet Engine Status
{{- if . -}}
{{- range . }}
{{printf "% 3d" .Id}} {{printf "%-30s" .Name}} {{printf "%-30s" .StateStr}} {{printf "%10s" .Set -}}
{{end}}
{{else}}
No replica sets found
{{end}}
`

View File

@@ -0,0 +1,13 @@
package templates
const RunningOps = `
# Running Ops ##################################################################################
Type Min Max Avg
Insert {{printf "% 8d" .Insert.Min}} {{printf "% 8d" .Insert.Max}} {{printf "% 8d" .Insert.Avg}}/{{.SampleRate}}
Query {{printf "% 8d" .Query.Min}} {{printf "% 8d" .Query.Max}} {{printf "% 8d" .Query.Avg}}/{{.SampleRate}}
Update {{printf "% 8d" .Update.Min}} {{printf "% 8d" .Update.Max}} {{printf "% 8d" .Update.Avg}}/{{.SampleRate}}
Delete {{printf "% 8d" .Delete.Min}} {{printf "% 8d" .Delete.Max}} {{printf "% 8d" .Delete.Avg}}/{{.SampleRate}}
GetMore {{printf "% 8d" .GetMore.Min}} {{printf "% 8d" .GetMore.Max}} {{printf "% 8d" .GetMore.Avg}}/{{.SampleRate}}
Command {{printf "% 8d" .Command.Min}} {{printf "% 8d" .Command.Max}} {{printf "% 8d" .Command.Avg}}/{{.SampleRate}}
`

View File

@@ -0,0 +1,10 @@
package templates
const Security = `
# Security #####################################################################################
Users {{.Users}}
Roles {{.Roles}}
Auth {{.Auth}}
SSL {{.SSL}}
`

View File

@@ -0,0 +1,10 @@
{
"Version": "3.0.11",
"VersionArray": [ 3, 0, 11, 0 ],
"GitVersion": "48f8b49dc30cc2485c6c1f3db31b723258fcbf39",
"SysInfo": "Linux build2.ny.cbi.10gen.cc 2.6.32-431.3.1.el6.x86_64 #1 SMP Fri Jan 3 21:39:27 UTC 2014 x86_64 BOOST_LIB_VERSION=1_49",
"Bits": 64,
"Debug": false,
"MaxObjectSize": 16777216
}

View File

@@ -0,0 +1,69 @@
{
"Argv": null,
"Ok": 0,
"Parsed": {
"Sharding": {
"ClusterRole": ""
},
"Storage": {
"DbPath": "",
"Engine": ""
},
"SystemLog": {
"Destination": "",
"Path": ""
},
"Net": {
"HTTP": {
"Enabled": false,
"Port": 0,
"JSONPEnabled": false,
"RESTInterfaceEnabled": false
},
"SSL": {
"SSLOnNormalPorts": false,
"Mode": "requireSSL",
"PEMKeyFile": "",
"PEMKeyPassword": "",
"ClusterFile": "",
"ClusterPassword": "",
"CAFile": "",
"CRLFile": "",
"AllowConnectionsWithoutCertificates": false,
"AllowInvalidCertificates": false,
"AllowInvalidHostnames": false,
"DisabledProtocols": "",
"FIPSMode": false
}
},
"ProcessManagement": {
"Fork": false
},
"Replication": {
"ReplSet": ""
}
},
"Security": {
"KeyFile": "",
"ClusterAuthMode": "",
"Authorization": "enabled",
"JavascriptEnabled": false,
"Sasl": {
"HostName": "",
"ServiceName": "",
"SaslauthdSocketPath": ""
},
"EnableEncryption": false,
"EncryptionCipherMode": "",
"EncryptionKeyFile": "",
"Kmip": {
"KeyIdentifier": "",
"RotateMasterKey": false,
"ServerName": "",
"Port": "",
"ClientCertificateFile": "",
"ClientCertificatePassword": "",
"ServerCAFile": ""
}
}
}

View File

@@ -0,0 +1,219 @@
{
"Info": "",
"Inprog": [
{
"Desc": "conn49",
"ConnectionId": 49,
"Opid": 1481,
"Msg": "",
"NumYields": 93,
"Locks": {
"Global": "",
"MMAPV1Journal": "",
"Database": "",
"Collection": "",
"Metadata": "",
"Oplog": ""
},
"WaitingForLock": 0,
"ThreadId": "0x2e60220",
"Active": 1,
"MicrosecsRunning": 857422,
"SecsRunning": 0,
"Op": "getmore",
"Ns": "samples.col1",
"Insert": null,
"PlanSummary": "",
"Client": "127.0.0.1:46290",
"Query": {
"CurrentOp": 0
},
"Progress": {
"Done": 0,
"Total": 0
},
"KillPending": 0,
"LockStats": {
"Global": {
"DeadlockCount": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
},
"AcquireCount": {
"Rr": 186,
"Ww": 0,
"R": 0,
"W": 0
},
"AcquireWaitCount": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
},
"TimeAcquiringMicros": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
}
},
"MMAPV1Journal": {
"acquireCount": {
"r": 93
}
},
"Database": {
"acquireCount": {
"r": 93
}
}
}
},
{
"Desc": "conn53",
"ConnectionId": 53,
"Opid": 1442,
"Msg": "",
"NumYields": 0,
"Locks": {
"Global": "",
"MMAPV1Journal": "",
"Database": "",
"Collection": "",
"Metadata": "",
"Oplog": ""
},
"WaitingForLock": 0,
"ThreadId": "0x2e618e0",
"Active": 1,
"MicrosecsRunning": 4.668058e+06,
"SecsRunning": 4,
"Op": "getmore",
"Ns": "local.oplog.rs",
"Insert": null,
"PlanSummary": "",
"Client": "127.0.0.1:46300",
"Query": {
"CurrentOp": 0
},
"Progress": {
"Done": 0,
"Total": 0
},
"KillPending": 0,
"LockStats": {
"Global": {
"DeadlockCount": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
},
"AcquireCount": {
"Rr": 10,
"Ww": 0,
"R": 0,
"W": 0
},
"AcquireWaitCount": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
},
"TimeAcquiringMicros": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
}
},
"MMAPV1Journal": {
"acquireCount": {
"r": 5
}
},
"Database": {
"acquireCount": {
"r": 5
}
}
}
},
{
"Desc": "conn51",
"ConnectionId": 51,
"Opid": 1433,
"Msg": "",
"NumYields": 0,
"Locks": {
"Global": "",
"MMAPV1Journal": "",
"Database": "",
"Collection": "",
"Metadata": "",
"Oplog": ""
},
"WaitingForLock": 0,
"ThreadId": "0x2e603c0",
"Active": 1,
"MicrosecsRunning": 4.689207e+06,
"SecsRunning": 4,
"Op": "getmore",
"Ns": "local.oplog.rs",
"Insert": null,
"PlanSummary": "",
"Client": "127.0.0.1:46296",
"Query": {
"CurrentOp": 0
},
"Progress": {
"Done": 0,
"Total": 0
},
"KillPending": 0,
"LockStats": {
"Global": {
"DeadlockCount": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
},
"AcquireCount": {
"Rr": 10,
"Ww": 0,
"R": 0,
"W": 0
},
"AcquireWaitCount": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
},
"TimeAcquiringMicros": {
"Rr": 0,
"Ww": 0,
"R": 0,
"W": 0
}
},
"MMAPV1Journal": {
"acquireCount": {
"r": 5
}
},
"Database": {
"acquireCount": {
"r": 5
}
}
}
}
],
"FsyncLock": 0
}

View File

@@ -0,0 +1 @@
null

View File

@@ -0,0 +1,14 @@
[
"actionlog",
"changelog",
"chunks",
"databases",
"lockpings",
"locks",
"mongos",
"settings",
"shards",
"system.indexes",
"tags",
"version"
]

View File

@@ -0,0 +1,6 @@
[
"col1",
"col2",
"system.indexes",
"system.profile"
]

View File

@@ -0,0 +1,5 @@
[
"system.indexes",
"testcol02",
"testcol2"
]

View File

@@ -0,0 +1 @@
null

View File

@@ -0,0 +1,30 @@
{
"Extra": {
"LibcVersion": "2.24",
"PageSize": 4096,
"VersionSignature": "Ubuntu 4.8.0-22.24-generic 4.8.0",
"NumPages": 4.090087e+06,
"VersionString": "Linux version 4.8.0-22-generic (buildd@lgw01-11) (gcc version 6.2.0 20161005 (Ubuntu 6.2.0-5ubuntu12) ) #24-Ubuntu SMP Sat Oct 8 09:15:00 UTC 2016",
"CpuFeatures": "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts",
"CpuFrequencyMHz": "2500.000",
"KernelVersion": "4.8.0-22-generic",
"MaxOpenFiles": 1024
},
"Os": {
"Type": "Linux",
"Version": "16.10",
"Name": "Ubuntu"
},
"System": {
"CurrentTime": "",
"Hostname": "karl-HP-ENVY",
"MemSizeMB": 15976,
"NumCores": 8,
"NumaEnabled": false,
"CpuAddrSize": 64,
"CpuArch": "x86_64"
},
"DatabasesCount": 0,
"CollectionsCount": 0,
"ID": 0
}

View File

@@ -0,0 +1,5 @@
{
"SetName": null,
"Hosts": null,
"Msg": "isdbgrid"
}

View File

@@ -0,0 +1,38 @@
{
"Databases": [
{
"Name": "samples",
"SizeOnDisk": 704643072,
"Empty": false,
"Shards": {
"r1": 218103808,
"r2": 486539264
}
},
{
"Name": "testdb",
"SizeOnDisk": 83886080,
"Empty": false,
"Shards": {
"r1": 83886080
}
},
{
"Name": "testdb02",
"SizeOnDisk": 83886080,
"Empty": false,
"Shards": {
"r2": 83886080
}
},
{
"Name": "config",
"SizeOnDisk": 67108864,
"Empty": false,
"Shards": null
}
],
"TotalSize": 872415232,
"TotalSizeMb": 832,
"OK": true
}

View File

@@ -0,0 +1,58 @@
{
"Date": "",
"MyState": 1,
"Term": 0,
"HeartbeatIntervalMillis": 0,
"Members": [
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 0,
"Name": "localhost:18001",
"Health": 1,
"StateStr": "PRIMARY",
"Uptime": 13502,
"ConfigVersion": 1,
"Self": true,
"State": 1,
"ElectionTime": 6341767496013447169,
"ElectionDate": "",
"Set": ""
},
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 1,
"Name": "localhost:18002",
"Health": 1,
"StateStr": "SECONDARY",
"Uptime": 13499,
"ConfigVersion": 1,
"Self": false,
"State": 2,
"ElectionTime": 0,
"ElectionDate": "",
"Set": ""
},
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 2,
"Name": "localhost:18003",
"Health": 1,
"StateStr": "SECONDARY",
"Uptime": 13497,
"ConfigVersion": 1,
"Self": false,
"State": 2,
"ElectionTime": 0,
"ElectionDate": "",
"Set": ""
}
],
"Ok": 1,
"Set": "r2"
}

View File

@@ -0,0 +1,9 @@
{
"Date": "",
"MyState": 0,
"Term": 0,
"HeartbeatIntervalMillis": 0,
"Members": null,
"Ok": 0,
"Set": ""
}

View File

@@ -0,0 +1,58 @@
{
"Date": "",
"MyState": 1,
"Term": 0,
"HeartbeatIntervalMillis": 0,
"Members": [
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 0,
"Name": "localhost:17001",
"Health": 1,
"StateStr": "PRIMARY",
"Uptime": 15959,
"ConfigVersion": 1,
"Self": true,
"State": 1,
"ElectionTime": 6341767483128545281,
"ElectionDate": "",
"Set": ""
},
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 1,
"Name": "localhost:17002",
"Health": 1,
"StateStr": "SECONDARY",
"Uptime": 15955,
"ConfigVersion": 1,
"Self": false,
"State": 2,
"ElectionTime": 0,
"ElectionDate": "",
"Set": ""
},
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 2,
"Name": "localhost:17003",
"Health": 1,
"StateStr": "SECONDARY",
"Uptime": 15953,
"ConfigVersion": 1,
"Self": false,
"State": 2,
"ElectionTime": 0,
"ElectionDate": "",
"Set": ""
}
],
"Ok": 1,
"Set": "r1"
}

View File

@@ -0,0 +1,58 @@
{
"Date": "",
"MyState": 1,
"Term": 0,
"HeartbeatIntervalMillis": 0,
"Members": [
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 0,
"Name": "localhost:18001",
"Health": 1,
"StateStr": "PRIMARY",
"Uptime": 15955,
"ConfigVersion": 1,
"Self": true,
"State": 1,
"ElectionTime": 6341767496013447169,
"ElectionDate": "",
"Set": ""
},
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 1,
"Name": "localhost:18002",
"Health": 1,
"StateStr": "SECONDARY",
"Uptime": 15952,
"ConfigVersion": 1,
"Self": false,
"State": 2,
"ElectionTime": 0,
"ElectionDate": "",
"Set": ""
},
{
"Optime": null,
"OptimeDate": "",
"InfoMessage": "",
"Id": 2,
"Name": "localhost:18003",
"Health": 1,
"StateStr": "SECONDARY",
"Uptime": 15950,
"ConfigVersion": 1,
"Self": false,
"State": 2,
"ElectionTime": 0,
"ElectionDate": "",
"Set": ""
}
],
"Ok": 1,
"Set": "r2"
}

View File

@@ -0,0 +1,58 @@
{
"Host": "karl-HP-ENVY",
"Version": "3.2.4",
"Process": "mongos",
"Pid": 2658,
"Uptime": 86400,
"UptimeMillis": 86399860,
"UptimeEstimate": 85467,
"LocalTime": "2016-10-19T11:29:55.826-03:00",
"Asserts": {
"msg": 0,
"regular": 0,
"rollovers": 0,
"user": 28,
"warning": 0
},
"BackgroundFlushing": null,
"ExtraInfo": {
"PageFaults": 10,
"HeapUsageBytes": 2.524536e+06,
"Note": "fields vary by platform"
},
"Connections": {
"Current": 3,
"Available": 816,
"TotalCreated": 65
},
"Dur": null,
"GlobalLock": null,
"Locks": null,
"Network": {
"BytesIn": 33608,
"BytesOut": 678809,
"NumRequests": 522
},
"Opcounters": {
"Insert": 0,
"Query": 22,
"Update": 0,
"Delete": 0,
"GetMore": 0,
"Command": 473
},
"OpcountersRepl": null,
"RecordStats": null,
"Mem": {
"Bits": 64,
"Resident": 21,
"Virtual": 228,
"Supported": true,
"Mapped": 0,
"MappedWithJournal": 0
},
"Repl": null,
"ShardCursorType": null,
"StorageEngine": null,
"WiredTiger": null
}

View File

@@ -0,0 +1,14 @@
{
"Shards": [
{
"ID": "r1",
"Host": "r1/localhost:17001,localhost:17002,localhost:17003"
},
{
"ID": "r2",
"Host": "r2/localhost:18001,localhost:18002,localhost:18003"
}
],
"OK": 1
}

View File

@@ -0,0 +1,8 @@
{
"name": "local.oplog.rs",
"options": {
"autoIndexId": false,
"capped": true,
"size": 1.7267414016e+10
}
}

View File

@@ -0,0 +1,69 @@
package test
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
)
func RootDir() (string, error) {
out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err != nil {
rootdir, err := searchDir()
if err != nil {
return "", err
}
return rootdir, nil
}
return strings.Replace(string(out), "\n", "", -1), nil
}
func searchDir() (string, error) {
rootdir := ""
dir, err := os.Getwd()
if err != nil {
return "", err
}
for i := 0; i < 3; i++ {
if FileExists(dir + "/.git") {
rootdir = filepath.Clean(dir + "test")
break
}
dir = dir + "/.."
}
if rootdir == "" {
return "", fmt.Errorf("cannot detect root dir")
}
return rootdir, nil
}
func FileExists(file string) bool {
_, err := os.Lstat(file)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return true
}
func LoadJson(filename string, destination interface{}) error {
dat, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
err = json.Unmarshal(dat, &destination)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,139 @@
{
"comment": "",
"ignore": "test",
"package": [
{
"checksumSHA1": "9NR0rrcAT5J76C5xMS4AVksS9o0=",
"path": "github.com/StackExchange/wmi",
"revision": "e54cbda6595d7293a7a468ccf9525f6bc8887f99",
"revisionTime": "2016-08-11T21:45:55Z"
},
{
"checksumSHA1": "wDZdTaY9JiqqqnF4c3pHP71nWmk=",
"path": "github.com/go-ole/go-ole",
"revision": "7dfdcf409020452e29b4babcbb22f984d2aa308a",
"revisionTime": "2016-07-29T03:38:29Z"
},
{
"checksumSHA1": "qLYVTQDhgrVIeZ2KI9eZV51mmug=",
"path": "github.com/go-ole/go-ole/oleutil",
"revision": "7dfdcf409020452e29b4babcbb22f984d2aa308a",
"revisionTime": "2016-07-29T03:38:29Z"
},
{
"checksumSHA1": "JSHl8b3nI8EWvzm+uyrIqj2Hiu4=",
"path": "github.com/golang/mock/gomock",
"revision": "bd3c8e81be01eef76d4b503f5e687d2d1354d2d9",
"revisionTime": "2016-01-21T18:51:14Z"
},
{
"checksumSHA1": "lJDwzzEBuS9sjxVOSsq7+rvw+cA=",
"path": "github.com/howeyc/gopass",
"revision": "f5387c492211eb133053880d23dfae62aa14123d",
"revisionTime": "2016-10-03T13:09:00Z"
},
{
"checksumSHA1": "nBmwoMSG67LapH7e79RepVUFS/I=",
"path": "github.com/pborman/getopt",
"revision": "e5fda1850301b0c7c7c38959116cbc7913bb00f3",
"revisionTime": "2016-08-19T15:59:08Z"
},
{
"checksumSHA1": "eBc6L91Na66Z5Avo4qloBpPcJFo=",
"path": "github.com/percona/toolkit-go/mongolib/proto",
"revision": "3cd401c8852ad074dbeac7efbef68422c16bfff7",
"revisionTime": "2016-10-30T21:11:33Z"
},
{
"checksumSHA1": "Hky3u+8Rqum+wB5BHMj0A8ZmT4g=",
"path": "github.com/pkg/errors",
"revision": "17b591df37844cde689f4d5813e5cea0927d8dd2",
"revisionTime": "2016-08-22T09:00:10Z"
},
{
"checksumSHA1": "G1oy2AGv5pCnNL0hRfg+mlX4Uq4=",
"path": "github.com/shirou/gopsutil/cpu",
"revision": "4d0c402af66c78735c5ccf820dc2ca7de5e4ff08",
"revisionTime": "2016-07-25T08:59:33Z"
},
{
"checksumSHA1": "/Pngfnhd3BiUzWRjrVOfsWpORRA=",
"path": "github.com/shirou/gopsutil/host",
"revision": "4d0c402af66c78735c5ccf820dc2ca7de5e4ff08",
"revisionTime": "2016-07-25T08:59:33Z"
},
{
"checksumSHA1": "jc2EYr0yhTJIdYJafcNs1ZEXk/o=",
"path": "github.com/shirou/gopsutil/internal/common",
"revision": "4d0c402af66c78735c5ccf820dc2ca7de5e4ff08",
"revisionTime": "2016-07-25T08:59:33Z"
},
{
"checksumSHA1": "LUipApIqOLcKbZfwXknNCifGles=",
"path": "github.com/shirou/gopsutil/mem",
"revision": "4d0c402af66c78735c5ccf820dc2ca7de5e4ff08",
"revisionTime": "2016-07-25T08:59:33Z"
},
{
"checksumSHA1": "k57srhFXKSysrFdEh3b6oiO48hw=",
"path": "github.com/shirou/gopsutil/net",
"revision": "4d0c402af66c78735c5ccf820dc2ca7de5e4ff08",
"revisionTime": "2016-07-25T08:59:33Z"
},
{
"checksumSHA1": "QBFpNvR0Z1r1ojPBgn6b8/PTbm0=",
"path": "github.com/shirou/gopsutil/process",
"revision": "4d0c402af66c78735c5ccf820dc2ca7de5e4ff08",
"revisionTime": "2016-07-25T08:59:33Z"
},
{
"checksumSHA1": "Nve7SpDmjsv6+rhkXAkfg/UQx94=",
"path": "github.com/shirou/w32",
"revision": "bb4de0191aa41b5507caa14b0650cdbddcd9280b",
"revisionTime": "2016-09-30T03:27:40Z"
},
{
"checksumSHA1": "9C4Av3ypK5pi173F76ogJT/d8x4=",
"path": "golang.org/x/crypto/ssh/terminal",
"revision": "b2fa06b6af4b7c9bfeb8569ab7b17f04550717bf",
"revisionTime": "2016-10-28T17:07:08Z"
},
{
"checksumSHA1": "u4rtURsE9Cn7TK4OPwH8axBMK6M=",
"path": "golang.org/x/sys/unix",
"revision": "8d1157a435470616f975ff9bb013bea8d0962067",
"revisionTime": "2016-10-06T02:47:49Z"
},
{
"checksumSHA1": "1D8GzeoFGUs5FZOoyC2DpQg8c5Y=",
"path": "gopkg.in/mgo.v2",
"revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
"revisionTime": "2016-08-18T02:01:20Z"
},
{
"checksumSHA1": "YsB2DChSV9HxdzHaKATllAUKWSI=",
"path": "gopkg.in/mgo.v2/bson",
"revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
"revisionTime": "2016-08-18T02:01:20Z"
},
{
"checksumSHA1": "XQsrqoNT1U0KzLxOFcAZVvqhLfk=",
"path": "gopkg.in/mgo.v2/internal/json",
"revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
"revisionTime": "2016-08-18T02:01:20Z"
},
{
"checksumSHA1": "LEvMCnprte47qdAxWvQ/zRxVF1U=",
"path": "gopkg.in/mgo.v2/internal/sasl",
"revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
"revisionTime": "2016-08-18T02:01:20Z"
},
{
"checksumSHA1": "+1WDRPaOphSCmRMxVPIPBV4aubc=",
"path": "gopkg.in/mgo.v2/internal/scram",
"revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
"revisionTime": "2016-08-18T02:01:20Z"
}
],
"rootPath": "github.com/percona/toolkit-go/pt-mongodb-summary"
}