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.

162 lines
4.6 KiB

3 years ago
  1. package shell
  2. import (
  3. "flag"
  4. "fmt"
  5. "github.com/chrislusf/seaweedfs/weed/filer"
  6. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  7. "github.com/chrislusf/seaweedfs/weed/util"
  8. "io"
  9. "strings"
  10. )
  11. func init() {
  12. Commands = append(Commands, &commandRemoteCache{})
  13. }
  14. type commandRemoteCache struct {
  15. }
  16. func (c *commandRemoteCache) Name() string {
  17. return "remote.cache"
  18. }
  19. func (c *commandRemoteCache) Help() string {
  20. return `cache the file content for mounted directories or files
  21. # assume a remote storage is configured to name "cloud1"
  22. remote.configure -name=cloud1 -type=s3 -access_key=xxx -secret_key=yyy
  23. # mount and pull one bucket
  24. remote.mount -dir=xxx -remote=cloud1/bucket
  25. # after mount, run one of these command to cache the content of the files
  26. remote.cache -dir=xxx
  27. remote.cache -dir=xxx/some/sub/dir
  28. remote.cache -dir=xxx/some/sub/dir -include=*.pdf
  29. This is designed to run regularly. So you can add it to some cronjob.
  30. If a file is already synchronized with the remote copy, the file will be skipped to avoid unnecessary copy.
  31. The actual data copying goes through volume severs.
  32. `
  33. }
  34. func (c *commandRemoteCache) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
  35. remoteMountCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
  36. dir := remoteMountCommand.String("dir", "", "a directory in filer")
  37. fileFiler := newFileFilter(remoteMountCommand)
  38. if err = remoteMountCommand.Parse(args); err != nil {
  39. return nil
  40. }
  41. mappings, listErr := filer.ReadMountMappings(commandEnv.option.GrpcDialOption, commandEnv.option.FilerAddress)
  42. if listErr != nil {
  43. return listErr
  44. }
  45. if *dir == "" {
  46. jsonPrintln(writer, mappings)
  47. fmt.Fprintln(writer, "need to specify '-dir' option")
  48. return nil
  49. }
  50. var localMountedDir string
  51. var remoteStorageMountedLocation *filer_pb.RemoteStorageLocation
  52. for k, loc := range mappings.Mappings {
  53. if strings.HasPrefix(*dir, k) {
  54. localMountedDir, remoteStorageMountedLocation = k, loc
  55. }
  56. }
  57. if localMountedDir == "" {
  58. jsonPrintln(writer, mappings)
  59. fmt.Fprintf(writer, "%s is not mounted\n", *dir)
  60. return nil
  61. }
  62. // find remote storage configuration
  63. remoteStorageConf, err := filer.ReadRemoteStorageConf(commandEnv.option.GrpcDialOption, commandEnv.option.FilerAddress, remoteStorageMountedLocation.Name)
  64. if err != nil {
  65. return err
  66. }
  67. // pull content from remote
  68. if err = c.cacheContentData(commandEnv, writer, util.FullPath(localMountedDir), remoteStorageMountedLocation, util.FullPath(*dir), fileFiler, remoteStorageConf); err != nil {
  69. return fmt.Errorf("cache content data: %v", err)
  70. }
  71. return nil
  72. }
  73. func recursivelyTraverseDirectory(filerClient filer_pb.FilerClient, dirPath util.FullPath, visitEntry func(dir util.FullPath, entry *filer_pb.Entry) bool) (err error) {
  74. err = filer_pb.ReadDirAllEntries(filerClient, dirPath, "", func(entry *filer_pb.Entry, isLast bool) error {
  75. if entry.IsDirectory {
  76. if !visitEntry(dirPath, entry) {
  77. return nil
  78. }
  79. subDir := dirPath.Child(entry.Name)
  80. if err := recursivelyTraverseDirectory(filerClient, subDir, visitEntry); err != nil {
  81. return err
  82. }
  83. } else {
  84. if !visitEntry(dirPath, entry) {
  85. return nil
  86. }
  87. }
  88. return nil
  89. })
  90. return
  91. }
  92. func shouldCacheToLocal(entry *filer_pb.Entry) bool {
  93. if entry.IsDirectory {
  94. return false
  95. }
  96. if entry.RemoteEntry == nil {
  97. return false
  98. }
  99. if entry.RemoteEntry.LocalMtime == 0 && entry.RemoteEntry.RemoteSize > 0 {
  100. return true
  101. }
  102. return false
  103. }
  104. func mayHaveCachedToLocal(entry *filer_pb.Entry) bool {
  105. if entry.IsDirectory {
  106. return false
  107. }
  108. if entry.RemoteEntry == nil {
  109. return false // should not uncache an entry that is not in remote
  110. }
  111. if entry.RemoteEntry.LocalMtime > 0 && len(entry.Chunks) > 0 {
  112. return true
  113. }
  114. return false
  115. }
  116. func (c *commandRemoteCache) cacheContentData(commandEnv *CommandEnv, writer io.Writer, localMountedDir util.FullPath, remoteMountedLocation *filer_pb.RemoteStorageLocation, dirToCache util.FullPath, fileFilter *FileFilter, remoteConf *filer_pb.RemoteConf) error {
  117. return recursivelyTraverseDirectory(commandEnv, dirToCache, func(dir util.FullPath, entry *filer_pb.Entry) bool {
  118. if !shouldCacheToLocal(entry) {
  119. return true // true means recursive traversal should continue
  120. }
  121. if fileFilter.matches(entry) {
  122. return true
  123. }
  124. println(dir, entry.Name)
  125. remoteLocation := filer.MapFullPathToRemoteStorageLocation(localMountedDir, remoteMountedLocation, dir.Child(entry.Name))
  126. if err := filer.DownloadToLocal(commandEnv, remoteConf, remoteLocation, dir, entry); err != nil {
  127. fmt.Fprintf(writer, "DownloadToLocal %+v: %v\n", remoteLocation, err)
  128. return false
  129. }
  130. return true
  131. })
  132. }