diff --git a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml index fc1f38d66..ff0554bc6 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml @@ -377,6 +377,9 @@ spec: The VM Namespace Only relevant for an openshift source. type: string + newName: + description: The new name of the VM after matching DNS1123 requirements. + type: string operatingSystem: description: The Operating System detected by virt-v2v. type: string diff --git a/operator/config/crd/bases/forklift.konveyor.io_plans.yaml b/operator/config/crd/bases/forklift.konveyor.io_plans.yaml index 4366bd7ea..f6da3b769 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_plans.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_plans.yaml @@ -888,6 +888,10 @@ spec: The VM Namespace Only relevant for an openshift source. type: string + newName: + description: The new name of the VM after matching DNS1123 + requirements. + type: string operatingSystem: description: The Operating System detected by virt-v2v. type: string diff --git a/pkg/apis/forklift/v1beta1/plan/vm.go b/pkg/apis/forklift/v1beta1/plan/vm.go index a92820add..45f5c8b3b 100644 --- a/pkg/apis/forklift/v1beta1/plan/vm.go +++ b/pkg/apis/forklift/v1beta1/plan/vm.go @@ -72,6 +72,8 @@ type VMStatus struct { Firmware string `json:"firmware,omitempty"` // The Operating System detected by virt-v2v. OperatingSystem string `json:"operatingSystem,omitempty"` + // The new name of the VM after matching DNS1123 requirements. + NewName string `json:"newName,omitempty"` // Conditions. libcnd.Conditions `json:",inline"` diff --git a/pkg/controller/plan/BUILD.bazel b/pkg/controller/plan/BUILD.bazel index 82ff64394..eb404d678 100644 --- a/pkg/controller/plan/BUILD.bazel +++ b/pkg/controller/plan/BUILD.bazel @@ -22,10 +22,10 @@ go_library( "//pkg/controller/base", "//pkg/controller/plan/adapter", "//pkg/controller/plan/adapter/base", - "//pkg/controller/plan/adapter/ova", "//pkg/controller/plan/context", "//pkg/controller/plan/handler", "//pkg/controller/plan/scheduler", + "//pkg/controller/plan/util", "//pkg/controller/provider/web", "//pkg/controller/provider/web/vsphere", "//pkg/controller/validation", diff --git a/pkg/controller/plan/adapter/ova/BUILD.bazel b/pkg/controller/plan/adapter/ova/BUILD.bazel index 7e226422e..0d5368639 100644 --- a/pkg/controller/plan/adapter/ova/BUILD.bazel +++ b/pkg/controller/plan/adapter/ova/BUILD.bazel @@ -7,7 +7,6 @@ go_library( "builder.go", "client.go", "destinationclient.go", - "ovfparser.go", "validator.go", ], importpath = "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/ova", @@ -27,7 +26,6 @@ go_library( "//pkg/lib/error", "//pkg/lib/inventory/web", "//pkg/lib/itinerary", - "//pkg/lib/logging", "//vendor/github.com/go-logr/logr", "//vendor/k8s.io/api/core/v1:core", "//vendor/k8s.io/apimachinery/pkg/api/resource", diff --git a/pkg/controller/plan/adapter/ova/ovfparser.go b/pkg/controller/plan/adapter/ova/ovfparser.go deleted file mode 100644 index 1ba4e2d1a..000000000 --- a/pkg/controller/plan/adapter/ova/ovfparser.go +++ /dev/null @@ -1,146 +0,0 @@ -package ova - -import ( - "encoding/xml" - "fmt" - "strings" - - "github.com/konveyor/forklift-controller/pkg/lib/logging" -) - -const ( - // Name. - Name = "virt-v2v-parser" -) - -// Package logger. -var log = logging.WithName(Name) - -// Map of osinfo ids to vmware guest ids. -var osV2VMap = map[string]string{ - "centos6": "centos6_64Guest", - "centos7": "centos7_64Guest", - "centos8": "centos8_64Guest", - "centos9": "centos9_64Guest", - "rhel7": "rhel7_64Guest", - "rhel8": "rhel8_64Guest", - "rhel9": "rhel9_64Guest", - "rocky": "rockylinux_64Guest", - "sles10": "sles10_64Guest", - "sles11": "sles11_64Guest", - "sles12": "sles12_64Guest", - "sles15": "sles15_64Guest", - "sles16": "sles16_64Guest", - "opensuse": "opensuse64Guest", - "debian4": "debian4_64Guest", - "debian5": "debian5_64Guest", - "debian6": "debian6_64Guest", - "debian7": "debian7_64Guest", - "debian8": "debian8_64Guest", - "debian9": "debian9_64Guest", - "debian10": "debian10_64Guest", - "debian11": "debian11_64Guest", - "debian12": "debian12_64Guest", - "ubuntu": "ubuntu64Guest", - "fedora": "fedora64Guest", - "win7": "windows7Server64Guest", - "win8": "windows8Server64Guest", - "win10": "windows9Server64Guest", - "win11": "windows11_64Guest", - "win12": "windows12_64Guest", - "win2k19": "windows2019srv_64Guest", - "win2k22": "windows2022srvNext_64Guest", -} - -type OvaVmconfig struct { - XMLName xml.Name `xml:"domain"` - Name string `xml:"name"` - OS OS `xml:"os"` - Metadata Metadata `xml:"metadata"` -} - -type OS struct { - Type OSType `xml:"type"` - Loader Loader `xml:"loader"` - Nvram Nvram `xml:"nvram"` -} - -type Metadata struct { - LibOsInfo LibOsInfo `xml:"libosinfo"` -} - -type LibOsInfo struct { - V2VOS V2VOS `xml:"os"` -} - -type V2VOS struct { - ID string `xml:"id,attr"` -} - -type OSType struct { - Arch string `xml:"arch,attr"` - Machine string `xml:"machine,attr"` - Content string `xml:",chardata"` -} - -type Loader struct { - Readonly string `xml:"readonly,attr"` - Type string `xml:"type,attr"` - Secure string `xml:"secure,attr"` - Path string `xml:",chardata"` -} - -type Nvram struct { - Template string `xml:"template,attr"` -} - -func readConfFromXML(xmlData string) (*OvaVmconfig, error) { - var vmConfig OvaVmconfig - - reader := strings.NewReader(xmlData) - decoder := xml.NewDecoder(reader) - - err := decoder.Decode(&vmConfig) - if err != nil { - return &vmConfig, err - } - return &vmConfig, nil -} - -func GetFirmwareFromConfig(vmConfigXML string) (firmware string, err error) { - xmlConf, err := readConfFromXML(vmConfigXML) - if err != nil { - return - } - - path := xmlConf.OS.Loader.Path - if strings.Contains(path, "OVMF") { - return UEFI, nil - } - return BIOS, nil -} - -func GetOperationSystemFromConfig(vmConfigXML string) (os string, err error) { - xmlConf, err := readConfFromXML(vmConfigXML) - if err != nil { - return - } - return mapOs(xmlConf.Metadata.LibOsInfo.V2VOS.ID), nil -} - -func mapOs(xmlOs string) (os string) { - split := strings.Split(xmlOs, "/") - distro := split[3] - switch distro { - case "rocky", "opensuse", "ubuntu", "fedora": - os = distro - default: - os = split[3] + strings.Split(split[4], ".")[0] - } - os, ok := osV2VMap[os] - if !ok { - log.Info(fmt.Sprintf("Received %s, mapped to: %s", xmlOs, os)) - os = "otherGuest64" - } - return -} diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 5de3e1f0e..71d59feeb 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -16,6 +16,7 @@ import ( "time" planbase "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/base" + "github.com/konveyor/forklift-controller/pkg/controller/plan/util" "github.com/konveyor/forklift-controller/pkg/controller/provider/web" model "github.com/konveyor/forklift-controller/pkg/controller/provider/web/vsphere" libref "github.com/konveyor/forklift-controller/pkg/lib/ref" @@ -28,7 +29,6 @@ import ( "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - k8svalidation "k8s.io/apimachinery/pkg/util/validation" cnv "kubevirt.io/api/core/v1" instancetypeapi "kubevirt.io/api/instancetype" instancetype "kubevirt.io/api/instancetype/v1beta1" @@ -38,7 +38,6 @@ import ( "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter" - ovfparser "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/ova" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" libcnd "github.com/konveyor/forklift-controller/pkg/lib/condition" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" @@ -941,7 +940,7 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s return } - url := fmt.Sprintf("http://%s:8080/ovf", pod.Status.PodIP) + url := fmt.Sprintf("http://%s:8080/vm", pod.Status.PodIP) /* Due to the virt-v2v operation, the ovf file is only available after the command's execution, meaning it appears following the copydisks phase. @@ -959,24 +958,24 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s } defer resp.Body.Close() - vmConfigBytes, err := io.ReadAll(resp.Body) + vmConf, err := io.ReadAll(resp.Body) if err != nil { err = liberr.Wrap(err) return } - vmConfigXML := string(vmConfigBytes) - switch r.Source.Provider.Type() { case api.Ova: - if vm.Firmware, err = ovfparser.GetFirmwareFromConfig(vmConfigXML); err != nil { + if vm.Firmware, err = util.GetFirmwareFromYaml(vmConf); err != nil { err = liberr.Wrap(err) return } - case api.VSphere: - if vm.OperatingSystem, err = ovfparser.GetOperationSystemFromConfig(vmConfigXML); err != nil { + r.Log.Info("Setting the vm firmware ", vm.Firmware, "vmId", vm.ID) + + if vm.OperatingSystem, err = util.GetOperationSystemFromYaml(vmConf); err != nil { err = liberr.Wrap(err) return } + r.Log.Info("Setting the vm OS ", vm.OperatingSystem, "vmId", vm.ID) } shutdownURL := fmt.Sprintf("http://%s:8080/shutdown", pod.Status.PodIP) @@ -1260,13 +1259,10 @@ func (r *KubeVirt) virtualMachine(vm *plan.VMStatus) (object *cnv.VirtualMachine //convention it will be automatically changed. var originalName string - if errs := k8svalidation.IsDNS1123Label(vm.Name); len(errs) > 0 { + if vm.NewName != "" { originalName = vm.Name - vm.Name, err = r.changeVmNameDNS1123(vm.Name, r.Plan.Spec.TargetNamespace) - if err != nil { - r.Log.Error(err, "Failed to update the VM name to meet DNS1123 protocol requirements.") - return - } + vm.Name = vm.NewName + r.Log.Info("VM name is incompatible with DNS1123 RFC, renaming", "originalName", originalName, "newName", vm.Name) } @@ -1722,6 +1718,15 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, Value: vm.RootDisk, }) } + + if vm.NewName != "" { + environment = append(environment, + core.EnvVar{ + Name: "V2V_NewName", + Value: vm.NewName, + }) + } + // pod annotations annotations := map[string]string{} if r.Plan.Spec.TransferNetwork != nil { diff --git a/pkg/controller/plan/migration.go b/pkg/controller/plan/migration.go index 8554f33e8..358f216bb 100644 --- a/pkg/controller/plan/migration.go +++ b/pkg/controller/plan/migration.go @@ -30,6 +30,7 @@ import ( meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + k8svalidation "k8s.io/apimachinery/pkg/util/validation" cdi "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -718,6 +719,13 @@ func (r *Migration) execute(vm *plan.VMStatus) (err error) { err = nil break } + if errs := k8svalidation.IsDNS1123Label(vm.Name); len(errs) > 0 { + vm.NewName, err = r.kubevirt.changeVmNameDNS1123(vm.Name, r.Plan.Spec.TargetNamespace) + if err != nil { + r.Log.Error(err, "Failed to update the VM name to meet DNS1123 protocol requirements.") + return + } + } vm.Phase = r.next(vm.Phase) case PreHook, PostHook: runner := HookRunner{Context: r.Context} diff --git a/pkg/controller/plan/util/BUILD.bazel b/pkg/controller/plan/util/BUILD.bazel index 195f4cb78..dff289312 100644 --- a/pkg/controller/plan/util/BUILD.bazel +++ b/pkg/controller/plan/util/BUILD.bazel @@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "util", srcs = [ + "kubevirtvmparser.go", "openstack.go", "ovirt.go", "utils.go", @@ -13,7 +14,9 @@ go_library( "//pkg/apis/forklift/v1beta1", "//pkg/controller/provider/web/openstack", "//pkg/controller/provider/web/ovirt", + "//pkg/lib/logging", "//pkg/settings", + "//vendor/gopkg.in/yaml.v2:yaml_v2", "//vendor/k8s.io/api/core/v1:core", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", ], diff --git a/pkg/controller/plan/util/kubevirtvmparser.go b/pkg/controller/plan/util/kubevirtvmparser.go new file mode 100644 index 000000000..37ab5d95e --- /dev/null +++ b/pkg/controller/plan/util/kubevirtvmparser.go @@ -0,0 +1,135 @@ +package util + +import ( + "fmt" + "strings" + + "github.com/konveyor/forklift-controller/pkg/lib/logging" + "gopkg.in/yaml.v2" +) + +const ( + // Name. + Name = "kubevirt-vm-parser" +) + +// Package logger. +var log = logging.WithName(Name) + +// Map of osinfo ids to vmware guest ids. +var osV2VMap = map[string]string{ + "centos6": "centos6_64Guest", + "centos7": "centos7_64Guest", + "centos8": "centos8_64Guest", + "centos9": "centos9_64Guest", + "rhel7": "rhel7_64Guest", + "rhel8": "rhel8_64Guest", + "rhel9": "rhel9_64Guest", + "rocky": "rockylinux_64Guest", + "sles10": "sles10_64Guest", + "sles11": "sles11_64Guest", + "sles12": "sles12_64Guest", + "sles15": "sles15_64Guest", + "sles16": "sles16_64Guest", + "opensuse": "opensuse64Guest", + "debian4": "debian4_64Guest", + "debian5": "debian5_64Guest", + "debian6": "debian6_64Guest", + "debian7": "debian7_64Guest", + "debian8": "debian8_64Guest", + "debian9": "debian9_64Guest", + "debian10": "debian10_64Guest", + "debian11": "debian11_64Guest", + "debian12": "debian12_64Guest", + "ubuntu": "ubuntu64Guest", + "fedora": "fedora64Guest", + "win7": "windows7Server64Guest", + "win8": "windows8Server64Guest", + "win10": "windows9Server64Guest", + "win11": "windows11_64Guest", + "win12": "windows12_64Guest", + "win2k19": "windows2019srv_64Guest", + "win2k22": "windows2022srvNext_64Guest", +} + +type OS struct { + Firmware string `yaml:"firmware"` +} + +type Domain struct { + OS OS `yaml:"os"` +} + +type TemplateSpec struct { + Domain Domain `yaml:"domain"` +} + +type Template struct { + Spec TemplateSpec `yaml:"spec"` +} + +type VirtualMachineSpec struct { + Template Template `yaml:"template"` +} + +type VirtualMachine struct { + APIVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Metadata Metadata `yaml:"metadata"` + Spec VirtualMachineSpec `yaml:"spec"` +} + +type Metadata struct { + Name string `yaml:"name"` + Labels map[string]string `yaml:"labels"` +} + +func GetFirmwareFromYaml(yamlData []byte) (string, error) { + var vm VirtualMachine + if err := yaml.Unmarshal(yamlData, &vm); err != nil { + return "", err + } + + firmware := vm.Spec.Template.Spec.Domain.OS.Firmware + if firmware == "" { + log.Info("Firmware type was not detected") + } + + return firmware, nil +} + +func GetOperationSystemFromYaml(yamlData []byte) (os string, err error) { + var vm VirtualMachine + if err = yaml.Unmarshal(yamlData, &vm); err != nil { + return + } + + labels := vm.Metadata.Labels + if osinfo, ok := labels["libguestfs.org/osinfo"]; ok { + return mapOs(osinfo), nil + + } + return +} + +func mapOs(labelOS string) (os string) { + distro := strings.SplitN(labelOS, ".", 2)[0] + + switch { + case strings.HasPrefix(distro, "rocky"): + distro = "rocky" + case strings.HasPrefix(distro, "opensuse"): + distro = "opensuse" + case strings.HasPrefix(distro, "ubuntu"): + distro = "ubuntu" + case strings.HasPrefix(distro, "fedora"): + distro = "fedora" + } + + os, ok := osV2VMap[distro] + if !ok { + log.Info(fmt.Sprintf("Received %s, mapped to: %s", labelOS, os)) + os = "otherGuest64" + } + return +} diff --git a/virt-v2v/cold/BUILD.bazel b/virt-v2v/cold/BUILD.bazel index 033594f37..814ffa14b 100644 --- a/virt-v2v/cold/BUILD.bazel +++ b/virt-v2v/cold/BUILD.bazel @@ -218,7 +218,7 @@ rpmtree( "@groff-base-0__1.22.4-10.el9.x86_64//rpm", "@gsettings-desktop-schemas-0__40.0-6.el9.x86_64//rpm", "@gssproxy-0__0.8.4-7.el9.x86_64//rpm", - "@guestfs-tools-0__1.51.6-3.el9.x86_64//rpm", + "@guestfs-tools-0__1.51.6-5.el9.x86_64//rpm", "@gzip-0__1.12-1.el9.x86_64//rpm", "@hexedit-0__1.6-1.el9.x86_64//rpm", "@hivex-libs-0__1.3.21-3.el9.x86_64//rpm", @@ -234,8 +234,8 @@ rpmtree( "@kbd-0__2.4.0-10.el9.x86_64//rpm", "@kbd-legacy-0__2.4.0-10.el9.x86_64//rpm", "@kbd-misc-0__2.4.0-10.el9.x86_64//rpm", - "@kernel-core-0__5.14.0-503.el9.x86_64//rpm", - "@kernel-modules-core-0__5.14.0-503.el9.x86_64//rpm", + "@kernel-core-0__5.14.0-500.el9.x86_64//rpm", + "@kernel-modules-core-0__5.14.0-500.el9.x86_64//rpm", "@kernel-srpm-macros-0__1.0-13.el9.x86_64//rpm", "@keyutils-0__1.6.3-1.el9.x86_64//rpm", "@keyutils-libs-0__1.6.3-1.el9.x86_64//rpm", @@ -448,9 +448,9 @@ rpmtree( "@psmisc-0__23.4-3.el9.x86_64//rpm", "@publicsuffix-list-dafsa-0__20210518-3.el9.x86_64//rpm", "@pyproject-srpm-macros-0__1.12.0-1.el9.x86_64//rpm", - "@qemu-img-17__9.0.0-8.el9.x86_64//rpm", - "@qemu-kvm-common-17__9.0.0-8.el9.x86_64//rpm", - "@qemu-kvm-core-17__9.0.0-8.el9.x86_64//rpm", + "@qemu-img-17__9.0.0-9.el9.x86_64//rpm", + "@qemu-kvm-common-17__9.0.0-9.el9.x86_64//rpm", + "@qemu-kvm-core-17__9.0.0-9.el9.x86_64//rpm", "@qt5-srpm-macros-0__5.15.9-1.el9.x86_64//rpm", "@quota-1__4.09-2.el9.x86_64//rpm", "@quota-nls-1__4.09-2.el9.x86_64//rpm", @@ -489,7 +489,7 @@ rpmtree( "@util-linux-0__2.37.4-20.el9.x86_64//rpm", "@util-linux-core-0__2.37.4-20.el9.x86_64//rpm", "@vim-minimal-2__8.2.2637-21.el9.x86_64//rpm", - "@virt-v2v-1__2.5.6-3.el9.x86_64//rpm", + "@virt-v2v-1__2.5.6-4.el9.x86_64//rpm", "@virtio-win-0__1.9.15-4.el9.x86_64//rpm", "@which-0__2.21-29.el9.x86_64//rpm", "@xfsprogs-0__6.4.0-4.el9.x86_64//rpm", diff --git a/virt-v2v/cold/WORKSPACE b/virt-v2v/cold/WORKSPACE index e96bf6dc0..0b7e18fba 100644 --- a/virt-v2v/cold/WORKSPACE +++ b/virt-v2v/cold/WORKSPACE @@ -755,10 +755,10 @@ rpm( ) rpm( - name = "guestfs-tools-0__1.51.6-3.el9.x86_64", - sha256 = "6c60f887151fbe60372898f0b25509806136f7e1e4becf4b99b899aa1b5c0734", + name = "guestfs-tools-0__1.51.6-5.el9.x86_64", + sha256 = "969e43c14a60825bf95be175506bbbd68b2d89e484368f54784eb00da276ccc6", urls = [ - "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/guestfs-tools-1.51.6-3.el9.x86_64.rpm", + "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/guestfs-tools-1.51.6-5.el9.x86_64.rpm", ], ) @@ -883,18 +883,18 @@ rpm( ) rpm( - name = "kernel-core-0__5.14.0-503.el9.x86_64", - sha256 = "c86fb50191f3048ae76d5fcd62a7fc6c93d63640fe100e8d67b0511c961b34f5", + name = "kernel-core-0__5.14.0-500.el9.x86_64", + sha256 = "cb2e86056f8b891685c07035822f98d64b85c3475484e974507f3701d7efb88d", urls = [ - "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/BaseOS/x86_64/os/Packages/kernel-core-5.14.0-503.el9.x86_64.rpm", + "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/BaseOS/x86_64/os/Packages/kernel-core-5.14.0-500.el9.x86_64.rpm", ], ) rpm( - name = "kernel-modules-core-0__5.14.0-503.el9.x86_64", - sha256 = "f78ca958af67a106068569661e6f8401548a93f583d37c5d75844fc4e199e0fe", + name = "kernel-modules-core-0__5.14.0-500.el9.x86_64", + sha256 = "423123b2ee77bab95ca82ce380a3f1badc23ee079e17011566cf39c5d28194c1", urls = [ - "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/BaseOS/x86_64/os/Packages/kernel-modules-core-5.14.0-503.el9.x86_64.rpm", + "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/BaseOS/x86_64/os/Packages/kernel-modules-core-5.14.0-500.el9.x86_64.rpm", ], ) @@ -2595,26 +2595,26 @@ rpm( ) rpm( - name = "qemu-img-17__9.0.0-8.el9.x86_64", - sha256 = "fc519f57f16c15acc83acf771218c3382a02632c3f0ec1356f45d888bce9e89b", + name = "qemu-img-17__9.0.0-9.el9.x86_64", + sha256 = "2bf775ba6b143e12ced2aa33a62da5e0a3c4800263b8a918f3ffb483da4b8399", urls = [ - "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/qemu-img-9.0.0-8.el9.x86_64.rpm", + "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/qemu-img-9.0.0-9.el9.x86_64.rpm", ], ) rpm( - name = "qemu-kvm-common-17__9.0.0-8.el9.x86_64", - sha256 = "31128c38d0d9f410b6ac825ae4cd29d7e41c9b69367e48a48429bec7eb6e2dc2", + name = "qemu-kvm-common-17__9.0.0-9.el9.x86_64", + sha256 = "2143b7c3052baae9dbac1edf4aa1dc998ae8de68fbf7b249e3bbdf34ac1bf134", urls = [ - "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/qemu-kvm-common-9.0.0-8.el9.x86_64.rpm", + "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/qemu-kvm-common-9.0.0-9.el9.x86_64.rpm", ], ) rpm( - name = "qemu-kvm-core-17__9.0.0-8.el9.x86_64", - sha256 = "27c203bc3f8d0abc965d92b4f9859ca074be3fe18ab49210798b6be2164c4f9d", + name = "qemu-kvm-core-17__9.0.0-9.el9.x86_64", + sha256 = "ddd66bc0869a1d089514d7a80b07b805b69c7731552c12f061f58b9a5dfeaaaf", urls = [ - "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/qemu-kvm-core-9.0.0-8.el9.x86_64.rpm", + "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/qemu-kvm-core-9.0.0-9.el9.x86_64.rpm", ], ) @@ -2923,10 +2923,10 @@ rpm( ) rpm( - name = "virt-v2v-1__2.5.6-3.el9.x86_64", - sha256 = "1f08fe066958e10ea81ab4d10d093a74af8343e3d7a79b16addff81f2cfba599", + name = "virt-v2v-1__2.5.6-4.el9.x86_64", + sha256 = "8e2a23b3f72fc9c6c2f6f2730855acccb63b6a50f1bb5872e406ffc602b48214", urls = [ - "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/virt-v2v-2.5.6-3.el9.x86_64.rpm", + "https://composes.stream.centos.org/development/latest-CentOS-Stream/compose/AppStream/x86_64/os/Packages/virt-v2v-2.5.6-4.el9.x86_64.rpm", ], ) diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index f94b84e41..752749104 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -25,13 +25,15 @@ const ( ) var ( - xmlFilePath string - server *http.Server + yamlFilePath string + server *http.Server ) const LETTERS = "abcdefghijklmnopqrstuvwxyz" const LETTERS_LENGTH = len(LETTERS) +var nameChanged bool + func main() { source := os.Getenv("V2V_source") if source == vSphere { @@ -58,18 +60,18 @@ func main() { } var err error - xmlFilePath, err = getXMLFile(DIR, "xml") + yamlFilePath, err = getYamlFile(DIR, "yaml") if err != nil { - fmt.Println("Error getting XML file:", err) + fmt.Println("Error getting YAML file:", err) os.Exit(1) } - err = customizeVM(source, xmlFilePath) + err = customizeVM(source, yamlFilePath) if err != nil { fmt.Println("Warning customizing the VM failed:", err) } - http.HandleFunc("/ovf", ovfHandler) + http.HandleFunc("/vm", vmHandler) http.HandleFunc("/shutdown", shutdownHandler) server = &http.Server{Addr: ":8080"} @@ -80,18 +82,8 @@ func main() { } } -func getVmDiskPaths(domain *OvaVmconfig) []string { - var resp []string - for _, disk := range domain.Devices.Disks { - if disk.Source.File != "" { - resp = append(resp, disk.Source.File) - } - } - return resp -} - -func customizeVM(source string, xmlFilePath string) error { - domain, err := GetDomainFromXml(xmlFilePath) +func customizeVM(source string, yamlFilePath string) error { + vmConfig, err := GetVmConfigYaml(yamlFilePath) if err != nil { fmt.Printf("Error mapping xml to domain: %v\n", err) @@ -100,7 +92,7 @@ func customizeVM(source string, xmlFilePath string) error { } // Get operating system. - operatingSystem := domain.Metadata.LibOsInfo.V2VOS.ID + operatingSystem := vmConfig.OSInfo if operatingSystem == "" { fmt.Printf("Warning: no operating system found") @@ -110,15 +102,13 @@ func customizeVM(source string, xmlFilePath string) error { fmt.Printf("Operating System ID: %s\n", operatingSystem) } - // Get domain disks. - disks := getVmDiskPaths(domain) - if len(disks) == 0 { + if len(vmConfig.DiskPaths) == 0 { fmt.Printf("Warning: no V2V domain disks found") // No customization when no disks found. return nil } else { - fmt.Printf("V2V domain disks: %v\n", disks) + fmt.Printf("V2V domain disks: %v\n", vmConfig.DiskPaths) } // Customization for vSphere source. @@ -127,7 +117,7 @@ func customizeVM(source string, xmlFilePath string) error { if strings.Contains(operatingSystem, "win") { t := EmbedTool{filesystem: &scriptFS} - err = CustomizeWindows(disks, DIR, &t) + err = CustomizeWindows(vmConfig.DiskPaths, DIR, &t) if err != nil { fmt.Println("Error customizing disk image:", err) return err @@ -138,7 +128,7 @@ func customizeVM(source string, xmlFilePath string) error { if !strings.Contains(operatingSystem, "win") { t := EmbedTool{filesystem: &scriptFS} - err = CustomizeLinux(CustomizeDomainExec, disks, DIR, &t) + err = CustomizeLinux(CustomizeDomainExec, vmConfig.DiskPaths, DIR, &t) if err != nil { fmt.Println("Error customizing disk image:", err) return err @@ -189,7 +179,16 @@ func buildCommand() []string { fmt.Println("Error creating directory ", err) os.Exit(1) } - virtV2vArgs = append(virtV2vArgs, "-o", "local", "-os", DIR) + virtV2vArgs = append(virtV2vArgs, "-o", "kubevirt") + + // When converting VM with name that do not meet DNS1123 RFC requirements, + // it should be changed to supported one to ensure the conversion does not fail. + if checkEnvVariablesSet("V2V_NewName") { + virtV2vArgs = append(virtV2vArgs, "-on", os.Getenv("V2V_NewName")) + nameChanged = true + } + + virtV2vArgs = append(virtV2vArgs, "-os", DIR) //Disks on filesystem storage. if err := LinkDisks(FS, 15); err != nil { @@ -308,13 +307,20 @@ func LinkDisks(diskKind string, num int) (err error) { return } + var diskSuffix string + if nameChanged { + diskSuffix = os.Getenv("V2V_newName") + } else { + diskSuffix = os.Getenv("V2V_vmName") + } + for _, disk := range disks { diskNum, err := strconv.Atoi(disk[num:]) if err != nil { fmt.Println("Error getting disks names ", err) return err } - diskLink := fmt.Sprintf("%s/%s-sd%s", DIR, os.Getenv("V2V_vmName"), genName(diskNum+1)) + diskLink := fmt.Sprintf("%s/%s-sd%s", DIR, diskSuffix, genName(diskNum+1)) diskImgPath := disk if diskKind == FS { diskImgPath = fmt.Sprintf("%s/disk.img", disk) @@ -361,7 +367,7 @@ func executeVirtV2v(args []string) error { return nil } -func getXMLFile(dir, fileExtension string) (string, error) { +func getYamlFile(dir, fileExtension string) (string, error) { files, err := filepath.Glob(filepath.Join(dir, "*."+fileExtension)) if err != nil { return "", err @@ -369,26 +375,29 @@ func getXMLFile(dir, fileExtension string) (string, error) { if len(files) > 0 { return files[0], nil } - return "", fmt.Errorf("XML file was not found") + return "", fmt.Errorf("yaml file was not found") } -func ovfHandler(w http.ResponseWriter, r *http.Request) { - xmlData, err := ReadXMLFile(xmlFilePath) +func vmHandler(w http.ResponseWriter, r *http.Request) { + if yamlFilePath == "" { + fmt.Println("Error: YAML file path is empty.") + http.Error(w, "YAML file path is empty", http.StatusInternalServerError) + return + } + + yamlData, err := os.ReadFile(yamlFilePath) if err != nil { - fmt.Printf("Error: %v\n", err) - http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Printf("Error reading yaml file: %v\n", err) + http.Error(w, "Error reading Yaml file", http.StatusInternalServerError) return } - w.Header().Set("Content-Type", "application/xml") - _, err = w.Write(xmlData) - if err == nil { - w.WriteHeader(http.StatusOK) - } else { + w.Header().Set("Content-Type", "text/yaml") + _, err = w.Write(yamlData) + if err != nil { fmt.Printf("Error writing response: %v\n", err) http.Error(w, "Error writing response", http.StatusInternalServerError) } - } func shutdownHandler(w http.ResponseWriter, r *http.Request) { diff --git a/virt-v2v/cold/xml-reader.go b/virt-v2v/cold/xml-reader.go deleted file mode 100644 index 07aadc656..000000000 --- a/virt-v2v/cold/xml-reader.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "encoding/xml" - "fmt" - "os" -) - -type OvaVmconfig struct { - XMLName xml.Name `xml:"domain"` - Name string `xml:"name"` - OS OS `xml:"os"` - Metadata Metadata `xml:"metadata"` - Devices Devices `xml:"devices"` -} - -type OS struct { - Type OSType `xml:"type"` - Loader Loader `xml:"loader"` - Nvram Nvram `xml:"nvram"` -} - -type Metadata struct { - LibOsInfo LibOsInfo `xml:"libosinfo"` -} - -type Devices struct { - Disks []Disk `xml:"disk"` -} -type Disk struct { - Source Source `xml:"source"` -} -type Source struct { - File string `xml:"file,attr"` -} - -type LibOsInfo struct { - V2VOS V2VOS `xml:"os"` -} - -type V2VOS struct { - ID string `xml:"id,attr"` -} - -type OSType struct { - Arch string `xml:"arch,attr"` - Machine string `xml:"machine,attr"` - Content string `xml:",chardata"` -} - -type Loader struct { - Readonly string `xml:"readonly,attr"` - Type string `xml:"type,attr"` - Secure string `xml:"secure,attr"` - Path string `xml:",chardata"` -} - -type Nvram struct { - Template string `xml:"template,attr"` -} - -// ReadXMLFile reads the content of an XML []byte from the given file path. -// -// Arguments: -// - filePath (string): The path to the XML file. -// -// Returns: -// - []byte: The content of the XML file. -// - error: An error if the file cannot be read, or nil if successful. -func ReadXMLFile(filePath string) ([]byte, error) { - if filePath == "" { - return nil, fmt.Errorf("XML file path is empty") - } - - xmlData, err := os.ReadFile(filePath) - if err != nil { - return nil, fmt.Errorf("error reading XML file: %w", err) - } - - return xmlData, nil -} - -func GetDomainFromXml(xmlFilePath string) (*OvaVmconfig, error) { - xmlData, err := ReadXMLFile(xmlFilePath) - if err != nil { - fmt.Printf("Error read XML: %v\n", err) - return nil, err - } - - var xmlConf OvaVmconfig - err = xml.Unmarshal(xmlData, &xmlConf) - if err != nil { - fmt.Printf("Error unmarshalling XML: %v\n", err) - return nil, err - } - return &xmlConf, nil -} diff --git a/virt-v2v/cold/yaml-reader.go b/virt-v2v/cold/yaml-reader.go new file mode 100644 index 000000000..4bc27cae7 --- /dev/null +++ b/virt-v2v/cold/yaml-reader.go @@ -0,0 +1,73 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +type VmConfig struct { + OSInfo string + DiskPaths []string +} + +// ReadYAMLFile reads the YAML file and extracts the HostDisk paths and osinfo label. +func GetVmConfigYaml(filePath string) (*VmConfig, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, fmt.Errorf("error opening YAML file: %w", err) + } + defer file.Close() + + config := &VmConfig{} + scanner := bufio.NewScanner(file) + var inLabels, inVolumes bool + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + if strings.HasPrefix(line, "labels:") { + inLabels = true + continue + } + + if inLabels { + if strings.HasPrefix(line, "libguestfs.org/osinfo:") { + config.OSInfo = strings.TrimSpace(strings.TrimPrefix(line, "libguestfs.org/osinfo:")) + } + if !strings.HasPrefix(line, "libguestfs.org/") { + inLabels = false + } + } + + if strings.Contains(line, "volumes:") { + inVolumes = true + continue + } + if inVolumes { + if strings.Contains(line, "hostDisk:") { + scanner.Scan() + pathLine := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(pathLine, "path:") { + pathValue := strings.TrimSpace(strings.TrimPrefix(pathLine, "path:")) + if pathValue != "" { + config.DiskPaths = append(config.DiskPaths, pathValue) + } + } + } + if strings.Contains(line, "- name:") { + inVolumes = false + } + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading YAML file: %w", err) + } + + return config, nil +}