diff --git a/docs/pt-galera-log-explainer.rst b/docs/pt-galera-log-explainer.rst index 52bc8161..3b3cda00 100644 --- a/docs/pt-galera-log-explainer.rst +++ b/docs/pt-galera-log-explainer.rst @@ -48,36 +48,20 @@ You can filter by type of events pt-galera-log-explainer list --sst --views *.log -.. - whois - ~~~~~ - Find out information about nodes, using any type of info - - .. code-block:: bash - - pt-galera-log-explainer whois '218469b2' mysql.log - { - "input": "218469b2", - "IPs": [ - "172.17.0.3" - ], - "nodeNames": [ - "galera-node2" - ], - "hostname": "", - "nodeUUIDs:": [ - "218469b2", - "259b78a0", - "fa81213d", - ] - } - - Using any type of information - - .. code-block:: bash - - pt-galera-log-explainer whois '172.17.0.3' mysql.log - pt-galera-log-explainer whois 'galera-node2' mysql.log +whois +~~~~~ +Find out information about nodes, using any type of information + +.. code-block:: bash + + pt-galera-log-explainer [flags] whois [--json] [--type { nodename | ip | uuid | auto }] + + +.. code-block:: bash + + pt-galera-log-explainer whois '218469b2' mysql.log + pt-galera-log-explainer whois '172.17.0.3' mysql.log + pt-galera-log-explainer whois 'galera-node2' mysql.log conflicts @@ -219,6 +203,24 @@ Example outputs 2023-03-12T19:44:59.855443Z | node1 left | 2023-03-12T19:44:59.855491Z | PRIMARY(n=2) | + $ pt-galera-log-explainer whois 172.17.0.2 --no-color tests/logs/upgrade/* + ip: + └── 172.17.0.2 + ├── nodename: + │ └── node1 (2023-03-12 19:35:07.644683 +0000 UTC) + │ + └── uuid: + ├── 1d3ea8f5 (2023-03-12 07:24:13.789261 +0000 UTC) + ├── 54ab931e (2023-03-12 07:43:08.563339 +0000 UTC) + ├── fecde235 (2023-03-12 08:46:48.963504 +0000 UTC) + ├── a07872e1 (2023-03-12 08:49:41.206124 +0000 UTC) + ├── 60da0bf9-aa9c (2023-03-12 12:29:48.873397 +0000 UTC) + ├── 35b62086-902c (2023-03-12 13:04:23.979636 +0000 UTC) + ├── ca2c2a5f-a82a (2023-03-12 19:35:05.878879 +0000 UTC) + └── eefb9c8a-b69a (2023-03-12 19:43:17.133756 +0000 UTC) + + + Requirements ============ diff --git a/go.mod b/go.mod index 0e59a840..164990b4 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 + github.com/xlab/treeprint v1.2.0 go.mongodb.org/mongo-driver v1.14.0 golang.org/x/crypto v0.21.0 golang.org/x/exp v0.0.0-20230321023759-10a507213a29 diff --git a/go.sum b/go.sum index f79d2a1d..88136a8b 100644 --- a/go.sum +++ b/go.sum @@ -108,6 +108,8 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/src/go/pt-galera-log-explainer/README.rst b/src/go/pt-galera-log-explainer/README.rst index 52bc8161..3b3cda00 100644 --- a/src/go/pt-galera-log-explainer/README.rst +++ b/src/go/pt-galera-log-explainer/README.rst @@ -48,36 +48,20 @@ You can filter by type of events pt-galera-log-explainer list --sst --views *.log -.. - whois - ~~~~~ - Find out information about nodes, using any type of info - - .. code-block:: bash - - pt-galera-log-explainer whois '218469b2' mysql.log - { - "input": "218469b2", - "IPs": [ - "172.17.0.3" - ], - "nodeNames": [ - "galera-node2" - ], - "hostname": "", - "nodeUUIDs:": [ - "218469b2", - "259b78a0", - "fa81213d", - ] - } - - Using any type of information - - .. code-block:: bash - - pt-galera-log-explainer whois '172.17.0.3' mysql.log - pt-galera-log-explainer whois 'galera-node2' mysql.log +whois +~~~~~ +Find out information about nodes, using any type of information + +.. code-block:: bash + + pt-galera-log-explainer [flags] whois [--json] [--type { nodename | ip | uuid | auto }] + + +.. code-block:: bash + + pt-galera-log-explainer whois '218469b2' mysql.log + pt-galera-log-explainer whois '172.17.0.3' mysql.log + pt-galera-log-explainer whois 'galera-node2' mysql.log conflicts @@ -219,6 +203,24 @@ Example outputs 2023-03-12T19:44:59.855443Z | node1 left | 2023-03-12T19:44:59.855491Z | PRIMARY(n=2) | + $ pt-galera-log-explainer whois 172.17.0.2 --no-color tests/logs/upgrade/* + ip: + └── 172.17.0.2 + ├── nodename: + │ └── node1 (2023-03-12 19:35:07.644683 +0000 UTC) + │ + └── uuid: + ├── 1d3ea8f5 (2023-03-12 07:24:13.789261 +0000 UTC) + ├── 54ab931e (2023-03-12 07:43:08.563339 +0000 UTC) + ├── fecde235 (2023-03-12 08:46:48.963504 +0000 UTC) + ├── a07872e1 (2023-03-12 08:49:41.206124 +0000 UTC) + ├── 60da0bf9-aa9c (2023-03-12 12:29:48.873397 +0000 UTC) + ├── 35b62086-902c (2023-03-12 13:04:23.979636 +0000 UTC) + ├── ca2c2a5f-a82a (2023-03-12 19:35:05.878879 +0000 UTC) + └── eefb9c8a-b69a (2023-03-12 19:43:17.133756 +0000 UTC) + + + Requirements ============ diff --git a/src/go/pt-galera-log-explainer/main_test.go b/src/go/pt-galera-log-explainer/main_test.go index cf9df4dd..66146836 100644 --- a/src/go/pt-galera-log-explainer/main_test.go +++ b/src/go/pt-galera-log-explainer/main_test.go @@ -139,24 +139,29 @@ func TestMain(t *testing.T) { }, { name: "operator_ambiguous_ips_whois_cluster1-1", - cmd: []string{"whois", "cluster1-1", "--pxc-operator"}, + cmd: []string{"whois", "cluster1-1", "--pxc-operator", "--json"}, path: "tests/logs/operator_ambiguous_ips/*", }, { name: "operator_ambiguous_ips_whois_e2239bca-93a3", - cmd: []string{"whois", "e2239bca-93a3", "--pxc-operator"}, + cmd: []string{"whois", "e2239bca-93a3", "--pxc-operator", "--json"}, path: "tests/logs/operator_ambiguous_ips/*", }, { // symlink to the output of the test above, should be identical name: "operator_ambiguous_ips_whois_e2239bca-256c-11ee-93a3-e23704b1e880", - cmd: []string{"whois", "e2239bca-256c-11ee-93a3-e23704b1e880", "--pxc-operator"}, + cmd: []string{"whois", "e2239bca-256c-11ee-93a3-e23704b1e880", "--pxc-operator", "--json"}, + path: "tests/logs/operator_ambiguous_ips/*", + }, + { + name: "operator_ambiguous_ips_whois_tree_no_color_e2239bca-93a3", + cmd: []string{"whois", "e2239bca-93a3", "--pxc-operator", "--no-color"}, path: "tests/logs/operator_ambiguous_ips/*", }, { name: "operator_ambiguous_ips_whois_10.16.27.98", - cmd: []string{"whois", "10.16.27.98", "--pxc-operator"}, + cmd: []string{"whois", "10.16.27.98", "--pxc-operator", "--json"}, path: "tests/logs/operator_ambiguous_ips/*", }, } diff --git a/src/go/pt-galera-log-explainer/tests/expected/operator_ambiguous_ips_whois_tree_no_color_e2239bca-93a3 b/src/go/pt-galera-log-explainer/tests/expected/operator_ambiguous_ips_whois_tree_no_color_e2239bca-93a3 new file mode 100644 index 00000000..a6bbffb4 --- /dev/null +++ b/src/go/pt-galera-log-explainer/tests/expected/operator_ambiguous_ips_whois_tree_no_color_e2239bca-93a3 @@ -0,0 +1,46 @@ +uuid: +└── e2239bca-93a3 + ├── nodename: + │ └── cluster1-1 (2023-05-18 13:13:27.582217 +0000 UTC) + │ ├── ip: + │ │ ├── 10.16.27.195 (2023-05-10 09:06:21.290854 +0000 UTC) + │ │ ├── 10.16.27.93 (2023-05-10 10:49:15.965568 +0000 UTC) + │ │ ├── 10.16.27.67 (2023-05-10 11:43:19.838842 +0000 UTC) + │ │ ├── 10.16.27.149 (2023-05-21 00:55:34.59855 +0000 UTC) + │ │ └── 10.16.27.203 (2023-05-21 01:21:12.237121 +0000 UTC) + │ │ + │ └── uuid: + │ ├── 09afeef6-a69d (2023-05-10 09:06:21.310966 +0000 UTC) + │ ├── 106cd5a8-8e1c (2023-05-10 09:42:20.096709 +0000 UTC) + │ ├── 6a146d09-8747 (2023-05-10 10:49:15.98352 +0000 UTC) + │ ├── f7946b60-bf31 (2023-05-10 11:43:19.859843 +0000 UTC) + │ ├── 2cc76c37-becc (2023-05-10 11:51:58.610014 +0000 UTC) + │ ├── 2cc76c37-becd (2023-05-12 19:13:56.828375 +0000 UTC) + │ ├── 2cc76c37-bece (2023-05-12 19:29:34.102395 +0000 UTC) + │ ├── 2cc76c37-becf (2023-05-16 02:56:58.102204 +0000 UTC) + │ ├── 96435e8a-bab8 (2023-05-16 02:58:05.880842 +0000 UTC) + │ ├── d0e11ff4-be29 (2023-05-16 02:59:44.222891 +0000 UTC) + │ ├── 215101e1-b61d (2023-05-16 03:01:59.175607 +0000 UTC) + │ ├── 87e7065b-bf25 (2023-05-16 03:04:51.285176 +0000 UTC) + │ ├── 87e7065b-bf26 (2023-05-16 07:52:26.432272 +0000 UTC) + │ ├── 3c016ef3-af4c (2023-05-18 08:51:06.97503 +0000 UTC) + │ ├── 5fd057e4-bab5 (2023-05-18 11:15:16.987418 +0000 UTC) + │ ├── 250ac3d5-8380 (2023-05-18 13:15:19.825195 +0000 UTC) + │ ├── 70a8263e-989f (2023-05-18 13:17:26.686853 +0000 UTC) + │ ├── 7a3b782e-96c0 (2023-05-18 14:00:39.734694 +0000 UTC) + │ ├── 4ca2c784-a878 (2023-05-21 00:55:34.619148 +0000 UTC) + │ ├── e123e2f3-ace4 (2023-05-21 01:21:12.258721 +0000 UTC) + │ ├── e123e2f3-ace5 (2023-05-24 09:08:00.784586 +0000 UTC) + │ ├── c943db75-9035 (2023-05-25 04:36:13.482766 +0000 UTC) + │ ├── c943db75-9036 (2023-05-28 08:23:24.701198 +0000 UTC) + │ ├── 8e6f32b6-bf89 (2023-05-28 08:55:54.185342 +0000 UTC) + │ │ └── nodename: + │ │ └── unspecified (2023-05-29 07:16:49.686673 +0000 UTC) + │ │ + │ └── 66e2b7bf-8000 (2023-05-29 07:20:31.719983 +0000 UTC) + │ + │ + └── ip: + └── 10.16.27.98 (2023-05-18 13:13:27.582217 +0000 UTC) + + diff --git a/src/go/pt-galera-log-explainer/translate/whois.go b/src/go/pt-galera-log-explainer/translate/whois.go index c597643a..2167ef02 100644 --- a/src/go/pt-galera-log-explainer/translate/whois.go +++ b/src/go/pt-galera-log-explainer/translate/whois.go @@ -3,6 +3,10 @@ package translate import ( "encoding/json" "time" + + "github.com/percona/percona-toolkit/src/go/pt-galera-log-explainer/utils" + "github.com/xlab/treeprint" + "golang.org/x/exp/slices" ) type WhoisNode struct { @@ -60,6 +64,57 @@ func (n *WhoisNode) MarshalJSON() ([]byte, error) { return json.Marshal(n.Values) } +func (n *WhoisNode) String() string { + return n.tree().String() +} + +func (n *WhoisNode) tree() treeprint.Tree { + root := treeprint.NewWithRoot(utils.Paint(utils.GreenText, n.nodetype) + ":") + for _, value := range n.valuesSortedByTimestamps() { + valueData := n.Values[value] + str := value + if valueData.Timestamp != nil { + str += utils.Paint(utils.BlueText, " ("+valueData.Timestamp.String()+")") + } + if len(valueData.SubNodes) == 0 { + root.AddNode(str) + continue + } + subtree := root.AddBranch(str) + + // forcing map iteration for repeatable outputs + for _, subNodeType := range forcedIterationOrder { + subnode, ok := valueData.SubNodes[subNodeType] + if ok { + subtree.AddNode(subnode.tree()) + } + } + } + return root +} + +func (n *WhoisNode) valuesSortedByTimestamps() []string { + values := []string{} + for value := range n.Values { + values = append(values, value) + } + + // keep nil timestamps at the top + slices.SortFunc(values, func(a, b string) bool { + if n.Values[a].Timestamp == nil && n.Values[b].Timestamp == nil { + return a < b + } + if n.Values[a].Timestamp == nil { // implied b!=nil + return true // meaning, nil < nonnil, a < b + } + if n.Values[b].Timestamp == nil { // implied a!=nil + return false // meaning a is greater than b + } + return n.Values[a].Timestamp.Before(*n.Values[b].Timestamp) + }) + return values +} + func (n *WhoisNode) addKey(value string, timestamp time.Time) bool { storedValue := n.rootNode.GetValueData(value, n.nodetype) if storedValue != nil { diff --git a/src/go/pt-galera-log-explainer/whois.go b/src/go/pt-galera-log-explainer/whois.go index 783bee3b..7f346786 100644 --- a/src/go/pt-galera-log-explainer/whois.go +++ b/src/go/pt-galera-log-explainer/whois.go @@ -16,6 +16,7 @@ type whois struct { Search string `arg:"" name:"search" help:"the identifier (node name, ip, uuid) to search"` SearchType string `name:"type" help:"what kind of information is the input (node name, ip, uuid). Auto-detected when possible." enum:"nodename,ip,uuid,auto" default:"auto"` Paths []string `arg:"" name:"paths" help:"paths of the log to use"` + Json bool } func (w *whois) Help() string { @@ -68,12 +69,17 @@ func (w *whois) Run() error { } log.Debug().Str("searchType", w.SearchType).Msg("whois searchType") + out := translate.Whois(w.Search, w.SearchType) - json, err := json.MarshalIndent(out, "", "\t") - if err != nil { - return err + if w.Json { + json, err := json.MarshalIndent(out, "", "\t") + if err != nil { + return err + } + fmt.Println(string(json)) + } else { + fmt.Println(out) } - fmt.Println(string(json)) return nil }