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.

124 lines
4.7 KiB

  1. package shell
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "log"
  7. "time"
  8. "github.com/chrislusf/seaweedfs/weed/operation"
  9. "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb"
  10. "github.com/chrislusf/seaweedfs/weed/storage/needle"
  11. "google.golang.org/grpc"
  12. )
  13. func init() {
  14. commands = append(commands, &commandVolumeMove{})
  15. }
  16. type commandVolumeMove struct {
  17. grpcDialOption grpc.DialOption
  18. }
  19. func (c *commandVolumeMove) Name() string {
  20. return "volume.move"
  21. }
  22. func (c *commandVolumeMove) Help() string {
  23. return `move a live volume from one volume server to another volume server
  24. volume.move <source volume server host:port> <target volume server host:port> <volume id>
  25. This command move a live volume from one volume server to another volume server. Here are the steps:
  26. 1. This command asks the target volume server to copy the source volume from source volume server, remember the last entry's timestamp.
  27. 2. This command asks the target volume server to mount the new volume
  28. Now the master will mark this volume id as readonly.
  29. 3. This command asks the target volume server to tail the source volume for updates after the timestamp, for 1 minutes to drain the requests.
  30. 4. This command asks the source volume server to unmount the source volume
  31. Now the master will mark this volume id as writable.
  32. 5. This command asks the source volume server to delete the source volume
  33. `
  34. }
  35. func (c *commandVolumeMove) Do(args []string, commandEnv *commandEnv, writer io.Writer) (err error) {
  36. if len(args) != 3 {
  37. fmt.Fprintf(writer, "received args: %+v\n", args)
  38. return fmt.Errorf("need 3 args of <source volume server host:port> <target volume server host:port> <volume id>")
  39. }
  40. c.grpcDialOption = commandEnv.option.GrpcDialOption
  41. ctx := context.Background()
  42. sourceVolumeServer, targetVolumeServer, volumeIdString := args[0], args[1], args[2]
  43. volumeId, err := needle.NewVolumeId(volumeIdString)
  44. if err != nil {
  45. return fmt.Errorf("wrong volume id format %s: %v", volumeId, err)
  46. }
  47. if sourceVolumeServer == targetVolumeServer {
  48. return fmt.Errorf("source and target volume servers are the same!")
  49. }
  50. log.Printf("copying volume %d from %s to %s", volumeId, sourceVolumeServer, targetVolumeServer)
  51. lastAppendAtNs, err := c.copyVolume(ctx, volumeId, sourceVolumeServer, targetVolumeServer)
  52. if err != nil {
  53. return fmt.Errorf("copy volume %d from %s to %s: %v", volumeId, sourceVolumeServer, targetVolumeServer, err)
  54. }
  55. log.Printf("tailing volume %d from %s to %s", volumeId, sourceVolumeServer, targetVolumeServer)
  56. if err = c.tailVolume(ctx, volumeId, sourceVolumeServer, targetVolumeServer, lastAppendAtNs, 5*time.Second); err != nil {
  57. return fmt.Errorf("tail volume %d from %s to %s: %v", volumeId, sourceVolumeServer, targetVolumeServer, err)
  58. }
  59. log.Printf("deleting volume %d from %s", volumeId, sourceVolumeServer)
  60. if err = c.deleteVolume(ctx, volumeId, sourceVolumeServer); err != nil {
  61. return fmt.Errorf("delete volume %d from %s: %v", volumeId, sourceVolumeServer, err)
  62. }
  63. log.Printf("moved volume %d from %s to %s", volumeId, sourceVolumeServer, targetVolumeServer)
  64. return nil
  65. }
  66. func (c *commandVolumeMove) copyVolume(ctx context.Context, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer string) (lastAppendAtNs uint64, err error) {
  67. err = operation.WithVolumeServerClient(targetVolumeServer, c.grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error {
  68. resp, replicateErr := volumeServerClient.VolumeCopy(ctx, &volume_server_pb.VolumeCopyRequest{
  69. VolumeId: uint32(volumeId),
  70. SourceDataNode: sourceVolumeServer,
  71. })
  72. if replicateErr == nil {
  73. lastAppendAtNs = resp.LastAppendAtNs
  74. }
  75. return replicateErr
  76. })
  77. return
  78. }
  79. func (c *commandVolumeMove) tailVolume(ctx context.Context, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer string, lastAppendAtNs uint64, timeout time.Duration) (err error) {
  80. return operation.WithVolumeServerClient(targetVolumeServer, c.grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error {
  81. _, replicateErr := volumeServerClient.VolumeTailReceiver(ctx, &volume_server_pb.VolumeTailReceiverRequest{
  82. VolumeId: uint32(volumeId),
  83. SinceNs: lastAppendAtNs,
  84. DrainingSeconds: uint32(timeout.Seconds()),
  85. SourceVolumeServer: sourceVolumeServer,
  86. })
  87. return replicateErr
  88. })
  89. }
  90. func (c *commandVolumeMove) deleteVolume(ctx context.Context, volumeId needle.VolumeId, sourceVolumeServer string) (err error) {
  91. return operation.WithVolumeServerClient(sourceVolumeServer, c.grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error {
  92. _, unmountErr := volumeServerClient.VolumeDelete(ctx, &volume_server_pb.VolumeDeleteRequest{
  93. VolumeId: uint32(volumeId),
  94. })
  95. return unmountErr
  96. })
  97. }