mirror of
https://github.com/harvester/vm-import-controller.git
synced 2025-06-03 01:44:51 +00:00
VM Import VMs Don't Copy Source Descriptions
- Upgrade gophercloud to v2.5.0. - Migrate code according to https://github.com/gophercloud/gophercloud/blob/main/docs/MIGRATING.md - Set the `compute` microversion to 2.19 to get the server description. Related to: https://github.com/harvester/harvester/issues/6464 Signed-off-by: Volker Theile <vtheile@suse.com>
This commit is contained in:
parent
4659913073
commit
5288366b56
4
go.mod
4
go.mod
@ -4,7 +4,7 @@ go 1.23.4
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gophercloud/gophercloud v1.11.0
|
github.com/gophercloud/gophercloud/v2 v2.5.0
|
||||||
github.com/harvester/harvester v1.3.0
|
github.com/harvester/harvester v1.3.0
|
||||||
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0
|
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0
|
||||||
github.com/onsi/ginkgo/v2 v2.22.0
|
github.com/onsi/ginkgo/v2 v2.22.0
|
||||||
@ -96,7 +96,7 @@ require (
|
|||||||
golang.org/x/mod v0.21.0 // indirect
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/oauth2 v0.24.0 // indirect
|
golang.org/x/oauth2 v0.24.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/term v0.27.0 // indirect
|
golang.org/x/term v0.27.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
|
9
go.sum
9
go.sum
@ -553,8 +553,8 @@ github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1a
|
|||||||
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||||
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
|
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
|
||||||
github.com/gophercloud/gophercloud v0.10.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
|
github.com/gophercloud/gophercloud v0.10.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
|
||||||
github.com/gophercloud/gophercloud v1.11.0 h1:ls0O747DIq1D8SUHc7r2vI8BFbMLeLFuENaAIfEx7OM=
|
github.com/gophercloud/gophercloud/v2 v2.5.0 h1:DubPfC43gsZiGZ9LT1IJflVMm+0rck0ejoPsH8D5rqk=
|
||||||
github.com/gophercloud/gophercloud v1.11.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
|
github.com/gophercloud/gophercloud/v2 v2.5.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
@ -1186,7 +1186,6 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@ -1384,8 +1383,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
@ -13,17 +13,16 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud/v2"
|
||||||
"github.com/gophercloud/gophercloud/openstack"
|
"github.com/gophercloud/gophercloud/v2/openstack"
|
||||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"
|
"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes"
|
||||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
|
"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots"
|
||||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots"
|
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors"
|
||||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop"
|
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers"
|
||||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
|
"github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata"
|
||||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
"github.com/gophercloud/gophercloud/v2/openstack/image/v2/images"
|
||||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata"
|
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks"
|
||||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
"github.com/gophercloud/gophercloud/v2/openstack/utils"
|
||||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
@ -37,8 +36,9 @@ import (
|
|||||||
const (
|
const (
|
||||||
NotUniqueName = "notUniqueName"
|
NotUniqueName = "notUniqueName"
|
||||||
NotServerFound = "noServerFound"
|
NotServerFound = "noServerFound"
|
||||||
pollingTimeout = 2 * 60 * 60 // in seconds
|
pollingTimeout = 2 * 60 * 60 * time.Second
|
||||||
annotationDescription = "field.cattle.io/description"
|
annotationDescription = "field.cattle.io/description"
|
||||||
|
computeMicroversion = "2.19"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
@ -62,7 +62,12 @@ type ExtendedVolume struct {
|
|||||||
// - https://docs.openstack.org/api-ref/compute/?expanded=list-all-metadata-detail%2Ccreate-server-detail#show-server-details
|
// - https://docs.openstack.org/api-ref/compute/?expanded=list-all-metadata-detail%2Ccreate-server-detail#show-server-details
|
||||||
type ExtendedServer struct {
|
type ExtendedServer struct {
|
||||||
servers.Server
|
servers.Server
|
||||||
Description string `json:"description,omitempty"`
|
ServerDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerDescription struct {
|
||||||
|
// This requires microversion 2.19 or later.
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient will generate a GopherCloud client
|
// NewClient will generate a GopherCloud client
|
||||||
@ -117,7 +122,7 @@ func NewClient(ctx context.Context, endpoint string, region string, secret *core
|
|||||||
return nil, fmt.Errorf("error generating new client: %v", err)
|
return nil, fmt.Errorf("error generating new client: %v", err)
|
||||||
}
|
}
|
||||||
client.HTTPClient.Transport = tr
|
client.HTTPClient.Transport = tr
|
||||||
err = openstack.Authenticate(client, authOpts)
|
err = openstack.Authenticate(ctx, client, authOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error authenticated client: %v", err)
|
return nil, fmt.Errorf("error authenticated client: %v", err)
|
||||||
}
|
}
|
||||||
@ -132,7 +137,24 @@ func NewClient(ctx context.Context, endpoint string, region string, secret *core
|
|||||||
return nil, fmt.Errorf("error generating compute client: %v", err)
|
return nil, fmt.Errorf("error generating compute client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
imageClient, err := openstack.NewImageServiceV2(client, endPointOpts)
|
// Try to set the `compute` microversion to 2.19 to get the server description.
|
||||||
|
// https://docs.openstack.org/nova/latest/reference/api-microversion-history.html
|
||||||
|
supportedMicroversions, err := utils.GetSupportedMicroversions(ctx, computeClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch supported microversions from compute client: %v", err)
|
||||||
|
}
|
||||||
|
supported, err := supportedMicroversions.IsSupported(computeMicroversion)
|
||||||
|
if err == nil && supported {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"type": computeClient.Type,
|
||||||
|
"minMicroversion": fmt.Sprintf("%d.%d", supportedMicroversions.MinMajor, supportedMicroversions.MinMinor),
|
||||||
|
"maxMicroversion": fmt.Sprintf("%d.%d", supportedMicroversions.MaxMajor, supportedMicroversions.MaxMinor),
|
||||||
|
"microversion": computeMicroversion,
|
||||||
|
}).Debug("Setting custom microversion")
|
||||||
|
computeClient.Microversion = computeMicroversion
|
||||||
|
}
|
||||||
|
|
||||||
|
imageClient, err := openstack.NewImageV2(client, endPointOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error generating image client: %v", err)
|
return nil, fmt.Errorf("error generating image client: %v", err)
|
||||||
}
|
}
|
||||||
@ -156,7 +178,7 @@ func NewClient(ctx context.Context, endpoint string, region string, secret *core
|
|||||||
|
|
||||||
func (c *Client) Verify() error {
|
func (c *Client) Verify() error {
|
||||||
pg := servers.List(c.computeClient, servers.ListOpts{})
|
pg := servers.List(c.computeClient, servers.ListOpts{})
|
||||||
allPg, err := pg.AllPages()
|
allPg, err := pg.AllPages(c.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating all pages: %v", err)
|
return fmt.Errorf("error generating all pages: %v", err)
|
||||||
}
|
}
|
||||||
@ -189,7 +211,7 @@ func (c *Client) PreFlightChecks(vm *migration.VirtualMachineImport) (err error)
|
|||||||
}).Info("Checking the source network as part of the preflight checks")
|
}).Info("Checking the source network as part of the preflight checks")
|
||||||
|
|
||||||
pgr := networks.List(c.networkClient, networks.ListOpts{Name: nm.SourceNetwork})
|
pgr := networks.List(c.networkClient, networks.ListOpts{Name: nm.SourceNetwork})
|
||||||
allPgs, err := pgr.AllPages()
|
allPgs, err := pgr.AllPages(c.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while generating all pages during querying source network '%s': %v", nm.SourceNetwork, err)
|
return fmt.Errorf("error while generating all pages during querying source network '%s': %v", nm.SourceNetwork, err)
|
||||||
}
|
}
|
||||||
@ -215,7 +237,7 @@ func (c *Client) ExportVirtualMachine(vm *migration.VirtualMachineImport) error
|
|||||||
imageName := fmt.Sprintf("import-controller-%s-%d", vm.Spec.VirtualMachineName, vIndex)
|
imageName := fmt.Sprintf("import-controller-%s-%d", vm.Spec.VirtualMachineName, vIndex)
|
||||||
|
|
||||||
// create snapshot for volume
|
// create snapshot for volume
|
||||||
snapInfo, err := snapshots.Create(c.storageClient, snapshots.CreateOpts{
|
snapInfo, err := snapshots.Create(c.ctx, c.storageClient, snapshots.CreateOpts{
|
||||||
Name: imageName,
|
Name: imageName,
|
||||||
VolumeID: v.ID,
|
VolumeID: v.ID,
|
||||||
Force: true,
|
Force: true,
|
||||||
@ -235,14 +257,17 @@ func (c *Client) ExportVirtualMachine(vm *migration.VirtualMachineImport) error
|
|||||||
"snapshot.size": snapInfo.Size,
|
"snapshot.size": snapInfo.Size,
|
||||||
}).Info("Waiting for snapshot to be available")
|
}).Info("Waiting for snapshot to be available")
|
||||||
|
|
||||||
if err := snapshots.WaitForStatus(c.storageClient, snapInfo.ID, "available", pollingTimeout); err != nil {
|
ctxWithTimeout1, cancel1 := context.WithTimeout(c.ctx, pollingTimeout)
|
||||||
|
defer cancel1()
|
||||||
|
|
||||||
|
if err := snapshots.WaitForStatus(ctxWithTimeout1, c.storageClient, snapInfo.ID, "available"); err != nil {
|
||||||
return fmt.Errorf("timeout waiting for snapshot %s to be available: %v", snapInfo.ID, err)
|
return fmt.Errorf("timeout waiting for snapshot %s to be available: %v", snapInfo.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
volObj, err := volumes.Create(c.storageClient, volumes.CreateOpts{
|
volObj, err := volumes.Create(c.ctx, c.storageClient, volumes.CreateOpts{
|
||||||
SnapshotID: snapInfo.ID,
|
SnapshotID: snapInfo.ID,
|
||||||
Size: snapInfo.Size,
|
Size: snapInfo.Size,
|
||||||
}).Extract()
|
}, nil).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -260,7 +285,10 @@ func (c *Client) ExportVirtualMachine(vm *migration.VirtualMachineImport) error
|
|||||||
"retryDelay": c.options.UploadImageRetryDelay,
|
"retryDelay": c.options.UploadImageRetryDelay,
|
||||||
}).Info("Waiting for volume to be available")
|
}).Info("Waiting for volume to be available")
|
||||||
|
|
||||||
if err := volumes.WaitForStatus(c.storageClient, volObj.ID, "available", pollingTimeout); err != nil {
|
ctxWithTimeout2, cancel2 := context.WithTimeout(c.ctx, pollingTimeout)
|
||||||
|
defer cancel2()
|
||||||
|
|
||||||
|
if err := volumes.WaitForStatus(ctxWithTimeout2, c.storageClient, volObj.ID, "available"); err != nil {
|
||||||
return fmt.Errorf("timeout waiting for volume %s to be available: %v", volObj.ID, err)
|
return fmt.Errorf("timeout waiting for volume %s to be available: %v", volObj.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,17 +301,17 @@ func (c *Client) ExportVirtualMachine(vm *migration.VirtualMachineImport) error
|
|||||||
"attachedVolumeID": v.ID,
|
"attachedVolumeID": v.ID,
|
||||||
}).Info("Creating a new image from a volume")
|
}).Info("Creating a new image from a volume")
|
||||||
|
|
||||||
volImage, err := volumeactions.UploadImage(c.storageClient, volObj.ID, volumeactions.UploadImageOpts{
|
volImage, err := volumes.UploadImage(c.ctx, c.storageClient, volObj.ID, volumes.UploadImageOpts{
|
||||||
ImageName: imageName,
|
ImageName: imageName,
|
||||||
DiskFormat: "raw",
|
DiskFormat: "raw",
|
||||||
}).Extract()
|
}).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error while uploading volume image: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for image to be ready
|
// wait for image to be ready
|
||||||
for i := 0; i < c.options.UploadImageRetryCount; i++ {
|
for i := 0; i < c.options.UploadImageRetryCount; i++ {
|
||||||
imgObj, err := images.Get(c.imageClient, volImage.ImageID).Extract()
|
imgObj, err := images.Get(c.ctx, c.imageClient, volImage.ImageID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error checking status of volume image %s: %v", volImage.ImageID, err)
|
return fmt.Errorf("error checking status of volume image %s: %v", volImage.ImageID, err)
|
||||||
}
|
}
|
||||||
@ -309,9 +337,9 @@ func (c *Client) ExportVirtualMachine(vm *migration.VirtualMachineImport) error
|
|||||||
"imageID": volImage.ImageID,
|
"imageID": volImage.ImageID,
|
||||||
}).Info("Downloading an image")
|
}).Info("Downloading an image")
|
||||||
|
|
||||||
contents, err := imagedata.Download(c.imageClient, volImage.ImageID).Extract()
|
contents, err := imagedata.Download(c.ctx, c.imageClient, volImage.ImageID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error downloading volume image %s: %v", volImage.ImageID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawImageFileName := generateRawImageFileName(vm.Status.ImportedVirtualMachineName, vIndex)
|
rawImageFileName := generateRawImageFileName(vm.Status.ImportedVirtualMachineName, vIndex)
|
||||||
@ -321,21 +349,22 @@ func (c *Client) ExportVirtualMachine(vm *migration.VirtualMachineImport) error
|
|||||||
"namespace": vm.Namespace,
|
"namespace": vm.Namespace,
|
||||||
"spec.virtualMachineName": vm.Spec.VirtualMachineName,
|
"spec.virtualMachineName": vm.Spec.VirtualMachineName,
|
||||||
"volume.imageID": volImage.ImageID,
|
"volume.imageID": volImage.ImageID,
|
||||||
|
"rawImageFileName": rawImageFileName,
|
||||||
}).Info("Downloading RAW image")
|
}).Info("Downloading RAW image")
|
||||||
err = writeRawImageFile(filepath.Join(server.TempDir(), rawImageFileName), contents)
|
err = writeRawImageFile(filepath.Join(server.TempDir(), rawImageFileName), contents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error downloading RAW image %s: %v", rawImageFileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := volumes.Delete(c.storageClient, volObj.ID, volumes.DeleteOpts{}).ExtractErr(); err != nil {
|
if err := volumes.Delete(c.ctx, c.storageClient, volObj.ID, volumes.DeleteOpts{}).ExtractErr(); err != nil {
|
||||||
return fmt.Errorf("error deleting volume %s: %v", volObj.ID, err)
|
return fmt.Errorf("error deleting volume %s: %v", volObj.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := snapshots.Delete(c.storageClient, snapInfo.ID).ExtractErr(); err != nil {
|
if err := snapshots.Delete(c.ctx, c.storageClient, snapInfo.ID).ExtractErr(); err != nil {
|
||||||
return fmt.Errorf("error deleting snapshot %s: %v", snapInfo.ID, err)
|
return fmt.Errorf("error deleting snapshot %s: %v", snapInfo.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := images.Delete(c.imageClient, volImage.ImageID).ExtractErr(); err != nil {
|
if err := images.Delete(c.ctx, c.imageClient, volImage.ImageID).ExtractErr(); err != nil {
|
||||||
return fmt.Errorf("error deleting image %s: %v", volImage.ImageID, err)
|
return fmt.Errorf("error deleting image %s: %v", volImage.ImageID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,7 +390,7 @@ func (c *Client) PowerOffVirtualMachine(vm *migration.VirtualMachineImport) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return startstop.Stop(c.computeClient, serverUUID).ExtractErr()
|
return servers.Stop(c.ctx, c.computeClient, serverUUID).ExtractErr()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -387,7 +416,7 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku
|
|||||||
return nil, fmt.Errorf("error finding VM in GenerateVirtualMachine: %v", err)
|
return nil, fmt.Errorf("error finding VM in GenerateVirtualMachine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorObj, err := flavors.Get(c.computeClient, vmObj.Flavor["id"].(string)).Extract()
|
flavorObj, err := flavors.Get(c.ctx, c.computeClient, vmObj.Flavor["id"].(string)).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error looking up flavor: %v", err)
|
return nil, fmt.Errorf("error looking up flavor: %v", err)
|
||||||
}
|
}
|
||||||
@ -410,10 +439,10 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku
|
|||||||
}
|
}
|
||||||
|
|
||||||
if vmObj.Description != "" {
|
if vmObj.Description != "" {
|
||||||
if newVM.GetAnnotations() == nil {
|
if newVM.Annotations == nil {
|
||||||
newVM.Annotations = make(map[string]string)
|
newVM.Annotations = make(map[string]string)
|
||||||
}
|
}
|
||||||
newVM.ObjectMeta.Annotations[annotationDescription] = vmObj.Description
|
newVM.Annotations[annotationDescription] = vmObj.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
vmSpec := kubevirt.VirtualMachineSpec{
|
vmSpec := kubevirt.VirtualMachineSpec{
|
||||||
@ -594,7 +623,7 @@ func (c *Client) checkOrGetUUID(input string) (string, error) {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
pg := servers.List(c.computeClient, servers.ListOpts{Name: input})
|
pg := servers.List(c.computeClient, servers.ListOpts{Name: input})
|
||||||
allPg, err := pg.AllPages()
|
allPg, err := pg.AllPages(c.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error generating all pages in checkorgetuuid :%v", err)
|
return "", fmt.Errorf("error generating all pages in checkorgetuuid :%v", err)
|
||||||
}
|
}
|
||||||
@ -634,9 +663,11 @@ func (c *Client) findVM(name string) (*ExtendedServer, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sr := servers.Get(c.computeClient, parsedUUID)
|
sr := servers.Get(c.ctx, c.computeClient, parsedUUID)
|
||||||
|
|
||||||
var s ExtendedServer
|
var s ExtendedServer
|
||||||
err = sr.ExtractInto(&s)
|
err = sr.ExtractInto(&s)
|
||||||
|
|
||||||
return &s, err
|
return &s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,7 +695,7 @@ func (c *Client) ImageFirmwareSettings(instance *servers.Server) (bool, bool, bo
|
|||||||
var imageID string
|
var imageID string
|
||||||
var uefiType, tpmEnabled, secureBoot bool
|
var uefiType, tpmEnabled, secureBoot bool
|
||||||
for _, v := range instance.AttachedVolumes {
|
for _, v := range instance.AttachedVolumes {
|
||||||
resp := volumes.Get(c.storageClient, v.ID)
|
resp := volumes.Get(c.ctx, c.storageClient, v.ID)
|
||||||
var volInfo volumes.Volume
|
var volInfo volumes.Volume
|
||||||
if err := resp.ExtractIntoStructPtr(&volInfo, "volume"); err != nil {
|
if err := resp.ExtractIntoStructPtr(&volInfo, "volume"); err != nil {
|
||||||
return uefiType, tpmEnabled, secureBoot, fmt.Errorf("error extracting volume info for volume %s: %v", v.ID, err)
|
return uefiType, tpmEnabled, secureBoot, fmt.Errorf("error extracting volume info for volume %s: %v", v.ID, err)
|
||||||
@ -679,7 +710,7 @@ func (c *Client) ImageFirmwareSettings(instance *servers.Server) (bool, bool, bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
imageInfo, err := images.Get(c.imageClient, imageID).Extract()
|
imageInfo, err := images.Get(c.ctx, c.imageClient, imageID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return uefiType, tpmEnabled, secureBoot, fmt.Errorf("error getting image details for image %s: %v", imageID, err)
|
return uefiType, tpmEnabled, secureBoot, fmt.Errorf("error getting image details for image %s: %v", imageID, err)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -200,3 +201,25 @@ func Test_SourceGetOptions(t *testing.T) {
|
|||||||
assert.Equal(options.UploadImageRetryDelay, tc.expected.UploadImageRetryDelay, tc.desc)
|
assert.Equal(options.UploadImageRetryDelay, tc.expected.UploadImageRetryDelay, tc.desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_ExtendedServer(t *testing.T) {
|
||||||
|
assert := require.New(t)
|
||||||
|
|
||||||
|
var dejson any
|
||||||
|
sejson := []byte(`{"server": {"id": "b3693d06-8135-4c7c-b3ea-d37b2cc6fb8f", "name": "cirros-tiny", "status": "SHUTOFF", "tenant_id": "88c800f12d7d4e4e93b2e2883aed1bf5", "user_id": "94ebd4b2c5a140dd9bc20dc5139d6823", "metadata": {}, "hostId": "d44ae638ea333eefe401ae01c9dec9add9ed7b6cad1024a3a220d1f4", "image": "", "flavor": {"id": "1", "links": [{"rel": "bookmark", "href": "http://48.151.623.42/compute/flavors/1"}]}, "created": "2025-02-18T17:07:24Z", "updated": "2025-02-18T17:25:13Z", "addresses": {"shared": [{"version": 4, "addr": "192.168.233.13", "OS-EXT-IPS:type": "fixed", "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:25:90:74"}]}, "accessIPv4": "", "accessIPv6": "", "links": [{"rel": "self", "href": "http://48.151.623.42/compute/v2.1/servers/b3693d06-8135-4c7c-b3ea-d37b2cc6fb8f"}, {"rel": "bookmark", "href": "http://48.151.623.42/compute/servers/b3693d06-8135-4c7c-b3ea-d37b2cc6fb8f"}], "OS-DCF:diskConfig": "AUTO", "OS-EXT-AZ:availability_zone": "nova", "config_drive": "", "key_name": null, "OS-SRV-USG:launched_at": "2025-02-18T17:08:02.000000", "OS-SRV-USG:terminated_at": null, "OS-EXT-SRV-ATTR:host": "opnstk-server-vm", "OS-EXT-SRV-ATTR:instance_name": "instance-00000002", "OS-EXT-SRV-ATTR:hypervisor_hostname": "opnstk-server-vm", "OS-EXT-SRV-ATTR:reservation_id": "r-j7s0gpwg", "OS-EXT-SRV-ATTR:launch_index": 0, "OS-EXT-SRV-ATTR:hostname": "cirros-test", "OS-EXT-SRV-ATTR:kernel_id": "", "OS-EXT-SRV-ATTR:ramdisk_id": "", "OS-EXT-SRV-ATTR:root_device_name": "/dev/vda", "OS-EXT-SRV-ATTR:user_data": null, "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "stopped", "OS-EXT-STS:power_state": 4, "os-extended-volumes:volumes_attached": [{"id": "e6565b2e-6f99-45e8-9278-fd4b4b35a1ea", "delete_on_termination": false}], "host_status": "UP", "locked": false, "description": "test foo bar"}}`)
|
||||||
|
err := json.Unmarshal(sejson, &dejson)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sr := servers.GetResult{}
|
||||||
|
sr.Body = dejson
|
||||||
|
|
||||||
|
var s ExtendedServer
|
||||||
|
err = sr.ExtractInto(&s)
|
||||||
|
|
||||||
|
assert.NoError(err, "expect no error during extract")
|
||||||
|
assert.Equal(s.Name, "cirros-tiny", "expect name to be 'cirros-tiny'")
|
||||||
|
assert.Equal(s.Status, "", "expect status to be 'SHUTOFF'")
|
||||||
|
assert.Equal(s.Description, "test foo bar", "expect description to be 'test foo bar'")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user