github.com/rancher/longhorn-engine@v0.6.2/replica/client/client.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/golang/protobuf/ptypes/empty" 10 "golang.org/x/net/context" 11 "google.golang.org/grpc" 12 13 replicarpc "github.com/longhorn/longhorn-engine/replica/rpc" 14 syncagentrpc "github.com/longhorn/longhorn-engine/sync/rpc" 15 "github.com/longhorn/longhorn-engine/types" 16 ) 17 18 const ( 19 GRPCServiceCommonTimeout = 1 * time.Minute 20 GRPCServiceLongTimeout = 24 * time.Hour 21 ) 22 23 type ReplicaClient struct { 24 host string 25 replicaServiceURL string 26 syncAgentServiceURL string 27 } 28 29 func NewReplicaClient(address string) (*ReplicaClient, error) { 30 if strings.HasPrefix(address, "tcp://") { 31 address = strings.TrimPrefix(address, "tcp://") 32 } 33 34 if strings.HasPrefix(address, "http://") { 35 address = strings.TrimPrefix(address, "http://") 36 } 37 38 if strings.HasSuffix(address, "/v1") { 39 address = strings.TrimSuffix(address, "/v1") 40 } 41 42 parts := strings.Split(address, ":") 43 if len(parts) < 2 { 44 return nil, fmt.Errorf("Invalid address %s, must have a port in it", address) 45 } 46 47 port, err := strconv.Atoi(parts[1]) 48 if err != nil { 49 return nil, err 50 } 51 52 syncAgentServiceURL := strings.Replace(address, fmt.Sprintf(":%d", port), fmt.Sprintf(":%d", port+2), -1) 53 54 return &ReplicaClient{ 55 host: parts[0], 56 replicaServiceURL: address, 57 syncAgentServiceURL: syncAgentServiceURL, 58 }, nil 59 } 60 61 func GetDiskInfo(info *replicarpc.DiskInfo) *types.DiskInfo { 62 diskInfo := &types.DiskInfo{ 63 Name: info.Name, 64 Parent: info.Parent, 65 Children: info.Children, 66 Removed: info.Removed, 67 UserCreated: info.UserCreated, 68 Created: info.Created, 69 Size: info.Size, 70 Labels: info.Labels, 71 } 72 73 if diskInfo.Labels == nil { 74 diskInfo.Labels = map[string]string{} 75 } 76 77 return diskInfo 78 } 79 80 func GetReplicaInfo(r *replicarpc.Replica) *types.ReplicaInfo { 81 replicaInfo := &types.ReplicaInfo{ 82 Dirty: r.Dirty, 83 Rebuilding: r.Rebuilding, 84 Head: r.Head, 85 Parent: r.Parent, 86 Size: r.Size, 87 SectorSize: r.SectorSize, 88 BackingFile: r.BackingFile, 89 State: r.State, 90 Chain: r.Chain, 91 Disks: map[string]types.DiskInfo{}, 92 RemainSnapshots: int(r.RemainSnapshots), 93 RevisionCounter: r.RevisionCounter, 94 } 95 96 for diskName, diskInfo := range r.Disks { 97 replicaInfo.Disks[diskName] = *GetDiskInfo(diskInfo) 98 } 99 100 return replicaInfo 101 } 102 103 func (c *ReplicaClient) GetReplica() (*types.ReplicaInfo, error) { 104 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 105 if err != nil { 106 return nil, fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 107 } 108 defer conn.Close() 109 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 110 111 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 112 defer cancel() 113 114 replica, err := replicaServiceClient.ReplicaGet(ctx, &empty.Empty{}) 115 if err != nil { 116 return nil, fmt.Errorf("failed to get replica %v: %v", c.replicaServiceURL, err) 117 } 118 119 return GetReplicaInfo(replica), nil 120 } 121 122 func (c *ReplicaClient) OpenReplica() error { 123 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 124 if err != nil { 125 return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 126 } 127 defer conn.Close() 128 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 129 130 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 131 defer cancel() 132 133 if _, err := replicaServiceClient.ReplicaOpen(ctx, &empty.Empty{}); err != nil { 134 return fmt.Errorf("failed to open replica %v: %v", c.replicaServiceURL, err) 135 } 136 137 return nil 138 } 139 140 func (c *ReplicaClient) Close() error { 141 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 142 if err != nil { 143 return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 144 } 145 defer conn.Close() 146 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 147 148 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 149 defer cancel() 150 151 if _, err := replicaServiceClient.ReplicaClose(ctx, &empty.Empty{}); err != nil { 152 return fmt.Errorf("failed to close replica %v: %v", c.replicaServiceURL, err) 153 } 154 155 return nil 156 } 157 158 func (c *ReplicaClient) ReloadReplica() (*types.ReplicaInfo, error) { 159 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 160 if err != nil { 161 return nil, fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 162 } 163 defer conn.Close() 164 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 165 166 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 167 defer cancel() 168 169 replica, err := replicaServiceClient.ReplicaReload(ctx, &empty.Empty{}) 170 if err != nil { 171 return nil, fmt.Errorf("failed to reload replica %v: %v", c.replicaServiceURL, err) 172 } 173 174 return GetReplicaInfo(replica), nil 175 } 176 177 func (c *ReplicaClient) Revert(name, created string) error { 178 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 179 if err != nil { 180 return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 181 } 182 defer conn.Close() 183 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 184 185 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 186 defer cancel() 187 188 if _, err := replicaServiceClient.ReplicaRevert(ctx, &replicarpc.ReplicaRevertRequest{ 189 Name: name, 190 Created: created, 191 }); err != nil { 192 return fmt.Errorf("failed to revert replica %v: %v", c.replicaServiceURL, err) 193 } 194 195 return nil 196 } 197 198 func (c *ReplicaClient) RemoveDisk(disk string, force bool) error { 199 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 200 if err != nil { 201 return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 202 } 203 defer conn.Close() 204 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 205 206 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 207 defer cancel() 208 209 if _, err := replicaServiceClient.DiskRemove(ctx, &replicarpc.DiskRemoveRequest{ 210 Name: disk, 211 Force: force, 212 }); err != nil { 213 return fmt.Errorf("failed to remove disk %v for replica %v: %v", disk, c.replicaServiceURL, err) 214 } 215 216 return nil 217 } 218 219 func (c *ReplicaClient) ReplaceDisk(target, source string) error { 220 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 221 if err != nil { 222 return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 223 } 224 defer conn.Close() 225 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 226 227 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 228 defer cancel() 229 230 if _, err := replicaServiceClient.DiskReplace(ctx, &replicarpc.DiskReplaceRequest{ 231 Target: target, 232 Source: source, 233 }); err != nil { 234 return fmt.Errorf("failed to replace disk %v with %v for replica %v: %v", target, source, c.replicaServiceURL, err) 235 } 236 237 return nil 238 } 239 240 func (c *ReplicaClient) PrepareRemoveDisk(disk string) ([]*types.PrepareRemoveAction, error) { 241 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 242 if err != nil { 243 return nil, fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 244 } 245 defer conn.Close() 246 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 247 248 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 249 defer cancel() 250 251 reply, err := replicaServiceClient.DiskPrepareRemove(ctx, &replicarpc.DiskPrepareRemoveRequest{ 252 Name: disk, 253 }) 254 255 if err != nil { 256 return nil, fmt.Errorf("failed to prepare removing disk %v for replica %v: %v", disk, c.replicaServiceURL, err) 257 } 258 259 operations := []*types.PrepareRemoveAction{} 260 for _, op := range reply.Operations { 261 operations = append(operations, &types.PrepareRemoveAction{ 262 Action: op.Action, 263 Source: op.Source, 264 Target: op.Target, 265 }) 266 } 267 268 return operations, nil 269 } 270 271 func (c *ReplicaClient) MarkDiskAsRemoved(disk string) error { 272 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 273 if err != nil { 274 return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 275 } 276 defer conn.Close() 277 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 278 279 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 280 defer cancel() 281 282 if _, err := replicaServiceClient.DiskMarkAsRemoved(ctx, &replicarpc.DiskMarkAsRemovedRequest{ 283 Name: disk, 284 }); err != nil { 285 return fmt.Errorf("failed to mark disk %v as removed for replica %v: %v", disk, c.replicaServiceURL, err) 286 } 287 288 return nil 289 } 290 291 func (c *ReplicaClient) SetRebuilding(rebuilding bool) error { 292 conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure()) 293 if err != nil { 294 return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err) 295 } 296 defer conn.Close() 297 replicaServiceClient := replicarpc.NewReplicaServiceClient(conn) 298 299 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 300 defer cancel() 301 302 if _, err := replicaServiceClient.RebuildingSet(ctx, &replicarpc.RebuildingSetRequest{ 303 Rebuilding: rebuilding, 304 }); err != nil { 305 return fmt.Errorf("failed to set rebuilding to %v for replica %v: %v", rebuilding, c.replicaServiceURL, err) 306 } 307 308 return nil 309 } 310 311 func (c *ReplicaClient) RemoveFile(file string) error { 312 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 313 if err != nil { 314 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 315 } 316 defer conn.Close() 317 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 318 319 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 320 defer cancel() 321 322 if _, err := syncAgentServiceClient.FileRemove(ctx, &syncagentrpc.FileRemoveRequest{ 323 FileName: file, 324 }); err != nil { 325 return fmt.Errorf("failed to remove file %v: %v", file, err) 326 } 327 328 return nil 329 } 330 331 func (c *ReplicaClient) RenameFile(oldFileName, newFileName string) error { 332 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 333 if err != nil { 334 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 335 } 336 defer conn.Close() 337 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 338 339 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 340 defer cancel() 341 342 if _, err := syncAgentServiceClient.FileRename(ctx, &syncagentrpc.FileRenameRequest{ 343 OldFileName: oldFileName, 344 NewFileName: newFileName, 345 }); err != nil { 346 return fmt.Errorf("failed to rename or replace old file %v with new file %v: %v", oldFileName, newFileName, err) 347 } 348 349 return nil 350 } 351 352 func (c *ReplicaClient) SendFile(from, host string, port int32) error { 353 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 354 if err != nil { 355 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 356 } 357 defer conn.Close() 358 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 359 360 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceLongTimeout) 361 defer cancel() 362 363 if _, err := syncAgentServiceClient.FileSend(ctx, &syncagentrpc.FileSendRequest{ 364 FromFileName: from, 365 Host: host, 366 Port: port, 367 }); err != nil { 368 return fmt.Errorf("failed to send file %v to %v:%v: %v", from, host, port, err) 369 } 370 371 return nil 372 } 373 374 func (c *ReplicaClient) LaunchReceiver(toFilePath string) (string, int32, error) { 375 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 376 if err != nil { 377 return "", 0, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 378 } 379 defer conn.Close() 380 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 381 382 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 383 defer cancel() 384 385 reply, err := syncAgentServiceClient.ReceiverLaunch(ctx, &syncagentrpc.ReceiverLaunchRequest{ 386 ToFileName: toFilePath, 387 }) 388 if err != nil { 389 return "", 0, fmt.Errorf("failed to launch receiver for %v: %v", toFilePath, err) 390 } 391 392 return c.host, reply.Port, nil 393 } 394 395 func (c *ReplicaClient) CreateBackup(snapshot, dest, volume string, labels []string, credential map[string]string) (*syncagentrpc.BackupCreateReply, error) { 396 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 397 if err != nil { 398 return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 399 } 400 defer conn.Close() 401 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 402 403 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 404 defer cancel() 405 406 reply, err := syncAgentServiceClient.BackupCreate(ctx, &syncagentrpc.BackupCreateRequest{ 407 SnapshotFileName: snapshot, 408 BackupTarget: dest, 409 VolumeName: volume, 410 Labels: labels, 411 Credential: credential, 412 }) 413 if err != nil { 414 return nil, fmt.Errorf("failed to create backup to %v for volume %v: %v", dest, volume, err) 415 } 416 417 return reply, nil 418 } 419 420 func (c *ReplicaClient) GetBackupStatus(backupName string) (*syncagentrpc.BackupStatusReply, error) { 421 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 422 if err != nil { 423 return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 424 } 425 defer conn.Close() 426 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 427 428 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 429 defer cancel() 430 431 reply, err := syncAgentServiceClient.BackupGetStatus(ctx, &syncagentrpc.BackupProgressRequest{ 432 Backup: backupName, 433 }) 434 435 if err != nil { 436 return nil, err 437 } 438 439 return reply, nil 440 } 441 442 func (c *ReplicaClient) RmBackup(backup string) error { 443 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 444 if err != nil { 445 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 446 } 447 defer conn.Close() 448 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 449 450 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 451 defer cancel() 452 453 if _, err := syncAgentServiceClient.BackupRemove(ctx, &syncagentrpc.BackupRemoveRequest{ 454 Backup: backup, 455 }); err != nil { 456 return fmt.Errorf("failed to remove backup %v: %v", backup, err) 457 } 458 459 return nil 460 } 461 462 func (c *ReplicaClient) RestoreBackup(backup, snapshotFile string, credential map[string]string) error { 463 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 464 if err != nil { 465 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 466 } 467 defer conn.Close() 468 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 469 470 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 471 defer cancel() 472 473 if _, err := syncAgentServiceClient.BackupRestore(ctx, &syncagentrpc.BackupRestoreRequest{ 474 Backup: backup, 475 SnapshotFileName: snapshotFile, 476 Credential: credential, 477 }); err != nil { 478 return fmt.Errorf("failed to restore backup %v to snapshot %v: %v", backup, snapshotFile, err) 479 } 480 481 return nil 482 } 483 484 func (c *ReplicaClient) RestoreBackupIncrementally(backup, deltaFile, lastRestored, 485 snapshotDiskName string, credential map[string]string) error { 486 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 487 if err != nil { 488 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 489 } 490 defer conn.Close() 491 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 492 493 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 494 defer cancel() 495 496 if _, err := syncAgentServiceClient.BackupRestoreIncrementally(ctx, &syncagentrpc.BackupRestoreIncrementallyRequest{ 497 Backup: backup, 498 DeltaFileName: deltaFile, 499 LastRestoredBackupName: lastRestored, 500 Credential: credential, 501 SnapshotDiskName: snapshotDiskName, 502 }); err != nil { 503 return fmt.Errorf("failed to incrementally restore backup %v to file %v: %v", backup, deltaFile, err) 504 } 505 506 return nil 507 } 508 func (c *ReplicaClient) Reset() error { 509 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 510 if err != nil { 511 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 512 } 513 defer conn.Close() 514 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 515 516 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 517 defer cancel() 518 519 if _, err := syncAgentServiceClient.Reset(ctx, &empty.Empty{}); err != nil { 520 return fmt.Errorf("failed to cleanup restore info in Sync Agent Server: %v", err) 521 } 522 523 return nil 524 } 525 526 func (c *ReplicaClient) RestoreStatus() (*syncagentrpc.RestoreStatusReply, error) { 527 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 528 if err != nil { 529 return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 530 } 531 defer conn.Close() 532 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 533 534 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 535 defer cancel() 536 537 reply, err := syncAgentServiceClient.RestoreStatus(ctx, &empty.Empty{}) 538 if err != nil { 539 return nil, fmt.Errorf("failed to get restore status: %v", err) 540 } 541 542 return reply, nil 543 } 544 545 func (c *ReplicaClient) SnapshotPurge() error { 546 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 547 if err != nil { 548 return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 549 } 550 defer conn.Close() 551 552 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 553 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 554 defer cancel() 555 556 if _, err := syncAgentServiceClient.SnapshotPurge(ctx, &empty.Empty{}); err != nil { 557 return fmt.Errorf("failed to start snapshot purge: %v", err) 558 } 559 560 return nil 561 } 562 563 func (c *ReplicaClient) SnapshotPurgeStatus() (*syncagentrpc.SnapshotPurgeStatusReply, error) { 564 conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure()) 565 if err != nil { 566 return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err) 567 } 568 defer conn.Close() 569 570 syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn) 571 ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout) 572 defer cancel() 573 574 status, err := syncAgentServiceClient.SnapshotPurgeStatus(ctx, &empty.Empty{}) 575 if err != nil { 576 return nil, fmt.Errorf("failed to get snapshot purge status: %v", err) 577 } 578 579 return status, nil 580 }