github.com/yasker/longhorn-engine@v0.0.0-20160621014712-6ed6cfca0729/sync/sync.go (about)

     1  package sync
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  
     8  	"github.com/Sirupsen/logrus"
     9  	"github.com/rancher/longhorn/controller/client"
    10  	"github.com/rancher/longhorn/controller/rest"
    11  	"github.com/rancher/longhorn/replica"
    12  	replicaClient "github.com/rancher/longhorn/replica/client"
    13  )
    14  
    15  type Task struct {
    16  	client *client.ControllerClient
    17  }
    18  
    19  func NewTask(controller string) *Task {
    20  	return &Task{
    21  		client: client.NewControllerClient(controller),
    22  	}
    23  }
    24  
    25  func (t *Task) DeleteSnapshot(snapshot string) error {
    26  	replicas, err := t.client.ListReplicas()
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	for _, r := range replicas {
    32  		if ok, err := t.isRebuilding(&r); err != nil {
    33  			return err
    34  		} else if ok {
    35  			return fmt.Errorf("Can not remove a snapshot because %s is rebuilding", r.Address)
    36  		}
    37  	}
    38  
    39  	for _, replica := range replicas {
    40  		if err := t.removeSnapshot(&replica, snapshot); err != nil {
    41  			return err
    42  		}
    43  	}
    44  
    45  	return nil
    46  }
    47  
    48  func (t *Task) rmDisk(replicaInController *rest.Replica, disk string, markOnly bool) error {
    49  	repClient, err := replicaClient.NewReplicaClient(replicaInController.Address)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	return repClient.RemoveDisk(disk, markOnly)
    55  }
    56  
    57  func getNameAndIndex(chain []string, snapshot string) (string, int) {
    58  	index := find(chain, snapshot)
    59  	if index < 0 {
    60  		snapshot = fmt.Sprintf("volume-snap-%s.img", snapshot)
    61  		index = find(chain, snapshot)
    62  	}
    63  
    64  	if index < 0 {
    65  		return "", index
    66  	}
    67  
    68  	return snapshot, index
    69  }
    70  
    71  func (t *Task) isRebuilding(replicaInController *rest.Replica) (bool, error) {
    72  	repClient, err := replicaClient.NewReplicaClient(replicaInController.Address)
    73  	if err != nil {
    74  		return false, err
    75  	}
    76  
    77  	replica, err := repClient.GetReplica()
    78  	if err != nil {
    79  		return false, err
    80  	}
    81  
    82  	return replica.Rebuilding, nil
    83  }
    84  
    85  func (t *Task) removeSnapshot(replicaInController *rest.Replica, snapshot string) error {
    86  	if replicaInController.Mode != "RW" {
    87  		return fmt.Errorf("Can only removed snapshot from replica in mode RW, got %s", replicaInController.Mode)
    88  	}
    89  
    90  	repClient, err := replicaClient.NewReplicaClient(replicaInController.Address)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	output, err := repClient.PrepareRemoveDisk(snapshot)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	for _, op := range output.Operations {
   101  		switch op.Action {
   102  		case replica.OpRemove:
   103  			logrus.Infof("Removing %s on %s", op.Source, replicaInController.Address)
   104  			if err := t.rmDisk(replicaInController, op.Source, false); err != nil {
   105  				return err
   106  			}
   107  		case replica.OpMarkAsRemoved:
   108  			logrus.Infof("Marking %v as removed on %v", op.Source, replicaInController.Address)
   109  			if err := t.rmDisk(replicaInController, op.Source, true); err != nil {
   110  				return err
   111  			}
   112  		case replica.OpCoalesce:
   113  			logrus.Infof("Coalescing %v to %v on %v", op.Target, op.Source, replicaInController.Address)
   114  			if err = repClient.Coalesce(op.Target, op.Source); err != nil {
   115  				logrus.Errorf("Failed to coalesce %s on %s: %v", snapshot, replicaInController.Address, err)
   116  				return err
   117  			}
   118  			logrus.Infof("Hard-link %v to %v on %v", op.Source, op.Target, replicaInController.Address)
   119  			if err = repClient.HardLink(op.Source, op.Target); err != nil {
   120  				logrus.Errorf("Failed to hard-link %v to %v on %v", op.Source, op.Target, replicaInController.Address)
   121  				return err
   122  			}
   123  		}
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  func find(list []string, item string) int {
   130  	for i, val := range list {
   131  		if val == item {
   132  			return i
   133  		}
   134  	}
   135  	return -1
   136  }
   137  
   138  func (t *Task) AddReplica(replica string) error {
   139  	volume, err := t.client.GetVolume()
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	if volume.ReplicaCount == 0 {
   145  		return t.client.Start(replica)
   146  	}
   147  
   148  	if err := t.checkAndResetFailedRebuild(replica); err != nil {
   149  		return err
   150  	}
   151  
   152  	logrus.Infof("Adding replica %s in WO mode", replica)
   153  	_, err = t.client.CreateReplica(replica)
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	fromClient, toClient, err := t.getTransferClients(replica)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	if err := t.syncFiles(fromClient, toClient); err != nil {
   164  		return err
   165  	}
   166  
   167  	if err := t.reloadAndCheck(fromClient, toClient); err != nil {
   168  		return err
   169  	}
   170  
   171  	return t.setRw(replica)
   172  }
   173  
   174  func (t *Task) checkAndResetFailedRebuild(address string) error {
   175  	client, err := replicaClient.NewReplicaClient(address)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	replica, err := client.GetReplica()
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	if replica.State == "closed" && replica.Rebuilding {
   186  		if err := client.OpenReplica(); err != nil {
   187  			return err
   188  		}
   189  
   190  		if err := client.SetRebuilding(false); err != nil {
   191  			return err
   192  		}
   193  
   194  		return client.Close()
   195  	}
   196  
   197  	return nil
   198  }
   199  
   200  func (t *Task) setRw(replica string) error {
   201  	to, err := t.getToReplica(replica)
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	to.Mode = "RW"
   207  
   208  	to, err = t.client.UpdateReplica(to)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	if to.Mode != "RW" {
   214  		return fmt.Errorf("Failed to set replica to RW, in mode %s", to.Mode)
   215  	}
   216  
   217  	return nil
   218  }
   219  
   220  func (t *Task) reloadAndCheck(fromClient *replicaClient.ReplicaClient, toClient *replicaClient.ReplicaClient) error {
   221  	from, err := fromClient.GetReplica()
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	to, err := toClient.ReloadReplica()
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	fromChain := from.Chain[1:]
   232  	toChain := to.Chain[1:]
   233  
   234  	if !reflect.DeepEqual(fromChain, toChain) {
   235  		return fmt.Errorf("Chains are not equal: %v != %v", fromChain, toChain)
   236  	}
   237  
   238  	return toClient.SetRebuilding(false)
   239  }
   240  
   241  func (t *Task) syncFiles(fromClient *replicaClient.ReplicaClient, toClient *replicaClient.ReplicaClient) error {
   242  	from, err := fromClient.GetReplica()
   243  	if err != nil {
   244  		return err
   245  	}
   246  
   247  	if err := toClient.SetRebuilding(true); err != nil {
   248  		return err
   249  	}
   250  
   251  	to, err := toClient.GetReplica()
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	fromHead := ""
   257  	toHead := ""
   258  
   259  	for _, i := range from.Chain {
   260  		if strings.Contains(i, "volume-head") {
   261  			if fromHead != "" {
   262  				return fmt.Errorf("More than one head volume found in the from replica %s, %s", fromHead, i)
   263  			}
   264  			fromHead = i
   265  			continue
   266  		}
   267  
   268  		if err := t.syncFile(i, "", fromClient, toClient); err != nil {
   269  			return err
   270  		}
   271  
   272  		if err := t.syncFile(i+".meta", "", fromClient, toClient); err != nil {
   273  			return err
   274  		}
   275  	}
   276  
   277  	for _, i := range to.Chain {
   278  		if strings.Contains(i, "volume-head") {
   279  			if toHead != "" {
   280  				return fmt.Errorf("More than one head volume found in the to replica %s, %s", toHead, i)
   281  			}
   282  			toHead = i
   283  			continue
   284  		}
   285  	}
   286  
   287  	if fromHead == "" || toHead == "" {
   288  		return fmt.Errorf("Failed to find both source and destination head volumes, %s, %s", fromHead, toHead)
   289  	}
   290  
   291  	if err := t.syncFile(fromHead+".meta", toHead+".meta", fromClient, toClient); err != nil {
   292  		return err
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  func (t *Task) syncFile(from, to string, fromClient *replicaClient.ReplicaClient, toClient *replicaClient.ReplicaClient) error {
   299  	host, port, err := toClient.LaunchReceiver()
   300  	if err != nil {
   301  		return err
   302  	}
   303  
   304  	if to == "" {
   305  		to = from
   306  	}
   307  
   308  	logrus.Infof("Synchronizing %s to %s@%s:%d", from, to, host, port)
   309  	err = fromClient.SendFile(from, to, host, port)
   310  	if err != nil {
   311  		logrus.Infof("Failed synchronizing %s to %s@%s:%d: %v", from, to, host, port, err)
   312  	} else {
   313  		logrus.Infof("Done synchronizing %s to %s@%s:%d", from, to, host, port)
   314  	}
   315  
   316  	return err
   317  }
   318  
   319  func (t *Task) getTransferClients(address string) (*replicaClient.ReplicaClient, *replicaClient.ReplicaClient, error) {
   320  	from, err := t.getFromReplica()
   321  	if err != nil {
   322  		return nil, nil, err
   323  	}
   324  	logrus.Infof("Using replica %s as the source for rebuild ", from.Address)
   325  
   326  	fromClient, err := replicaClient.NewReplicaClient(from.Address)
   327  	if err != nil {
   328  		return nil, nil, err
   329  	}
   330  
   331  	to, err := t.getToReplica(address)
   332  	if err != nil {
   333  		return nil, nil, err
   334  	}
   335  	logrus.Infof("Using replica %s as the target for rebuild ", to.Address)
   336  
   337  	toClient, err := replicaClient.NewReplicaClient(to.Address)
   338  	if err != nil {
   339  		return nil, nil, err
   340  	}
   341  
   342  	return fromClient, toClient, nil
   343  }
   344  
   345  func (t *Task) getFromReplica() (rest.Replica, error) {
   346  	replicas, err := t.client.ListReplicas()
   347  	if err != nil {
   348  		return rest.Replica{}, err
   349  	}
   350  
   351  	for _, r := range replicas {
   352  		if r.Mode == "RW" {
   353  			return r, nil
   354  		}
   355  	}
   356  
   357  	return rest.Replica{}, fmt.Errorf("Failed to find good replica to copy from")
   358  }
   359  
   360  func (t *Task) getToReplica(address string) (rest.Replica, error) {
   361  	replicas, err := t.client.ListReplicas()
   362  	if err != nil {
   363  		return rest.Replica{}, err
   364  	}
   365  
   366  	for _, r := range replicas {
   367  		if r.Address == address {
   368  			if r.Mode != "WO" {
   369  				return rest.Replica{}, fmt.Errorf("Replica %s is not in mode WO got: %s", address, r.Mode)
   370  			}
   371  			return r, nil
   372  		}
   373  	}
   374  
   375  	return rest.Replica{}, fmt.Errorf("Failed to find target replica to copy to")
   376  }