You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
5.3 KiB

6 years ago
6 years ago
6 years ago
6 years ago
  1. package command
  2. import (
  3. "context"
  4. "github.com/chrislusf/seaweedfs/weed/filer2"
  5. "github.com/chrislusf/seaweedfs/weed/glog"
  6. "github.com/chrislusf/seaweedfs/weed/notification"
  7. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  8. "github.com/chrislusf/seaweedfs/weed/server"
  9. "github.com/spf13/viper"
  10. )
  11. func init() {
  12. cmdFilerExport.Run = runFilerExport // break init cycle
  13. }
  14. var cmdFilerExport = &Command{
  15. UsageLine: "filer.export -sourceStore=mysql -targetStore=cassandra",
  16. Short: "export meta data in filer store",
  17. Long: `Iterate the file tree and export all metadata out
  18. Both source and target store:
  19. * should be a store name already specified in filer.toml
  20. * do not need to be enabled state
  21. If target store is empty, only the directory tree will be listed.
  22. If target store is "notification", the list of entries will be sent to notification.
  23. This is usually used to bootstrap filer replication to a remote system.
  24. `,
  25. }
  26. var (
  27. // filerExportOutputFile = cmdFilerExport.Flag.String("output", "", "the output file. If empty, only list out the directory tree")
  28. filerExportSourceStore = cmdFilerExport.Flag.String("sourceStore", "", "the source store name in filer.toml, default to currently enabled store")
  29. filerExportTargetStore = cmdFilerExport.Flag.String("targetStore", "", "the target store name in filer.toml, or \"notification\" to export all files to message queue")
  30. dir = cmdFilerExport.Flag.String("dir", "/", "only process files under this directory")
  31. dirListLimit = cmdFilerExport.Flag.Int("dirListLimit", 100000, "limit directory list size")
  32. dryRun = cmdFilerExport.Flag.Bool("dryRun", false, "not actually moving data")
  33. verboseFilerExport = cmdFilerExport.Flag.Bool("v", false, "verbose entry details")
  34. )
  35. type statistics struct {
  36. directoryCount int
  37. fileCount int
  38. }
  39. func runFilerExport(cmd *Command, args []string) bool {
  40. weed_server.LoadConfiguration("filer", true)
  41. config := viper.GetViper()
  42. var sourceStore, targetStore filer2.FilerStore
  43. for _, store := range filer2.Stores {
  44. if store.GetName() == *filerExportSourceStore || *filerExportSourceStore == "" && config.GetBool(store.GetName()+".enabled") {
  45. viperSub := config.Sub(store.GetName())
  46. if err := store.Initialize(viperSub); err != nil {
  47. glog.Fatalf("Failed to initialize source store for %s: %+v",
  48. store.GetName(), err)
  49. } else {
  50. sourceStore = store
  51. }
  52. break
  53. }
  54. }
  55. for _, store := range filer2.Stores {
  56. if store.GetName() == *filerExportTargetStore {
  57. viperSub := config.Sub(store.GetName())
  58. if err := store.Initialize(viperSub); err != nil {
  59. glog.Fatalf("Failed to initialize target store for %s: %+v",
  60. store.GetName(), err)
  61. } else {
  62. targetStore = store
  63. }
  64. break
  65. }
  66. }
  67. if sourceStore == nil {
  68. glog.Errorf("Failed to find source store %s", *filerExportSourceStore)
  69. println("existing data sources are:")
  70. for _, store := range filer2.Stores {
  71. println(" " + store.GetName())
  72. }
  73. return false
  74. }
  75. if targetStore == nil && *filerExportTargetStore != "" && *filerExportTargetStore != "notification" {
  76. glog.Errorf("Failed to find target store %s", *filerExportTargetStore)
  77. println("existing data sources are:")
  78. for _, store := range filer2.Stores {
  79. println(" " + store.GetName())
  80. }
  81. return false
  82. }
  83. ctx := context.Background()
  84. stat := statistics{}
  85. var fn func(level int, entry *filer2.Entry) error
  86. if *filerExportTargetStore == "notification" {
  87. weed_server.LoadConfiguration("notification", false)
  88. v := viper.GetViper()
  89. notification.LoadConfiguration(v.Sub("notification"))
  90. fn = func(level int, entry *filer2.Entry) error {
  91. printout(level, entry)
  92. if *dryRun {
  93. return nil
  94. }
  95. return notification.Queue.SendMessage(
  96. string(entry.FullPath),
  97. &filer_pb.EventNotification{
  98. NewEntry: entry.ToProtoEntry(),
  99. },
  100. )
  101. }
  102. } else if targetStore == nil {
  103. fn = printout
  104. } else {
  105. fn = func(level int, entry *filer2.Entry) error {
  106. printout(level, entry)
  107. if *dryRun {
  108. return nil
  109. }
  110. return targetStore.InsertEntry(ctx, entry)
  111. }
  112. }
  113. doTraverse(ctx, &stat, sourceStore, filer2.FullPath(*dir), 0, fn)
  114. glog.Infof("processed %d directories, %d files", stat.directoryCount, stat.fileCount)
  115. return true
  116. }
  117. func doTraverse(ctx context.Context, stat *statistics, filerStore filer2.FilerStore, parentPath filer2.FullPath, level int, fn func(level int, entry *filer2.Entry) error) {
  118. limit := *dirListLimit
  119. lastEntryName := ""
  120. for {
  121. entries, err := filerStore.ListDirectoryEntries(ctx, parentPath, lastEntryName, false, limit)
  122. if err != nil {
  123. break
  124. }
  125. for _, entry := range entries {
  126. if fnErr := fn(level, entry); fnErr != nil {
  127. glog.Errorf("failed to process entry: %s", entry.FullPath)
  128. }
  129. if entry.IsDirectory() {
  130. stat.directoryCount++
  131. doTraverse(ctx, stat, filerStore, entry.FullPath, level+1, fn)
  132. } else {
  133. stat.fileCount++
  134. }
  135. }
  136. if len(entries) < limit {
  137. break
  138. }
  139. }
  140. }
  141. func printout(level int, entry *filer2.Entry) error {
  142. for i := 0; i < level; i++ {
  143. if i == level-1 {
  144. print("+-")
  145. } else {
  146. print("| ")
  147. }
  148. }
  149. print(entry.FullPath.Name())
  150. if *verboseFilerExport {
  151. for _, chunk := range entry.Chunks {
  152. print("[")
  153. print(chunk.FileId)
  154. print(",")
  155. print(chunk.Offset)
  156. print(",")
  157. print(chunk.Size)
  158. print(")")
  159. }
  160. }
  161. println()
  162. return nil
  163. }