From 11ebad9be0f41561a932de68ea09832620ab0bf4 Mon Sep 17 00:00:00 2001 From: Martin Necas Date: Mon, 9 Sep 2024 16:24:25 +0200 Subject: [PATCH] Add GetVmFromConfigYaml Signed-off-by: Martin Necas --- go.mod | 2 +- pkg/controller/plan/BUILD.bazel | 1 - pkg/controller/plan/adapter/ova/BUILD.bazel | 2 + pkg/controller/plan/kubevirt.go | 13 ++- pkg/controller/plan/util/BUILD.bazel | 5 +- pkg/controller/plan/util/kubevirtvmparser.go | 91 ++++-------------- .../plan/util/kubevirtvmparser_test.go | 31 ++++++ .../plan/util/testdata/new_format_bios.yml | 38 ++++++++ .../plan/util/testdata/new_format_efi.yml | 39 ++++++++ .../plan/util/testdata/old_format_bios.yml | 38 ++++++++ .../plan/util/testdata/old_format_efi.yml | 38 ++++++++ virt-v2v/cmd/entrypoint.go | 8 +- .../pkg/customize/testdata/valid_config.yml | 94 ++++++++----------- virt-v2v/pkg/server/server.go | 27 +++--- virt-v2v/pkg/utils/command.go | 11 ++- virt-v2v/pkg/utils/xml-reader.go | 23 +---- 16 files changed, 285 insertions(+), 176 deletions(-) create mode 100644 pkg/controller/plan/util/kubevirtvmparser_test.go create mode 100644 pkg/controller/plan/util/testdata/new_format_bios.yml create mode 100644 pkg/controller/plan/util/testdata/new_format_efi.yml create mode 100644 pkg/controller/plan/util/testdata/old_format_bios.yml create mode 100644 pkg/controller/plan/util/testdata/old_format_efi.yml diff --git a/go.mod b/go.mod index a3e0ee1bc..43f70d179 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( kubevirt.io/containerized-data-importer-api v1.59.0 libvirt.org/libvirt-go-xml v7.4.0+incompatible sigs.k8s.io/controller-runtime v0.16.3 + sigs.k8s.io/yaml v1.3.0 ) require ( @@ -115,7 +116,6 @@ require ( kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) replace github.com/gophercloud/gophercloud => github.com/kubev2v/gophercloud v0.0.0-20230629135522-9d701a75c760 diff --git a/pkg/controller/plan/BUILD.bazel b/pkg/controller/plan/BUILD.bazel index c7a3fbef0..ce4fc4458 100644 --- a/pkg/controller/plan/BUILD.bazel +++ b/pkg/controller/plan/BUILD.bazel @@ -22,7 +22,6 @@ go_library( "//pkg/controller/base", "//pkg/controller/plan/adapter", "//pkg/controller/plan/adapter/base", - "//pkg/controller/plan/adapter/ova", "//pkg/controller/plan/adapter/vsphere", "//pkg/controller/plan/context", "//pkg/controller/plan/handler", diff --git a/pkg/controller/plan/adapter/ova/BUILD.bazel b/pkg/controller/plan/adapter/ova/BUILD.bazel index 0d5368639..7e226422e 100644 --- a/pkg/controller/plan/adapter/ova/BUILD.bazel +++ b/pkg/controller/plan/adapter/ova/BUILD.bazel @@ -7,6 +7,7 @@ go_library( "builder.go", "client.go", "destinationclient.go", + "ovfparser.go", "validator.go", ], importpath = "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/ova", @@ -26,6 +27,7 @@ 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/kubevirt.go b/pkg/controller/plan/kubevirt.go index 163741bc0..46cc4d1e4 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -982,18 +982,18 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s case api.Ova: vmConf, err := io.ReadAll(resp.Body) if err != nil { - return err + return liberr.Wrap(err) } if vm.Firmware, err = util.GetFirmwareFromYaml(vmConf); err != nil { - return err + return liberr.Wrap(err) } case api.VSphere: inspectionXML, err := r.getInspectionXml(pod) if err != nil { - return err + return liberr.Wrap(err) } if vm.OperatingSystem, err = inspectionparser.GetOperationSystemFromConfig(inspectionXML); err != nil { - return err + return liberr.Wrap(err) } r.Log.Info("Setting the vm OS ", vm.OperatingSystem, "vmId", vm.ID) } @@ -1800,9 +1800,8 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, InitContainers: initContainers, Containers: []core.Container{ { - ImagePullPolicy: core.PullAlways, - Name: "virt-v2v", - Env: environment, + Name: "virt-v2v", + Env: environment, EnvFrom: []core.EnvFromSource{ { Prefix: "V2V_", diff --git a/pkg/controller/plan/util/BUILD.bazel b/pkg/controller/plan/util/BUILD.bazel index dff289312..ca7e8f33e 100644 --- a/pkg/controller/plan/util/BUILD.bazel +++ b/pkg/controller/plan/util/BUILD.bazel @@ -16,18 +16,21 @@ go_library( "//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", + "//vendor/kubevirt.io/api/core/v1:core", + "//vendor/sigs.k8s.io/yaml", ], ) go_test( name = "util_test", srcs = [ + "kubevirtvmparser_test.go", "util_suite_test.go", "utils_test.go", ], + data = glob(["testdata/**"]), embed = [":util"], deps = [ "//vendor/github.com/onsi/ginkgo/v2:ginkgo", diff --git a/pkg/controller/plan/util/kubevirtvmparser.go b/pkg/controller/plan/util/kubevirtvmparser.go index 37ab5d95e..b106ef11d 100644 --- a/pkg/controller/plan/util/kubevirtvmparser.go +++ b/pkg/controller/plan/util/kubevirtvmparser.go @@ -1,11 +1,10 @@ package util import ( - "fmt" - "strings" + cnv "kubevirt.io/api/core/v1" + "sigs.k8s.io/yaml" "github.com/konveyor/forklift-controller/pkg/lib/logging" - "gopkg.in/yaml.v2" ) const ( @@ -16,42 +15,6 @@ const ( // 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"` } @@ -91,45 +54,25 @@ func GetFirmwareFromYaml(yamlData []byte) (string, error) { } firmware := vm.Spec.Template.Spec.Domain.OS.Firmware - if firmware == "" { - log.Info("Firmware type was not detected") + if firmware != "" { + return firmware, nil } - return firmware, nil -} - -func GetOperationSystemFromYaml(yamlData []byte) (os string, err error) { - var vm VirtualMachine - if err = yaml.Unmarshal(yamlData, &vm); err != nil { - return + // FIXME: In newer version of virt-v2v the output will change to support CNV VM format. + // With this we will support both so the migrations should not fail during the update to newer virt-v2v. + // But we still need to remove the custom templating. + // https://issues.redhat.com/browse/RHEL-58065 + var cnvVm *cnv.VirtualMachine + if err := yaml.Unmarshal(yamlData, &cnvVm); err != nil { + return "", err } - - labels := vm.Metadata.Labels - if osinfo, ok := labels["libguestfs.org/osinfo"]; ok { - return mapOs(osinfo), nil - + if cnvVm.Spec.Template.Spec.Domain.Firmware.Bootloader.BIOS != nil { + return "bios", 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" + if cnvVm.Spec.Template.Spec.Domain.Firmware.Bootloader.EFI != nil { + return "uefi", nil } - os, ok := osV2VMap[distro] - if !ok { - log.Info(fmt.Sprintf("Received %s, mapped to: %s", labelOS, os)) - os = "otherGuest64" - } - return + log.Info("Firmware type was not detected") + return "", nil } diff --git a/pkg/controller/plan/util/kubevirtvmparser_test.go b/pkg/controller/plan/util/kubevirtvmparser_test.go new file mode 100644 index 000000000..3819b4c3c --- /dev/null +++ b/pkg/controller/plan/util/kubevirtvmparser_test.go @@ -0,0 +1,31 @@ +package util + +import ( + "fmt" + "os" + "path/filepath" + "testing" +) + +func TestKubevirtVmParser(t *testing.T) { + fmt.Println("test") + testFile(t, "new_format_bios.yml", "bios") + testFile(t, "new_format_efi.yml", "uefi") + testFile(t, "old_format_bios.yml", "bios") + testFile(t, "old_format_efi.yml", "uefi") +} + +func testFile(t *testing.T, filename, expectedFormat string) { + data, err := os.ReadFile(filepath.Join("testdata", filename)) + if err != nil { + fmt.Println(err) + } + firmware, err := GetFirmwareFromYaml(data) + if err != nil { + fmt.Println(err) + } + fmt.Println(firmware) + if firmware != expectedFormat { + t.Fatalf("Failed to parse '%s' from file '%s'", expectedFormat, filename) + } +} diff --git a/pkg/controller/plan/util/testdata/new_format_bios.yml b/pkg/controller/plan/util/testdata/new_format_bios.yml new file mode 100644 index 000000000..bf1db743f --- /dev/null +++ b/pkg/controller/plan/util/testdata/new_format_bios.yml @@ -0,0 +1,38 @@ +--- +# generated by virt-v2v 2.5.8fedora=40,release=1.fc40 +apiVersion: kubevirt.io/v1 +kind: VirtualMachine +metadata: + name: mnecas-win-2022 + labels: + libguestfs.org/virt-v2v-version: "2.5.8" + libguestfs.org/genid: f2fc75e8-4961-1425-4740-fcf2bc09685a + libguestfs.org/osinfo: win2k22 + libguestfs.org/source: vmware +spec: + template: + spec: + domain: + firmware: + bootloader: + bios: {} + resources: + clock: + timer: + hpet: + present: false + hyperv: {} + pit: + tickPolicy: delay + rtc: + tickPolicy: catchup + utc: {} + requests: + memory: 8192Mi + features: + cpu: + sockets: 2 + cores: 2 + thread: 1 + terminationGracePeriodSeconds: 0 + diff --git a/pkg/controller/plan/util/testdata/new_format_efi.yml b/pkg/controller/plan/util/testdata/new_format_efi.yml new file mode 100644 index 000000000..76746aa05 --- /dev/null +++ b/pkg/controller/plan/util/testdata/new_format_efi.yml @@ -0,0 +1,39 @@ +--- +# generated by virt-v2v 2.5.8fedora=40,release=1.fc40 +apiVersion: kubevirt.io/v1 +kind: VirtualMachine +metadata: + name: mnecas-win-2022 + labels: + libguestfs.org/virt-v2v-version: "2.5.8" + libguestfs.org/genid: f2fc75e8-4961-1425-4740-fcf2bc09685a + libguestfs.org/osinfo: win2k22 + libguestfs.org/source: vmware +spec: + template: + spec: + domain: + firmware: + bootloader: + efi: + persistent: true + resources: + clock: + timer: + hpet: + present: false + hyperv: {} + pit: + tickPolicy: delay + rtc: + tickPolicy: catchup + utc: {} + requests: + memory: 8192Mi + features: + cpu: + sockets: 2 + cores: 2 + thread: 1 + terminationGracePeriodSeconds: 0 + diff --git a/pkg/controller/plan/util/testdata/old_format_bios.yml b/pkg/controller/plan/util/testdata/old_format_bios.yml new file mode 100644 index 000000000..2eac58e25 --- /dev/null +++ b/pkg/controller/plan/util/testdata/old_format_bios.yml @@ -0,0 +1,38 @@ +--- +# generated by virt-v2v 2.5.8fedora=40,release=1.fc40 +apiVersion: kubevirt.io/v1 +kind: VirtualMachine +metadata: + name: mnecas-win-2022 + labels: + libguestfs.org/virt-v2v-version: "2.5.8" + libguestfs.org/genid: 4f3c8a7c-3464-c9b0-cf79-08325a6bf1e8 + libguestfs.org/osinfo: win2k22 + libguestfs.org/source: kvm +spec: + template: + spec: + domain: + os: + firmware: bios + resources: + clock: + timer: + hpet: + present: false + hyperv: {} + pit: + tickPolicy: delay + rtc: + tickPolicy: catchup + utc: {} + requests: + memory: 8192Mi + features: + acpi: {} + apic: {} + cpu: + sockets: 2 + cores: 2 + thread: 1 + terminationGracePeriodSeconds: 0 diff --git a/pkg/controller/plan/util/testdata/old_format_efi.yml b/pkg/controller/plan/util/testdata/old_format_efi.yml new file mode 100644 index 000000000..3a60de620 --- /dev/null +++ b/pkg/controller/plan/util/testdata/old_format_efi.yml @@ -0,0 +1,38 @@ +--- +# generated by virt-v2v 2.5.8fedora=40,release=1.fc40 +apiVersion: kubevirt.io/v1 +kind: VirtualMachine +metadata: + name: mnecas-win-2022 + labels: + libguestfs.org/virt-v2v-version: "2.5.8" + libguestfs.org/genid: 4f3c8a7c-3464-c9b0-cf79-08325a6bf1e8 + libguestfs.org/osinfo: win2k22 + libguestfs.org/source: kvm +spec: + template: + spec: + domain: + os: + firmware: uefi + resources: + clock: + timer: + hpet: + present: false + hyperv: {} + pit: + tickPolicy: delay + rtc: + tickPolicy: catchup + utc: {} + requests: + memory: 8192Mi + features: + acpi: {} + apic: {} + cpu: + sockets: 2 + cores: 2 + thread: 1 + terminationGracePeriodSeconds: 0 diff --git a/virt-v2v/cmd/entrypoint.go b/virt-v2v/cmd/entrypoint.go index 11eb4f746..bbe893399 100644 --- a/virt-v2v/cmd/entrypoint.go +++ b/virt-v2v/cmd/entrypoint.go @@ -108,7 +108,7 @@ func virtV2vBuildCommand() (args []string, err error) { } return nil, fmt.Errorf("virt-v2v supports the following providers: {%v}. Provided: %s\n", strings.Join(providers, ", "), source) } - args = append(args, "-o", "local", "-os", global.DIR) + args = append(args, "-o", "kubevirt", "-os", global.DIR) switch source { case global.VSPHERE: @@ -121,6 +121,12 @@ func virtV2vBuildCommand() (args []string, err error) { args = append(args, "-i", "ova", os.Getenv("V2V_diskPath")) } + // 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 utils.CheckEnvVariablesSet("V2V_NewName") { + args = append(args, "-on", os.Getenv("V2V_NewName")) + } + return args, nil } diff --git a/virt-v2v/pkg/customize/testdata/valid_config.yml b/virt-v2v/pkg/customize/testdata/valid_config.yml index f6e0837c5..3a60de620 100644 --- a/virt-v2v/pkg/customize/testdata/valid_config.yml +++ b/virt-v2v/pkg/customize/testdata/valid_config.yml @@ -1,56 +1,38 @@ - - - - yzamir-03 - - - - - - 2097152 - 2097152 - 1 - - - - - - - hvm - - - destroy - restart - restart - - - - - - - - - - - - - - - /dev/urandom - - - -
- - - - - - - - - - - +--- +# generated by virt-v2v 2.5.8fedora=40,release=1.fc40 +apiVersion: kubevirt.io/v1 +kind: VirtualMachine +metadata: + name: mnecas-win-2022 + labels: + libguestfs.org/virt-v2v-version: "2.5.8" + libguestfs.org/genid: 4f3c8a7c-3464-c9b0-cf79-08325a6bf1e8 + libguestfs.org/osinfo: win2k22 + libguestfs.org/source: kvm +spec: + template: + spec: + domain: + os: + firmware: uefi + resources: + clock: + timer: + hpet: + present: false + hyperv: {} + pit: + tickPolicy: delay + rtc: + tickPolicy: catchup + utc: {} + requests: + memory: 8192Mi + features: + acpi: {} + apic: {} + cpu: + sockets: 2 + cores: 2 + thread: 1 + terminationGracePeriodSeconds: 0 diff --git a/virt-v2v/pkg/server/server.go b/virt-v2v/pkg/server/server.go index e66edfe47..23cdbbde2 100644 --- a/virt-v2v/pkg/server/server.go +++ b/virt-v2v/pkg/server/server.go @@ -21,7 +21,7 @@ var ( // This information is later used in the vm creation step such as the firmware for the OVA or // Operating System for the VM creation. func Start() error { - http.HandleFunc("/ovf", ovfHandler) + http.HandleFunc("/vm", vmHandler) http.HandleFunc("/inspection", inspectorHandler) http.HandleFunc("/shutdown", shutdownHandler) server = &http.Server{Addr: ":8080"} @@ -34,20 +34,25 @@ func Start() error { return nil } -func ovfHandler(w http.ResponseWriter, r *http.Request) { - xmlFilePath, err := GetDomainFile(global.DIR, "xml") +func vmHandler(w http.ResponseWriter, r *http.Request) { + yamlFilePath, err := GetVmYamlFile(global.DIR) + if yamlFilePath == "" { + fmt.Println("Error: YAML file path is empty.") + http.Error(w, "YAML file path is empty", http.StatusInternalServerError) + return + } if err != nil { fmt.Println("Error getting XML file:", err) } - xmlData, err := utils.ReadXMLFile(xmlFilePath) + 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) + w.Header().Set("Content-Type", "text/yaml") + _, err = w.Write(yamlData) if err == nil { w.WriteHeader(http.StatusOK) } else { @@ -57,7 +62,7 @@ func ovfHandler(w http.ResponseWriter, r *http.Request) { } func inspectorHandler(w http.ResponseWriter, r *http.Request) { - xmlData, err := utils.ReadXMLFile(global.INSPECTION) + xmlData, err := os.ReadFile(global.INSPECTION) if err != nil { fmt.Printf("Error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -82,8 +87,8 @@ func shutdownHandler(w http.ResponseWriter, r *http.Request) { } } -func GetDomainFile(dir, fileExtension string) (string, error) { - files, err := filepath.Glob(filepath.Join(dir, fmt.Sprintf("%s.%s", os.Getenv("V2V_vmName"), fileExtension))) +func GetVmYamlFile(dir string) (string, error) { + files, err := filepath.Glob(filepath.Join(dir, fmt.Sprintf("%s.%s", utils.GetDiskName(), "yaml"))) if err != nil { return "", err } diff --git a/virt-v2v/pkg/utils/command.go b/virt-v2v/pkg/utils/command.go index 2b40c01be..41cf7c45b 100644 --- a/virt-v2v/pkg/utils/command.go +++ b/virt-v2v/pkg/utils/command.go @@ -101,6 +101,13 @@ func getDiskNumber(kind global.MountPath, disk string) (int, error) { } } +func GetDiskName() string { + if name := os.Getenv("V2V_newName"); name != "" { + return name + } + return os.Getenv("V2V_vmName") +} + func getDiskLink(kind global.MountPath, disk string) (string, error) { diskNum, err := getDiskNumber(kind, disk) if err != nil { @@ -109,7 +116,7 @@ func getDiskLink(kind global.MountPath, disk string) (string, error) { } return filepath.Join( global.DIR, - fmt.Sprintf("%s-sd%s", os.Getenv("V2V_vmName"), genName(diskNum+1)), + fmt.Sprintf("%s-sd%s", GetDiskName(), genName(diskNum+1)), ), nil } @@ -117,7 +124,7 @@ func GetLinkedDisks() ([]string, error) { disks, err := filepath.Glob( filepath.Join( global.DIR, - fmt.Sprintf("%s-sd*", os.Getenv("V2V_vmName")), + fmt.Sprintf("%s-sd*", GetDiskName()), ), ) if err != nil { diff --git a/virt-v2v/pkg/utils/xml-reader.go b/virt-v2v/pkg/utils/xml-reader.go index 634422a55..694e976e2 100644 --- a/virt-v2v/pkg/utils/xml-reader.go +++ b/virt-v2v/pkg/utils/xml-reader.go @@ -18,7 +18,7 @@ type InspectionV2V struct { } func GetInspectionV2vFromFile(xmlFilePath string) (*InspectionV2V, error) { - xmlData, err := ReadXMLFile(xmlFilePath) + xmlData, err := os.ReadFile(xmlFilePath) if err != nil { fmt.Printf("Error read XML: %v\n", err) return nil, err @@ -31,24 +31,3 @@ func GetInspectionV2vFromFile(xmlFilePath string) (*InspectionV2V, error) { } return &xmlConf, nil } - -// 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 -}