main.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright 2015 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "flag"
  17. "fmt"
  18. "log"
  19. "path/filepath"
  20. "time"
  21. "github.com/coreos/etcd/etcdserver/etcdserverpb"
  22. "github.com/coreos/etcd/pkg/pbutil"
  23. "github.com/coreos/etcd/pkg/types"
  24. "github.com/coreos/etcd/raft/raftpb"
  25. "github.com/coreos/etcd/raftsnap"
  26. "github.com/coreos/etcd/wal"
  27. "github.com/coreos/etcd/wal/walpb"
  28. "go.uber.org/zap"
  29. )
  30. func main() {
  31. snapfile := flag.String("start-snap", "", "The base name of snapshot file to start dumping")
  32. index := flag.Uint64("start-index", 0, "The index to start dumping")
  33. flag.Parse()
  34. if len(flag.Args()) != 1 {
  35. log.Fatalf("Must provide data-dir argument (got %+v)", flag.Args())
  36. }
  37. dataDir := flag.Args()[0]
  38. if *snapfile != "" && *index != 0 {
  39. log.Fatal("start-snap and start-index flags cannot be used together.")
  40. }
  41. var (
  42. walsnap walpb.Snapshot
  43. snapshot *raftpb.Snapshot
  44. err error
  45. )
  46. isIndex := *index != 0
  47. if isIndex {
  48. fmt.Printf("Start dumping log entries from index %d.\n", *index)
  49. walsnap.Index = *index
  50. } else {
  51. if *snapfile == "" {
  52. ss := raftsnap.New(zap.NewExample(), snapDir(dataDir))
  53. snapshot, err = ss.Load()
  54. } else {
  55. snapshot, err = raftsnap.Read(filepath.Join(snapDir(dataDir), *snapfile))
  56. }
  57. switch err {
  58. case nil:
  59. walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
  60. nodes := genIDSlice(snapshot.Metadata.ConfState.Nodes)
  61. fmt.Printf("Snapshot:\nterm=%d index=%d nodes=%s\n",
  62. walsnap.Term, walsnap.Index, nodes)
  63. case raftsnap.ErrNoSnapshot:
  64. fmt.Printf("Snapshot:\nempty\n")
  65. default:
  66. log.Fatalf("Failed loading snapshot: %v", err)
  67. }
  68. fmt.Println("Start dupmping log entries from snapshot.")
  69. }
  70. w, err := wal.OpenForRead(zap.NewExample(), walDir(dataDir), walsnap)
  71. if err != nil {
  72. log.Fatalf("Failed opening WAL: %v", err)
  73. }
  74. wmetadata, state, ents, err := w.ReadAll()
  75. w.Close()
  76. if err != nil && (!isIndex || err != wal.ErrSnapshotNotFound) {
  77. log.Fatalf("Failed reading WAL: %v", err)
  78. }
  79. id, cid := parseWALMetadata(wmetadata)
  80. vid := types.ID(state.Vote)
  81. fmt.Printf("WAL metadata:\nnodeID=%s clusterID=%s term=%d commitIndex=%d vote=%s\n",
  82. id, cid, state.Term, state.Commit, vid)
  83. fmt.Printf("WAL entries:\n")
  84. fmt.Printf("lastIndex=%d\n", ents[len(ents)-1].Index)
  85. fmt.Printf("%4s\t%10s\ttype\tdata\n", "term", "index")
  86. for _, e := range ents {
  87. msg := fmt.Sprintf("%4d\t%10d", e.Term, e.Index)
  88. switch e.Type {
  89. case raftpb.EntryNormal:
  90. msg = fmt.Sprintf("%s\tnorm", msg)
  91. var rr etcdserverpb.InternalRaftRequest
  92. if err := rr.Unmarshal(e.Data); err == nil {
  93. msg = fmt.Sprintf("%s\t%s", msg, rr.String())
  94. break
  95. }
  96. // TODO: remove sensitive information
  97. // (https://github.com/coreos/etcd/issues/7620)
  98. var r etcdserverpb.Request
  99. if err := r.Unmarshal(e.Data); err == nil {
  100. switch r.Method {
  101. case "":
  102. msg = fmt.Sprintf("%s\tnoop", msg)
  103. case "SYNC":
  104. msg = fmt.Sprintf("%s\tmethod=SYNC time=%q", msg, time.Unix(0, r.Time))
  105. case "QGET", "DELETE":
  106. msg = fmt.Sprintf("%s\tmethod=%s path=%s", msg, r.Method, excerpt(r.Path, 64, 64))
  107. default:
  108. msg = fmt.Sprintf("%s\tmethod=%s path=%s val=%s", msg, r.Method, excerpt(r.Path, 64, 64), excerpt(r.Val, 128, 0))
  109. }
  110. break
  111. }
  112. msg = fmt.Sprintf("%s\t???", msg)
  113. case raftpb.EntryConfChange:
  114. msg = fmt.Sprintf("%s\tconf", msg)
  115. var r raftpb.ConfChange
  116. if err := r.Unmarshal(e.Data); err != nil {
  117. msg = fmt.Sprintf("%s\t???", msg)
  118. } else {
  119. msg = fmt.Sprintf("%s\tmethod=%s id=%s", msg, r.Type, types.ID(r.NodeID))
  120. }
  121. }
  122. fmt.Println(msg)
  123. }
  124. }
  125. func walDir(dataDir string) string { return filepath.Join(dataDir, "member", "wal") }
  126. func snapDir(dataDir string) string { return filepath.Join(dataDir, "member", "snap") }
  127. func parseWALMetadata(b []byte) (id, cid types.ID) {
  128. var metadata etcdserverpb.Metadata
  129. pbutil.MustUnmarshal(&metadata, b)
  130. id = types.ID(metadata.NodeID)
  131. cid = types.ID(metadata.ClusterID)
  132. return id, cid
  133. }
  134. func genIDSlice(a []uint64) []types.ID {
  135. ids := make([]types.ID, len(a))
  136. for i, id := range a {
  137. ids[i] = types.ID(id)
  138. }
  139. return ids
  140. }
  141. // excerpt replaces middle part with ellipsis and returns a double-quoted
  142. // string safely escaped with Go syntax.
  143. func excerpt(str string, pre, suf int) string {
  144. if pre+suf > len(str) {
  145. return fmt.Sprintf("%q", str)
  146. }
  147. return fmt.Sprintf("%q...%q", str[:pre], str[len(str)-suf:])
  148. }