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.

132 lines
3.3 KiB

  1. package command
  2. import (
  3. "github.com/chrislusf/seaweedfs/weed/server"
  4. "github.com/spf13/viper"
  5. "github.com/chrislusf/seaweedfs/weed/filer2"
  6. "github.com/chrislusf/seaweedfs/weed/glog"
  7. )
  8. func init() {
  9. cmdFilerExport.Run = runFilerExport // break init cycle
  10. }
  11. var cmdFilerExport = &Command{
  12. UsageLine: "filer.export -sourceStore=mysql -targetStroe=cassandra",
  13. Short: "export meta data in filer store",
  14. Long: `Iterate the file tree and export all metadata out
  15. Both source and target store:
  16. * should be a store name already specified in filer.toml
  17. * do not need to be enabled state
  18. If target store is empty, only the directory tree will be listed.
  19. `,
  20. }
  21. var (
  22. // filerExportOutputFile = cmdFilerExport.Flag.String("output", "", "the output file. If empty, only list out the directory tree")
  23. filerExportSourceStore = cmdFilerExport.Flag.String("sourceStore", "", "the source store name in filer.toml")
  24. filerExportTargetStore = cmdFilerExport.Flag.String("targetStore", "", "the target store name in filer.toml")
  25. )
  26. type statistics struct {
  27. directoryCount int
  28. fileCount int
  29. }
  30. func runFilerExport(cmd *Command, args []string) bool {
  31. weed_server.LoadConfiguration("filer", true)
  32. config := viper.GetViper()
  33. var sourceStore, targetStore filer2.FilerStore
  34. for _, store := range filer2.Stores {
  35. if store.GetName() == *filerExportSourceStore {
  36. viperSub := config.Sub(store.GetName())
  37. if err := store.Initialize(viperSub); err != nil {
  38. glog.Fatalf("Failed to initialize store for %s: %+v",
  39. store.GetName(), err)
  40. } else {
  41. sourceStore = store
  42. }
  43. break
  44. }
  45. }
  46. for _, store := range filer2.Stores {
  47. if store.GetName() == *filerExportTargetStore {
  48. viperSub := config.Sub(store.GetName())
  49. if err := store.Initialize(viperSub); err != nil {
  50. glog.Fatalf("Failed to initialize store for %s: %+v",
  51. store.GetName(), err)
  52. } else {
  53. targetStore = store
  54. }
  55. break
  56. }
  57. }
  58. if sourceStore == nil {
  59. glog.Errorf("Failed to find source store %s", *filerExportSourceStore)
  60. println("existing data sources are:")
  61. for _, store := range filer2.Stores {
  62. println(" " + store.GetName())
  63. }
  64. return false
  65. }
  66. stat := statistics{}
  67. var fn func(level int, entry *filer2.Entry) error
  68. if targetStore == nil {
  69. fn = printout
  70. } else {
  71. fn = func(level int, entry *filer2.Entry) error {
  72. return targetStore.InsertEntry(entry)
  73. }
  74. }
  75. doTraverse(&stat, sourceStore, filer2.FullPath("/"), 0, fn)
  76. glog.Infof("processed %d directories, %d files", stat.directoryCount, stat.fileCount)
  77. return true
  78. }
  79. func doTraverse(stat *statistics, filerStore filer2.FilerStore, parentPath filer2.FullPath, level int, fn func(level int, entry *filer2.Entry) error) {
  80. limit := 1000
  81. lastEntryName := ""
  82. for {
  83. entries, err := filerStore.ListDirectoryEntries(parentPath, lastEntryName, false, limit)
  84. if err != nil {
  85. break
  86. }
  87. for _, entry := range entries {
  88. if fnErr := fn(level, entry); fnErr != nil {
  89. glog.Errorf("failed to process entry: %s", entry.FullPath)
  90. }
  91. if entry.IsDirectory() {
  92. stat.directoryCount++
  93. doTraverse(stat, filerStore, entry.FullPath, level+1, fn)
  94. } else {
  95. stat.fileCount++
  96. }
  97. }
  98. if len(entries) < limit {
  99. break
  100. }
  101. }
  102. }
  103. func printout(level int, entry *filer2.Entry) error {
  104. for i := 0; i < level; i++ {
  105. print(" ")
  106. }
  107. println(entry.FullPath.Name())
  108. return nil
  109. }