Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
var BackupCloneCmd = &cobra.Command{ Use: "clone <name|id>", Args: cobra.ExactArgs(1), Short: "Clone a backup", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { backup := args[0] toName := viper.GetString("to-backup-name") toContainerName := viper.GetString("to-container-name") threads := viper.GetUint("threads") if threads == 0 { return fmt.Errorf("an amount of threads cannot be zero") } if toContainerName == "" { return fmt.Errorf("swift container name connot be empty") } loc, err := getSrcAndDst("") if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcVolumeClient, err := newBlockStorageV3Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source volume client: %s", err) } srcObjectClient, err := newObjectStorageV1Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source object storage client: %s", err) } if v, err := backups_utils.IDFromName(cmd.Context(), srcVolumeClient, backup); err == nil { backup = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %s", err) } dstVolumeClient, err := newBlockStorageV3Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination volume client: %s", err) } dstObjectClient, err := newObjectStorageV1Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to destination source object storage client: %s", err) } srcBackup, err := waitForBackup(cmd.Context(), srcVolumeClient, backup, waitForBackupSec) if err != nil { return fmt.Errorf("failed to wait for a %q backup: %s", backup, err) } if srcBackup.IsIncremental { return fmt.Errorf("incremental backups are not supported") } defer measureTime() dstBackup, err := cloneBackup(cmd.Context(), srcVolumeClient, srcObjectClient, dstVolumeClient, dstObjectClient, srcBackup, toName, toContainerName, threads) if err != nil { return err } log.Printf("Migrated target backup name is %q (id: %q)", dstBackup.Name, dstBackup.ID) return nil }, }
BackupCloneCmd represents the backup clone command.
var BackupCmd = &cobra.Command{
Use: "backup",
}
BackupCmd represents the backup command.
var BackupRestoreCmd = &cobra.Command{ Use: "restore <backup_name|backup_id>", Args: cobra.ExactArgs(1), Short: "Restore a backup into a volume", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { backup := args[0] toVolumeName := viper.GetString("to-volume-name") size := viper.GetUint("volume-size") toAZ := viper.GetString("to-az") toVolumeType := viper.GetString("to-volume-type") loc, err := getSrcAndDst("") if err != nil { return err } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %s", err) } dstVolumeClient, err := newBlockStorageV3Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination volume client: %s", err) } err = checkAvailabilityZone(cmd.Context(), dstVolumeClient, "", &toAZ, &loc) if err != nil { return err } if v, err := backups_utils.IDFromName(cmd.Context(), dstVolumeClient, backup); err == nil { backup = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } backupObj, err := waitForBackup(cmd.Context(), dstVolumeClient, backup, waitForBackupSec) if err != nil { return fmt.Errorf("failed to wait for backup status: %s", err) } if backupObj.Size == 0 { return fmt.Errorf("target volume size must be specified") } if size > 0 { if int(size) < backupObj.Size { return fmt.Errorf("target volume size must not be less than %d", backupObj.Size) } backupObj.Size = int(size) } defer measureTime() dstVolume, err := backupToVolume(cmd.Context(), dstVolumeClient, backupObj, toVolumeName, toVolumeType, toAZ) if err != nil { return err } log.Printf("Target volume name is %q (id: %q)", dstVolume.Name, dstVolume.ID) return nil }, }
var BackupUploadCmd = &cobra.Command{ Use: "upload <filename|image_name|image_id>", Args: cobra.ExactArgs(1), Short: "Upload an image into a backup", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { image := args[0] toVolumeName := viper.GetString("to-volume-name") toBackupName := viper.GetString("to-backup-name") toContainerName := viper.GetString("to-container-name") size := viper.GetUint("volume-size") threads := viper.GetUint("threads") toAZ := viper.GetString("to-az") toVolumeType := viper.GetString("to-volume-type") restoreVolume := viper.GetBool("restore-volume") properties := viper.GetStringMapString("property") if threads == 0 { return fmt.Errorf("an amount of threads cannot be zero") } if toContainerName == "" { return fmt.Errorf("swift container name connot be empty") } loc, err := getSrcAndDst("") if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcObjectClient, err := newObjectStorageV1Client(srcProvider, loc.Src.Region) if err != nil { log.Printf("Failed to create source object storage client: %s", err) } srcImageClient, err := newGlanceV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source image client: %s", err) } if v, err := images_utils.IDFromName(cmd.Context(), srcImageClient, image); err == nil { image = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %s", err) } dstVolumeClient, err := newBlockStorageV3Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination volume client: %s", err) } dstObjectClient, err := newObjectStorageV1Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination object storage client, detailed image clone statistics will be unavailable: %s", err) } err = checkAvailabilityZone(cmd.Context(), dstVolumeClient, "", &toAZ, &loc) if err != nil { return err } defer measureTime() backup, err := uploadBackup(cmd.Context(), srcImageClient, srcObjectClient, dstObjectClient, dstVolumeClient, toBackupName, toContainerName, image, toAZ, properties, int(size), threads) if err != nil { return err } log.Printf("Target backup name is %q (id: %q)", backup.Name, backup.ID) if !restoreVolume { return nil } dstVolumeClient.TokenID = "" dstVolume, err := backupToVolume(cmd.Context(), dstVolumeClient, backup, toVolumeName, toVolumeType, toAZ) if err != nil { return err } log.Printf("Target volume name is %q (id: %q)", dstVolume.Name, dstVolume.ID) return nil }, }
var ImageCmd = &cobra.Command{ Use: "image <name|id>", Args: cobra.ExactArgs(1), Short: "Clone an image", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } imageWebDownload = viper.GetBool("image-web-download") return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { image := args[0] toName := viper.GetString("to-image-name") loc, err := getSrcAndDst("") if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcImageClient, err := newGlanceV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source image client: %s", err) } var srcObjectClient *gophercloud.ServiceClient if imageWebDownload { srcObjectClient, err = newObjectStorageV1Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source object storage client: %s", err) } } if v, err := images_utils.IDFromName(cmd.Context(), srcImageClient, image); err == nil { image = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %s", err) } dstImageClient, err := newGlanceV2Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination image client: %s", err) } dstObjectClient, err := newObjectStorageV1Client(dstProvider, loc.Dst.Region) if err != nil { log.Printf("failed to create destination object storage client, detailed image clone statistics will be unavailable: %s", err) } srcImg, err := waitForImage(cmd.Context(), srcImageClient, nil, image, 0, waitForImageSec) if err != nil { return fmt.Errorf("failed to wait for %q source image: %s", image, err) } if imageWebDownload { userProjectID, err := getAuthProjectID(srcImageClient.ProviderClient) if err != nil { return fmt.Errorf("failed to extract user project ID scope: %s", err) } if userProjectID != srcImg.Owner { return fmt.Errorf("cannot clone an image using web download import method, when an image belongs to another project (%s), try to set --image-web-download=false", srcImg.Owner) } } defer measureTime() dstImg, err := migrateImage(cmd.Context(), srcImageClient, dstImageClient, srcObjectClient, dstObjectClient, srcImg, toName) if err != nil { return err } log.Printf("Target image name is %q (id: %q)", dstImg.Name, dstImg.ID) return nil }, }
ImageCmd represents the image command.
var ( // RootCmd represents the base command when called without any subcommands. RootCmd = &cobra.Command{ Use: "cyclone", Short: "Clone OpenStack entities easily", SilenceUsage: true, } )
var SecretCmd = &cobra.Command{ Use: "secret <name|id|url>", Args: cobra.ExactArgs(1), Short: "Clone a secret", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { secret := args[0] toName := viper.GetString("to-secret-name") toExp := viper.GetString("to-secret-expiration") var toExpiration time.Duration if toExp != "" { var err error toExpiration, err = str2duration.ParseDuration(toExp) if err != nil { return fmt.Errorf("failed to parse --to-add-expiration-duration value: %v", err) } } loc, err := getSrcAndDst("") if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %v", err) } srcSecretClient, err := newSecretManagerV1Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source keymanager client: %v", err) } if v, err := secretsIDFromName(cmd.Context(), srcSecretClient, secret); err == nil { secret = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } srcSecret, err := waitForSecret(cmd.Context(), srcSecretClient, secret, waitForSecretSec) if err != nil { srcSecret, err = waitForSecret(cmd.Context(), srcSecretClient, uuidFromSecretRef(secret), waitForSecretSec) if err != nil { return fmt.Errorf("failed to wait for %q source image: %v", secret, err) } } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %v", err) } dstSecretClient, err := newSecretManagerV1Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination image client: %v", err) } defer measureTime() dstSecret, err := migrateSecret(cmd.Context(), srcSecretClient, dstSecretClient, srcSecret, toName, toExpiration) if err != nil { return err } log.Printf("Target secret name is %q (id: %q)", dstSecret.Name, dstSecret.SecretRef) return nil }, }
SecretCmd represents the secret command.
var SecurityGroupCmd = &cobra.Command{ Use: "security-group <name|id>", Args: cobra.ExactArgs(1), Short: "Clone a security group", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { securityGroup := args[0] toName, _ := cmd.Flags().GetString("to-security-group-name") disableDetection, _ = cmd.Flags().GetBool("disable-target-security-group-detection") loc, err := getSrcAndDst("") if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %v", err) } srcNetworkClient, err := newNetworkV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source network client: %v", err) } if v, err := securityGroupsIDFromName(cmd.Context(), srcNetworkClient, securityGroup); err == nil { securityGroup = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %v", err) } dstNetworkClient, err := newNetworkV2Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination network client: %v", err) } defer measureTime() dstSecurityGroup, err := migrateSecurityGroup(cmd.Context(), srcNetworkClient, dstNetworkClient, securityGroup, toName) if err != nil { return err } log.Printf("Target security group name is %q (id: %q)", dstSecurityGroup.Name, dstSecurityGroup.ID) return nil }, }
SecurityGroupCmd represents the security group command.
var ServerCmd = &cobra.Command{ Use: "server <name|id>", Args: cobra.ExactArgs(1), Short: "Clone a server", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } imageWebDownload = viper.GetBool("image-web-download") return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { server := args[0] toName := viper.GetString("to-server-name") toKeyName := viper.GetString("to-key-name") toFlavor := viper.GetString("to-flavor-name") toNetworkName := viper.GetString("to-network-name") toSubnetName := viper.GetString("to-subnet-name") toAZ := viper.GetString("to-az") toVolumeType := viper.GetString("to-volume-type") cloneViaSnapshot := viper.GetBool("clone-via-snapshot") forceBootable := viper.GetUint("bootable-volume") tries := int(viper.GetUint("tries")) if tries < 0 { return fmt.Errorf("tries cannot be negative") } forceLocal := viper.GetBool("local-disk") deleteVolOnTerm := viper.GetBool("delete-volume-on-termination") bootableDiskOnly := viper.GetBool("bootable-disk-only") skipServerCreation := viper.GetBool("skip-server-creation") if forceBootable > 0 && forceLocal { return fmt.Errorf("cannot use both --bootable-volume and --local-disk flags") } loc, err := getSrcAndDst(toAZ) if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcServerClient, err := newComputeV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source server client: %s", err) } srcImageClient, err := newGlanceV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source image client: %s", err) } var srcObjectClient *gophercloud.ServiceClient if imageWebDownload { srcObjectClient, err = newObjectStorageV1Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source object storage client: %s", err) } } srcVolumeClient, err := newBlockStorageV3Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source volume client: %s", err) } if v, err := servers_utils.IDFromName(cmd.Context(), srcServerClient, server); err == nil { server = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %s", err) } dstServerClient, err := newComputeV2Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination server client: %s", err) } dstImageClient, err := newGlanceV2Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination image client: %s", err) } dstVolumeClient, err := newBlockStorageV3Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination volume client: %s", err) } dstObjectClient, err := newObjectStorageV1Client(dstProvider, loc.Dst.Region) if err != nil { log.Printf("failed to create destination object storage client, detailed image clone statistics will be unavailable: %s", err) } dstNetworkClient, err := newNetworkV2Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination network client: %s", err) } srcServer, err := waitForServer(cmd.Context(), srcServerClient, server, waitForServerSec) if err != nil { return fmt.Errorf("failed to wait for %q source server: %s", server, err) } err = checkServerStatus(cmd.Context(), srcServerClient, srcServer) if err != nil { return err } srcFlavor, err := getSrcFlavor(cmd.Context(), srcServerClient, srcServer) if err != nil { return err } flavor, err := checkFlavor(cmd.Context(), dstServerClient, srcFlavor, &toFlavor) if err != nil { return err } err = checkAvailabilityZone(cmd.Context(), dstServerClient, srcServer.AvailabilityZone, &toAZ, &loc) if err != nil { return err } err = checKeyPair(cmd.Context(), dstServerClient, toKeyName) if err != nil { return err } // check destination network, create a port for a server var port *ports.Port var networkID string var network servers.Network if !skipServerCreation { if toSubnetName != "" { port, err = createServerPort(cmd.Context(), dstNetworkClient, toNetworkName, toSubnetName) if err != nil { return err } network.Port = port.ID defer func() { if err != nil { if err := ports.Delete(cmd.Context(), dstNetworkClient, port.ID).ExtractErr(); err != nil { log.Printf("Error deleting target server port: %s", err) } } }() } else { if toNetworkName == "" { log.Printf("New server network name is empty, detecting the network name from the source server") toNetworkName, err = getServerNetworkName(cmd.Context(), srcServerClient, srcServer) if err != nil { return err } log.Printf("Detected %q network name from the source server", toNetworkName) } networkID, err = networks_utils.IDFromName(cmd.Context(), dstNetworkClient, toNetworkName) if err != nil { return err } network.UUID = networkID } } defer measureTime() var vols []string var bootableVolume bool vols, bootableVolume, err = serverVolumeAttachments(cmd.Context(), srcServerClient, srcServer) if err != nil { return fmt.Errorf("failed to detect server volume attachments: %s", err) } log.Printf("Detected %q attached volumes", vols) if bootableDiskOnly && len(vols) > 1 { if bootableVolume { vols := vols[:1] log.Printf("Processing only the bootable disk: %s", vols) } else { vols = nil log.Printf("Processing only the local disk") } } var dstVolumes []*volumes.Volume var dstImage *images.Image if bootableVolume { log.Printf("The %q volume is a bootable volume", vols[0]) if forceLocal { dstImage, err = bootableToLocal(cmd.Context(), srcVolumeClient, srcImageClient, srcObjectClient, dstImageClient, dstObjectClient, cloneViaSnapshot, toAZ, loc, flavor, &vols) if err != nil { return err } dstImageID := dstImage.ID if !skipServerCreation { defer func() { if err := images.Delete(cmd.Context(), dstImageClient, dstImageID).ExtractErr(); err != nil { log.Printf("Error deleting migrated server snapshot: %s", err) } }() } bootableVolume = false } } else { if forceBootable > 0 && uint(srcFlavor.Disk) > forceBootable { return fmt.Errorf("cannot create a bootable volume with a size less than original disk size: %d", srcFlavor.Disk) } dstImage, err = createServerSnapshot(cmd.Context(), srcServerClient, srcImageClient, dstImageClient, srcObjectClient, dstObjectClient, srcServer, loc) if err != nil { return err } dstImageID := dstImage.ID if !skipServerCreation || forceBootable > 0 { defer func() { if err := images.Delete(cmd.Context(), dstImageClient, dstImageID).ExtractErr(); err != nil { log.Printf("Error deleting migrated server snapshot: %s", err) } }() } if forceBootable > 0 { if uint(srcFlavor.Disk) > forceBootable { return fmt.Errorf("cannot create a bootable volume with a size less than original disk size: %d", srcFlavor.Disk) } log.Printf("Forcing %s image to be converted to a bootable volume", dstImageID) bootableVolume = true var newBootableVolume *volumes.Volume newBootableVolume, err = imageToVolume(cmd.Context(), dstVolumeClient, dstImageClient, dstImage.ID, "", fmt.Sprintf("bootable for %s", dstImage.Name), "", toAZ, int(forceBootable), nil) if err != nil { return fmt.Errorf("failed to create a bootable volume for a VM: %s", err) } dstVolumes = append(dstVolumes, newBootableVolume) log.Printf("Cloned %q server local storage to %q volume in %q availability zone", srcServer.ID, newBootableVolume.ID, toAZ) dstImage = nil } } for i, v := range vols { var srcVolume, dstVolume *volumes.Volume srcVolume, err = waitForVolume(cmd.Context(), srcVolumeClient, v, waitForVolumeSec) if err != nil { return fmt.Errorf("failed to wait for a %q volume: %s", v, err) } dstVolume, err = migrateVolume(cmd.Context(), srcImageClient, srcVolumeClient, srcObjectClient, dstImageClient, dstVolumeClient, dstObjectClient, srcVolume, srcVolume.Name, toVolumeType, toAZ, cloneViaSnapshot, loc) if err != nil { return fmt.Errorf("failed to clone the %q volume: %s", srcVolume.ID, err) } dstVolumes = append(dstVolumes, dstVolume) if toAZ == "" { toAZ = dstVolumes[i].AvailabilityZone } log.Printf("Cloned %q volume to %q volume in %q availability zone", srcVolume.ID, dstVolume.ID, toAZ) } if skipServerCreation { log.Printf("Server artifacts were cloned to %q availability zone", toAZ) return nil } createOpts := createServerOpts(srcServer, toName, flavor.ID, toKeyName, toAZ, network, dstVolumes, dstImage, bootableVolume, deleteVolOnTerm) var dstServer *servers.Server dstServer, err = createServerRetry(cmd.Context(), dstServerClient, createOpts, tries) if err != nil { retErr := err err = nil return retErr } createServerSpeed(dstServer) if dstImage != nil { if _, err := waitForImage(cmd.Context(), dstImageClient, nil, dstImage.ID, 0, waitForImageSec); err != nil { log.Printf("Error waiting for %q image: %s", dstImage.ID, err) } } log.Printf("Server cloned to %q (%q) using %s flavor to %q availability zone", dstServer.Name, dstServer.ID, toFlavor, dstServer.AvailabilityZone) if port != nil { log.Printf("The %q port in the %q subnet was created", port.ID, toSubnetName) } return err }, }
ServerCmd represents the server command.
cobra.ExactArgs(1), Short: "Clone a share", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { share := args[0] toAZ := viper.GetString("to-az") toShareName := viper.GetString("to-share-name") toShareType := viper.GetString("to-share-type") toShareProto := viper.GetString("to-share-proto") toShareNetworkID := viper.GetString("to-share-network-id") loc, err := getSrcAndDst(toAZ) if err != nil { return err } if !loc.SameRegion || !loc.SameProject { return fmt.Errorf("shares can be copied only within the same OpenStack region and project") } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcShareClient, err := newSharedFileSystemV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source share client: %s", err) } if v, err := shares_utils.IDFromName(cmd.Context(), srcShareClient, share); err == nil { share = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } srcShare, err := waitForShare(cmd.Context(), srcShareClient, share, waitForShareSec) if err != nil { return fmt.Errorf("failed to wait for a %q share: %s", share, err) } err = checkShareAvailabilityZone(cmd.Context(), srcShareClient, srcShare.AvailabilityZone, &toAZ, &loc) if err != nil { return err } defer measureTime() dstShare, err := cloneShare(cmd.Context(), srcShareClient, srcShare, toShareName, toShareType, toShareProto, toShareNetworkID, toAZ) if err != nil { return err } log.Printf("Migrated target share name is %q (id: %q) to %q availability zone", dstShare.Name, dstShare.ID, dstShare.AvailabilityZone) return nil }, }Use: "share <name|id>", Args:
ShareCmd represents the share command.
cobra.ExactArgs(1), Short: "Mova a share to a different availability zone", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { share := args[0] toAZ := viper.GetString("to-az") deleteOldReplica := viper.GetBool("delete-old-replica") toShareNetworkID := viper.GetString("to-share-network-id") loc, err := getSrcAndDst(toAZ) if err != nil { return err } if !loc.SameRegion || !loc.SameProject { return fmt.Errorf("shares can be copied only within the same OpenStack region and project") } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcShareClient, err := newSharedFileSystemV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source share client: %s", err) } if v, err := shares_utils.IDFromName(cmd.Context(), srcShareClient, share); err == nil { share = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } srcShare, err := waitForShare(cmd.Context(), srcShareClient, share, waitForShareSec) if err != nil { return fmt.Errorf("failed to wait for a %q share: %s", share, err) } err = checkShareAvailabilityZone(cmd.Context(), srcShareClient, srcShare.AvailabilityZone, &toAZ, &loc) if err != nil { return err } defer measureTime() dstShare, err := moveShare(cmd.Context(), srcShareClient, srcShare, toShareNetworkID, toAZ, deleteOldReplica) if err != nil { return err } log.Printf("Moved target share name is %q (id: %q) to %q availability zone", dstShare.Name, dstShare.ID, dstShare.AvailabilityZone) return nil }, }Use: "move <name|id>", Args:
ShareMoveCmd represents the share move command.
var Version = "dev"
var VersionCmd = &cobra.Command{ Use: "version", Short: "Print version information", DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { fmt.Printf("cyclone %s compiled with %v on %v/%v\n", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) }, }
var VolumeCmd = &cobra.Command{ Use: "volume <name|id>", Args: cobra.ExactArgs(1), Short: "Clone a volume", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } imageWebDownload = viper.GetBool("image-web-download") return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { volume := args[0] toAZ := viper.GetString("to-az") toVolumeName := viper.GetString("to-volume-name") toVolumeType := viper.GetString("to-volume-type") cloneViaSnapshot := viper.GetBool("clone-via-snapshot") loc, err := getSrcAndDst(toAZ) if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcImageClient, err := newGlanceV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source image client: %s", err) } srcVolumeClient, err := newBlockStorageV3Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source volume client: %s", err) } var srcObjectClient *gophercloud.ServiceClient if imageWebDownload { srcObjectClient, err = newObjectStorageV1Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source object storage client: %s", err) } } if v, err := volumes_utils.IDFromName(cmd.Context(), srcVolumeClient, volume); err == nil { volume = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } dstProvider, err := newOpenStackClient(cmd.Context(), loc.Dst) if err != nil { return fmt.Errorf("failed to create a destination OpenStack client: %s", err) } dstImageClient, err := newGlanceV2Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination image client: %s", err) } dstVolumeClient, err := newBlockStorageV3Client(dstProvider, loc.Dst.Region) if err != nil { return fmt.Errorf("failed to create destination volume client: %s", err) } dstObjectClient, err := newObjectStorageV1Client(dstProvider, loc.Dst.Region) if err != nil { log.Printf("failed to create destination object storage client, detailed image clone statistics will be unavailable: %s", err) } srcVolume, err := waitForVolume(cmd.Context(), srcVolumeClient, volume, waitForVolumeSec) if err != nil { return fmt.Errorf("failed to wait for a %q volume: %s", volume, err) } err = checkAvailabilityZone(cmd.Context(), dstVolumeClient, srcVolume.AvailabilityZone, &toAZ, &loc) if err != nil { return err } defer measureTime() dstVolume, err := migrateVolume(cmd.Context(), srcImageClient, srcVolumeClient, srcObjectClient, dstImageClient, dstVolumeClient, dstObjectClient, srcVolume, toVolumeName, toVolumeType, toAZ, cloneViaSnapshot, loc) if err != nil { return err } log.Printf("Migrated target volume name is %q (id: %q) to %q availability zone", dstVolume.Name, dstVolume.ID, dstVolume.AvailabilityZone) return nil }, }
VolumeCmd represents the volume command.
var VolumeToImageCmd = &cobra.Command{ Use: "to-image <name|id>", Args: cobra.ExactArgs(1), Short: "Upload a volume to an image", PreRunE: func(cmd *cobra.Command, args []string) error { if err := parseTimeoutArgs(); err != nil { return err } return viper.BindPFlags(cmd.Flags()) }, RunE: func(cmd *cobra.Command, args []string) error { volume := args[0] toImageName := viper.GetString("to-image-name") cloneViaSnapshot := viper.GetBool("clone-via-snapshot") loc, err := getSrcAndDst("") if err != nil { return err } srcProvider, err := newOpenStackClient(cmd.Context(), loc.Src) if err != nil { return fmt.Errorf("failed to create a source OpenStack client: %s", err) } srcImageClient, err := newGlanceV2Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source image client: %s", err) } srcVolumeClient, err := newBlockStorageV3Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source volume client: %s", err) } srcObjectClient, err := newObjectStorageV1Client(srcProvider, loc.Src.Region) if err != nil { return fmt.Errorf("failed to create source object storage client: %s", err) } if v, err := volumes_utils.IDFromName(cmd.Context(), srcVolumeClient, volume); err == nil { volume = v } else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok { return err } srcVolume, err := waitForVolume(cmd.Context(), srcVolumeClient, volume, waitForVolumeSec) if err != nil { return fmt.Errorf("failed to wait for a %q volume: %s", volume, err) } var toAZ string err = checkAvailabilityZone(cmd.Context(), nil, srcVolume.AvailabilityZone, &toAZ, &loc) if err != nil { return err } defer measureTime() if srcVolume.Status == "in-use" { newVolume, err := cloneVolume(cmd.Context(), srcVolumeClient, srcObjectClient, srcVolume, "", toAZ, cloneViaSnapshot, loc) if err != nil { return err } defer func() { if err := volumes.Delete(cmd.Context(), srcVolumeClient, newVolume.ID, nil).ExtractErr(); err != nil { log.Printf("Failed to delete a cloned volume: %s", err) } }() srcVolume = newVolume } dstImage, err := volumeToImage(cmd.Context(), srcImageClient, srcVolumeClient, srcObjectClient, toImageName, srcVolume) if err != nil { return err } log.Printf("Target image name is %q (id: %q)", dstImage.Name, dstImage.ID) return nil }, }
VolumeToImageCmd represents the volume command.
Functions ¶
Types ¶
type Backoff ¶ added in v0.1.14
Backoff options.
func NewBackoff ¶ added in v0.1.14
func (*Backoff) WaitFor ¶ added in v0.1.14
WaitFor method polls a predicate function, once per interval with an arithmetic backoff, up to a timeout limit. This is an enhanced gophercloud.WaitFor function with a logic from https://github.com/sapcc/go-bits/blob/master/retry/pkg.go