pkg

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2023 License: Apache-2.0 Imports: 76 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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(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(srcVolumeClient, backup); err == nil {
			backup = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		dstProvider, err := newOpenStackClient(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(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(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
	},
}

BackupCmd represents the volume command

View Source
var BackupCmd = &cobra.Command{
	Use: "backup",
}

BackupCmd represents the backup command

View Source
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(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(dstVolumeClient, "", &toAZ, &loc)
		if err != nil {
			return err
		}

		if v, err := backups_utils.IDFromName(dstVolumeClient, backup); err == nil {
			backup = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		backupObj, err := waitForBackup(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(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
	},
}
View Source
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(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(srcImageClient, image); err == nil {
			image = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		dstProvider, err := newOpenStackClient(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(dstVolumeClient, "", &toAZ, &loc)
		if err != nil {
			return err
		}

		defer measureTime()

		backup, err := uploadBackup(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(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

	},
}
View Source
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(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(srcImageClient, image); err == nil {
			image = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		dstProvider, err := newOpenStackClient(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(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(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

View Source
var (
	// RootCmd represents the base command when called without any subcommands
	RootCmd = &cobra.Command{
		Use:          "cyclone",
		Short:        "Clone OpenStack entities easily",
		SilenceUsage: true,
	}
)
View Source
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(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(srcServerClient, server); err == nil {
			server = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		dstProvider, err := newOpenStackClient(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(srcServerClient, server, waitForServerSec)
		if err != nil {
			return fmt.Errorf("failed to wait for %q source server: %s", server, err)
		}

		err = checkServerStatus(srcServerClient, srcServer)
		if err != nil {
			return err
		}

		srcFlavor, err := getSrcFlavor(srcServerClient, srcServer)
		if err != nil {
			return err
		}
		flavor, err := checkFlavor(dstServerClient, srcFlavor, &toFlavor)
		if err != nil {
			return err
		}

		err = checkAvailabilityZone(dstServerClient, srcServer.AvailabilityZone, &toAZ, &loc)
		if err != nil {
			return err
		}

		err = checKeyPair(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(dstNetworkClient, toNetworkName, toSubnetName)
				if err != nil {
					return err
				}
				network.Port = port.ID
				defer func() {
					if err != nil {

						if err := ports.Delete(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(srcServerClient, srcServer)
					if err != nil {
						return err
					}
					log.Printf("Detected %q network name from the source server", toNetworkName)
				}
				networkID, err = networks_utils.IDFromName(dstNetworkClient, toNetworkName)
				if err != nil {
					return err
				}
				network.UUID = networkID
			}
		}

		defer measureTime()

		var vols []string
		var bootableVolume bool
		vols, bootableVolume, err = serverVolumeAttachments(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(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(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(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(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(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(srcVolumeClient, v, waitForVolumeSec)
			if err != nil {
				return fmt.Errorf("failed to wait for a %q volume: %s", v, err)
			}

			dstVolume, err = migrateVolume(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 *serverExtended
		dstServer, err = createServerRetry(dstServerClient, createOpts, tries)
		if err != nil {

			retErr := err
			err = nil
			return retErr
		}

		createServerSpeed(dstServer)

		if dstImage != nil {

			if _, err := waitForImage(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

View Source
var ShareCmd = &cobra.Command{
	Use:   "share <name|id>",
	Args:  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(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(srcShareClient, share); err == nil {
			share = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		srcShare, err := waitForShare(srcShareClient, share, waitForShareSec)
		if err != nil {
			return fmt.Errorf("failed to wait for a %q share: %s", share, err)
		}

		err = checkShareAvailabilityZone(srcShareClient, srcShare.AvailabilityZone, &toAZ, &loc)
		if err != nil {
			return err
		}

		defer measureTime()

		dstShare, err := cloneShare(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
	},
}

ShareCmd represents the share command

View Source
var ShareMoveCmd = &cobra.Command{
	Use:   "move <name|id>",
	Args:  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
		}

		srcProvider, err := newOpenStackClient(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(srcShareClient, share); err == nil {
			share = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		srcShare, err := waitForShare(srcShareClient, share, waitForShareSec)
		if err != nil {
			return fmt.Errorf("failed to wait for a %q share: %s", share, err)
		}

		err = checkShareAvailabilityZone(srcShareClient, srcShare.AvailabilityZone, &toAZ, &loc)
		if err != nil {
			return err
		}

		defer measureTime()

		dstShare, err := moveShare(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
	},
}

ShareMoveCmd represents the share move command

View Source
var Version = "dev"
View Source
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)
	},
}
View Source
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(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(srcVolumeClient, volume); err == nil {
			volume = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		dstProvider, err := newOpenStackClient(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(srcVolumeClient, volume, waitForVolumeSec)
		if err != nil {
			return fmt.Errorf("failed to wait for a %q volume: %s", volume, err)
		}

		err = checkAvailabilityZone(dstVolumeClient, srcVolume.AvailabilityZone, &toAZ, &loc)
		if err != nil {
			return err
		}

		defer measureTime()

		dstVolume, err := migrateVolume(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

View Source
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(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(srcVolumeClient, volume); err == nil {
			volume = v
		} else if err, ok := err.(gophercloud.ErrMultipleResourcesFound); ok {
			return err
		}

		srcVolume, err := waitForVolume(srcVolumeClient, volume, waitForVolumeSec)
		if err != nil {
			return fmt.Errorf("failed to wait for a %q volume: %s", volume, err)
		}

		var toAZ string
		err = checkAvailabilityZone(nil, srcVolume.AvailabilityZone, &toAZ, &loc)
		if err != nil {
			return err
		}

		defer measureTime()

		if srcVolume.Status == "in-use" {

			newVolume, err := cloneVolume(srcVolumeClient, srcObjectClient, srcVolume, "", toAZ, cloneViaSnapshot, loc)
			if err != nil {
				return err
			}

			defer func() {
				if err := volumes.Delete(srcVolumeClient, newVolume.ID, nil).ExtractErr(); err != nil {
					log.Printf("Failed to delete a cloned volume: %s", err)
				}
			}()

			srcVolume = newVolume
		}

		dstImage, err := volumeToImage(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

func Execute

func Execute()

Execute adds all child commands to the root command sets flags appropriately. This is called by main.main(). It only needs to happen once to the rootCmd.

Types

type Backoff added in v0.1.14

type Backoff struct {
	Timeout     int
	Factor      int
	MaxInterval time.Duration
}

Backoff options.

func NewBackoff added in v0.1.14

func NewBackoff(timeout int, factor int, maxInterval time.Duration) *Backoff

func (*Backoff) WaitFor added in v0.1.14

func (eb *Backoff) WaitFor(predicate func() (bool, error)) error

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

type Location

type Location struct {
	AuthURL                     string
	Region                      string
	Domain                      string
	Project                     string
	Username                    string
	Password                    string
	ApplicationCredentialName   string
	ApplicationCredentialID     string
	ApplicationCredentialSecret string
	Token                       string
	Origin                      string
}

type Locations

type Locations struct {
	Src         Location
	Dst         Location
	SameRegion  bool
	SameAZ      bool
	SameProject bool
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL