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.

169 lines
4.4 KiB

3 years ago
  1. package shell
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. "github.com/chrislusf/seaweedfs/weed/filer"
  7. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  8. "github.com/chrislusf/seaweedfs/weed/util"
  9. "io"
  10. "path/filepath"
  11. "strings"
  12. )
  13. func init() {
  14. Commands = append(Commands, &commandRemoteUncache{})
  15. }
  16. type commandRemoteUncache struct {
  17. }
  18. func (c *commandRemoteUncache) Name() string {
  19. return "remote.uncache"
  20. }
  21. func (c *commandRemoteUncache) Help() string {
  22. return `keep the metadata but remote cache the file content for mounted directories or files
  23. This is designed to run regularly. So you can add it to some cronjob.
  24. If a file is not synchronized with the remote copy, the file will be skipped to avoid loss of data.
  25. remote.uncache -dir=/xxx
  26. remote.uncache -dir=/xxx/some/sub/dir
  27. remote.uncache -dir=/xxx/some/sub/dir -include=*.pdf
  28. remote.uncache -dir=/xxx/some/sub/dir -exclude=*.txt
  29. `
  30. }
  31. func (c *commandRemoteUncache) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
  32. remoteUnmountCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
  33. dir := remoteUnmountCommand.String("dir", "", "a directory in filer")
  34. fileFiler := newFileFilter(remoteUnmountCommand)
  35. if err = remoteUnmountCommand.Parse(args); err != nil {
  36. return nil
  37. }
  38. mappings, listErr := filer.ReadMountMappings(commandEnv.option.GrpcDialOption, commandEnv.option.FilerAddress)
  39. if listErr != nil {
  40. return listErr
  41. }
  42. if *dir == "" {
  43. jsonPrintln(writer, mappings)
  44. fmt.Fprintln(writer, "need to specify '-dir' option")
  45. return nil
  46. }
  47. var localMountedDir string
  48. for k := range mappings.Mappings {
  49. if strings.HasPrefix(*dir, k) {
  50. localMountedDir = k
  51. }
  52. }
  53. if localMountedDir == "" {
  54. jsonPrintln(writer, mappings)
  55. fmt.Fprintf(writer, "%s is not mounted\n", *dir)
  56. return nil
  57. }
  58. // pull content from remote
  59. if err = c.uncacheContentData(commandEnv, writer, util.FullPath(*dir), fileFiler); err != nil {
  60. return fmt.Errorf("cache content data: %v", err)
  61. }
  62. return nil
  63. }
  64. func (c *commandRemoteUncache) uncacheContentData(commandEnv *CommandEnv, writer io.Writer, dirToCache util.FullPath, fileFilter *FileFilter) error {
  65. return recursivelyTraverseDirectory(commandEnv, dirToCache, func(dir util.FullPath, entry *filer_pb.Entry) bool {
  66. if !mayHaveCachedToLocal(entry) {
  67. return true // true means recursive traversal should continue
  68. }
  69. if fileFilter.matches(entry) {
  70. return true
  71. }
  72. if entry.RemoteEntry.LastLocalSyncTsNs/1e9 < entry.Attributes.Mtime {
  73. return true // should not uncache an entry that is not synchronized with remote
  74. }
  75. entry.RemoteEntry.LastLocalSyncTsNs = 0
  76. entry.Chunks = nil
  77. println(dir, entry.Name)
  78. err := commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
  79. _, updateErr := client.UpdateEntry(context.Background(), &filer_pb.UpdateEntryRequest{
  80. Directory: string(dir),
  81. Entry: entry,
  82. })
  83. return updateErr
  84. })
  85. if err != nil {
  86. fmt.Fprintf(writer, "uncache %+v: %v\n", dir.Child(entry.Name), err)
  87. return false
  88. }
  89. return true
  90. })
  91. }
  92. type FileFilter struct {
  93. include *string
  94. exclude *string
  95. minSize *int64
  96. maxSize *int64
  97. minAge *int64
  98. maxAge *int64
  99. }
  100. func newFileFilter(remoteMountCommand *flag.FlagSet) (ff *FileFilter) {
  101. ff = &FileFilter{}
  102. ff.include = remoteMountCommand.String("include", "", "pattens of file names, e.g., *.pdf, *.html, ab?d.txt")
  103. ff.exclude = remoteMountCommand.String("exclude", "", "pattens of file names, e.g., *.pdf, *.html, ab?d.txt")
  104. ff.minSize = remoteMountCommand.Int64("minSize", -1, "minimum file size in bytes")
  105. ff.maxSize = remoteMountCommand.Int64("maxSize", -1, "maximum file size in bytes")
  106. ff.minAge = remoteMountCommand.Int64("minAge", -1, "minimum file age in seconds")
  107. ff.maxAge = remoteMountCommand.Int64("maxAge", -1, "maximum file age in seconds")
  108. return
  109. }
  110. func (ff *FileFilter) matches(entry *filer_pb.Entry) bool {
  111. if *ff.include != "" {
  112. if ok, _ := filepath.Match(*ff.include, entry.Name); !ok {
  113. return true
  114. }
  115. }
  116. if *ff.exclude != "" {
  117. if ok, _ := filepath.Match(*ff.exclude, entry.Name); ok {
  118. return true
  119. }
  120. }
  121. if *ff.minSize != -1 {
  122. if int64(entry.Attributes.FileSize) < *ff.minSize {
  123. return false
  124. }
  125. }
  126. if *ff.maxSize != -1 {
  127. if int64(entry.Attributes.FileSize) > *ff.maxSize {
  128. return false
  129. }
  130. }
  131. if *ff.minAge != -1 {
  132. if entry.Attributes.Crtime < *ff.minAge {
  133. return false
  134. }
  135. }
  136. if *ff.maxAge != -1 {
  137. if entry.Attributes.Crtime > *ff.maxAge {
  138. return false
  139. }
  140. }
  141. return false
  142. }