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 }