mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-22 11:54:54 +00:00
moving mongodb code into toolkit repo
This commit is contained in:
12
src/go/pt-mongodb-summary/.gitignore
vendored
Normal file
12
src/go/pt-mongodb-summary/.gitignore
vendored
Normal 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
|
91
src/go/pt-mongodb-summary/README.md
Normal file
91
src/go/pt-mongodb-summary/README.md
Normal 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
|
||||
```
|
658
src/go/pt-mongodb-summary/main.go
Normal file
658
src/go/pt-mongodb-summary/main.go
Normal 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
|
||||
}
|
369
src/go/pt-mongodb-summary/main_test.go
Normal file
369
src/go/pt-mongodb-summary/main_test.go
Normal 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)
|
||||
}
|
||||
}
|
121
src/go/pt-mongodb-summary/oplog.go
Normal file
121
src/go/pt-mongodb-summary/oplog.go
Normal 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
|
||||
}
|
9
src/go/pt-mongodb-summary/templates/balancer.go
Normal file
9
src/go/pt-mongodb-summary/templates/balancer.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package templates
|
||||
|
||||
const BalancerStats = `
|
||||
# Balancer (per day)
|
||||
Success: {{.Success}}
|
||||
Failed: {{.Failed}}
|
||||
Splits: {{.Splits}}
|
||||
Drops: {{.Drops}}
|
||||
`
|
10
src/go/pt-mongodb-summary/templates/clusterwide.go
Normal file
10
src/go/pt-mongodb-summary/templates/clusterwide.go
Normal 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}}`
|
26
src/go/pt-mongodb-summary/templates/hostinfo.go
Normal file
26
src/go/pt-mongodb-summary/templates/hostinfo.go
Normal 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 -}}
|
||||
`
|
10
src/go/pt-mongodb-summary/templates/oplog.go
Normal file
10
src/go/pt-mongodb-summary/templates/oplog.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package templates
|
||||
|
||||
const Oplog = `
|
||||
# Oplog ########################################################################################
|
||||
Oplog Size {{.Size}} Mb
|
||||
Oplog Used {{.UsedMB}} Mb
|
||||
Oplog Length {{.Running}}
|
||||
Last Election {{.ElectionTime}}
|
||||
|
||||
`
|
13
src/go/pt-mongodb-summary/templates/replicaset.go
Normal file
13
src/go/pt-mongodb-summary/templates/replicaset.go
Normal 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}}
|
||||
`
|
13
src/go/pt-mongodb-summary/templates/runningops.go
Normal file
13
src/go/pt-mongodb-summary/templates/runningops.go
Normal 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}}
|
||||
`
|
10
src/go/pt-mongodb-summary/templates/security.go
Normal file
10
src/go/pt-mongodb-summary/templates/security.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package templates
|
||||
|
||||
const Security = `
|
||||
# Security #####################################################################################
|
||||
Users {{.Users}}
|
||||
Roles {{.Roles}}
|
||||
Auth {{.Auth}}
|
||||
SSL {{.SSL}}
|
||||
|
||||
`
|
10
src/go/pt-mongodb-summary/test/sample/buildinfo.json
Normal file
10
src/go/pt-mongodb-summary/test/sample/buildinfo.json
Normal 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
|
||||
}
|
||||
|
69
src/go/pt-mongodb-summary/test/sample/cmdopts.json
Normal file
69
src/go/pt-mongodb-summary/test/sample/cmdopts.json
Normal 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": ""
|
||||
}
|
||||
}
|
||||
}
|
219
src/go/pt-mongodb-summary/test/sample/currentop.json
Normal file
219
src/go/pt-mongodb-summary/test/sample/currentop.json
Normal 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
|
||||
}
|
@@ -0,0 +1 @@
|
||||
null
|
@@ -0,0 +1,14 @@
|
||||
[
|
||||
"actionlog",
|
||||
"changelog",
|
||||
"chunks",
|
||||
"databases",
|
||||
"lockpings",
|
||||
"locks",
|
||||
"mongos",
|
||||
"settings",
|
||||
"shards",
|
||||
"system.indexes",
|
||||
"tags",
|
||||
"version"
|
||||
]
|
@@ -0,0 +1,6 @@
|
||||
[
|
||||
"col1",
|
||||
"col2",
|
||||
"system.indexes",
|
||||
"system.profile"
|
||||
]
|
@@ -0,0 +1,5 @@
|
||||
[
|
||||
"system.indexes",
|
||||
"testcol02",
|
||||
"testcol2"
|
||||
]
|
@@ -0,0 +1 @@
|
||||
null
|
30
src/go/pt-mongodb-summary/test/sample/hostinfo.json
Normal file
30
src/go/pt-mongodb-summary/test/sample/hostinfo.json
Normal 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
|
||||
}
|
5
src/go/pt-mongodb-summary/test/sample/ismaster.json
Normal file
5
src/go/pt-mongodb-summary/test/sample/ismaster.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"SetName": null,
|
||||
"Hosts": null,
|
||||
"Msg": "isdbgrid"
|
||||
}
|
38
src/go/pt-mongodb-summary/test/sample/listdatabases.json
Normal file
38
src/go/pt-mongodb-summary/test/sample/listdatabases.json
Normal 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
|
||||
}
|
58
src/go/pt-mongodb-summary/test/sample/replsetgetstatus.json
Normal file
58
src/go/pt-mongodb-summary/test/sample/replsetgetstatus.json
Normal 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"
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Date": "",
|
||||
"MyState": 0,
|
||||
"Term": 0,
|
||||
"HeartbeatIntervalMillis": 0,
|
||||
"Members": null,
|
||||
"Ok": 0,
|
||||
"Set": ""
|
||||
}
|
@@ -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"
|
||||
}
|
@@ -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"
|
||||
}
|
58
src/go/pt-mongodb-summary/test/sample/serverstatus.json
Normal file
58
src/go/pt-mongodb-summary/test/sample/serverstatus.json
Normal 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
|
||||
}
|
14
src/go/pt-mongodb-summary/test/sample/shardsinfo.json
Normal file
14
src/go/pt-mongodb-summary/test/sample/shardsinfo.json
Normal 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
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "local.oplog.rs",
|
||||
"options": {
|
||||
"autoIndexId": false,
|
||||
"capped": true,
|
||||
"size": 1.7267414016e+10
|
||||
}
|
||||
}
|
69
src/go/pt-mongodb-summary/test/util.go
Normal file
69
src/go/pt-mongodb-summary/test/util.go
Normal 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
|
||||
}
|
139
src/go/pt-mongodb-summary/vendor/vendor.json
vendored
Normal file
139
src/go/pt-mongodb-summary/vendor/vendor.json
vendored
Normal 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"
|
||||
}
|
Reference in New Issue
Block a user