Skip to content

Commit e7cc335

Browse files
committed
Add a flag to skip the target server creation, clone server's images or volumes only
1 parent faf14b2 commit e7cc335

File tree

2 files changed

+60
-32
lines changed

2 files changed

+60
-32
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ $ source openrc-of-the-source-project
132132
$ cyclone server 6eb76733-95b7-4867-9f83-a6ab19804e2f --local-disk --to-key-name my-nova-keypair
133133
```
134134

135+
### Clone only server artifacts
136+
137+
The `--skip-server-creation` flag clones only images or volumes, which are used or attached to the source server. The destination server won't be created.
138+
The example below will convert the server's local bootable disk to a bootable block storage, which can be attached to some server lately.
139+
140+
```sh
141+
$ source openrc-of-the-source-project
142+
$ cyclone server 6eb76733-95b7-4867-9f83-a6ab19804e2f --bootable-volume 64 --skip-server-creation
143+
```
144+
135145
### Upload a local image file into a backup
136146

137147
Properties must be defined, when a backup supposed to be restored to a bootable volume.

pkg/server.go

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,7 @@ var ServerCmd = &cobra.Command{
635635
forceLocal := viper.GetBool("local-disk")
636636
deleteVolOnTerm := viper.GetBool("delete-volume-on-termination")
637637
bootableDiskOnly := viper.GetBool("bootable-disk-only")
638+
skipServerCreation := viper.GetBool("skip-server-creation")
638639

639640
if forceBootable > 0 && forceLocal {
640641
return fmt.Errorf("cannot use both --bootable-volume and --local-disk flags")
@@ -744,35 +745,38 @@ var ServerCmd = &cobra.Command{
744745
var network servers.Network
745746
// TODO: detect network settings from the source VM, when the same project is used
746747
// TODO: do this only when specific subnet name was provided, otherwise use auto-allocation
747-
if toSubnetName != "" {
748-
// TODO: a regular server deletion doesn't delete the port, find the way to hard bind server and port
749-
port, err = createServerPort(dstNetworkClient, toNetworkName, toSubnetName)
750-
if err != nil {
751-
return err
752-
}
753-
network.Port = port.ID
754-
defer func() {
748+
749+
if !skipServerCreation {
750+
if toSubnetName != "" {
751+
// TODO: a regular server deletion doesn't delete the port, find the way to hard bind server and port
752+
port, err = createServerPort(dstNetworkClient, toNetworkName, toSubnetName)
755753
if err != nil {
756-
// delete the port only on error
757-
if err := ports.Delete(dstNetworkClient, port.ID).ExtractErr(); err != nil {
758-
log.Printf("Error deleting target server port: %s", err)
754+
return err
755+
}
756+
network.Port = port.ID
757+
defer func() {
758+
if err != nil {
759+
// delete the port only on error
760+
if err := ports.Delete(dstNetworkClient, port.ID).ExtractErr(); err != nil {
761+
log.Printf("Error deleting target server port: %s", err)
762+
}
759763
}
764+
}()
765+
} else {
766+
if toNetworkName == "" {
767+
log.Printf("New server network name is empty, detecting the network name from the source server")
768+
toNetworkName, err = getServerNetworkName(srcServerClient, srcServer)
769+
if err != nil {
770+
return err
771+
}
772+
log.Printf("Detected %q network name from the source server", toNetworkName)
760773
}
761-
}()
762-
} else {
763-
if toNetworkName == "" {
764-
log.Printf("New server network name is empty, detecting the network name from the source server")
765-
toNetworkName, err = getServerNetworkName(srcServerClient, srcServer)
774+
networkID, err = networks_utils.IDFromName(dstNetworkClient, toNetworkName)
766775
if err != nil {
767776
return err
768777
}
769-
log.Printf("Detected %q network name from the source server", toNetworkName)
770-
}
771-
networkID, err = networks_utils.IDFromName(dstNetworkClient, toNetworkName)
772-
if err != nil {
773-
return err
778+
network.UUID = networkID
774779
}
775-
network.UUID = networkID
776780
}
777781

778782
defer measureTime()
@@ -808,11 +812,13 @@ var ServerCmd = &cobra.Command{
808812

809813
// TODO: add an option to keep artifacts on failure
810814
dstImageID := dstImage.ID
811-
defer func() {
812-
if err := images.Delete(dstImageClient, dstImageID).ExtractErr(); err != nil {
813-
log.Printf("Error deleting migrated server snapshot: %s", err)
814-
}
815-
}()
815+
if !skipServerCreation {
816+
defer func() {
817+
if err := images.Delete(dstImageClient, dstImageID).ExtractErr(); err != nil {
818+
log.Printf("Error deleting migrated server snapshot: %s", err)
819+
}
820+
}()
821+
}
816822

817823
// set bootable volume flag to false, because we set a proper dstImage var
818824
bootableVolume = false
@@ -830,11 +836,13 @@ var ServerCmd = &cobra.Command{
830836

831837
// TODO: add an option to keep artifacts on failure
832838
dstImageID := dstImage.ID
833-
defer func() {
834-
if err := images.Delete(dstImageClient, dstImageID).ExtractErr(); err != nil {
835-
log.Printf("Error deleting migrated server snapshot: %s", err)
836-
}
837-
}()
839+
if !skipServerCreation || forceBootable > 0 {
840+
defer func() {
841+
if err := images.Delete(dstImageClient, dstImageID).ExtractErr(); err != nil {
842+
log.Printf("Error deleting migrated server snapshot: %s", err)
843+
}
844+
}()
845+
}
838846

839847
if forceBootable > 0 {
840848
if uint(srcFlavor.Disk) > forceBootable {
@@ -848,6 +856,9 @@ var ServerCmd = &cobra.Command{
848856
return fmt.Errorf("failed to create a bootable volume for a VM: %s", err)
849857
}
850858
dstVolumes = append(dstVolumes, newBootableVolume)
859+
860+
log.Printf("Cloned %q server local storage to %q volume in %q availability zone", srcServer.ID, newBootableVolume.ID, toAZ)
861+
851862
// release dstImage pointer
852863
dstImage = nil
853864
}
@@ -871,9 +882,15 @@ var ServerCmd = &cobra.Command{
871882
if toAZ == "" {
872883
toAZ = dstVolumes[i].AvailabilityZone
873884
}
885+
log.Printf("Cloned %q volume to %q volume in %q availability zone", srcVolume.ID, dstVolume.ID, toAZ)
874886
// TODO: defer delete volumes on failure?
875887
}
876888

889+
if skipServerCreation {
890+
log.Printf("Server artifacts were cloned to %q availability zone", toAZ)
891+
return nil
892+
}
893+
877894
createOpts := createServerOpts(srcServer, toName, flavor.ID, toKeyName, toAZ, network, dstVolumes, dstImage, bootableVolume, deleteVolOnTerm)
878895
dstServer := new(serverExtended)
879896
err = servers.Create(dstServerClient, createOpts).ExtractInto(dstServer)
@@ -927,4 +944,5 @@ func initServerCmdFlags() {
927944
ServerCmd.Flags().BoolP("local-disk", "", false, "convert the attached bootable volume to a local disk")
928945
ServerCmd.Flags().BoolP("delete-volume-on-termination", "", true, "specifies whether or not to delete the attached bootable volume when the server is terminated")
929946
ServerCmd.Flags().BoolP("bootable-disk-only", "", false, "clone only the bootable disk/volume, skipping the rest attached volumes")
947+
ServerCmd.Flags().BoolP("skip-server-creation", "", false, "skip server creation, clone only server's artifacts: image and volumes")
930948
}

0 commit comments

Comments
 (0)