From a3cc313a76eef5c3b02c46240d4a178961482f1b Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Tue, 5 Mar 2024 10:14:33 +0200 Subject: [PATCH 01/11] OVA: workaround for virt-v2v firmware detection Signed-off-by: Bella Khizgiyaev --- pkg/controller/plan/BUILD.bazel | 1 - pkg/controller/plan/kubevirt.go | 14 +++++- virt-v2v/cold/entrypoint.go | 78 ++++++++++++++++++++++++++------- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/pkg/controller/plan/BUILD.bazel b/pkg/controller/plan/BUILD.bazel index 82ff64394..5b3eb99de 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/context", "//pkg/controller/plan/handler", "//pkg/controller/plan/scheduler", diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 5de3e1f0e..f0ebff3bc 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -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/firmware", 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,13 +958,18 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s } defer resp.Body.Close() +<<<<<<< HEAD vmConfigBytes, err := io.ReadAll(resp.Body) +======= + vmFirmware, err := io.ReadAll(resp.Body) +>>>>>>> ae0dd5b8 (OVA: workaround for virt-v2v firmware detection) if err != nil { err = liberr.Wrap(err) return } vmConfigXML := string(vmConfigBytes) +<<<<<<< HEAD switch r.Source.Provider.Type() { case api.Ova: if vm.Firmware, err = ovfparser.GetFirmwareFromConfig(vmConfigXML); err != nil { @@ -978,6 +982,12 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s return } } +======= + vm.Firmware = string(vmFirmware) + r.Log.Info("Setting the vm fimware", + "vm", + vm.String()) +>>>>>>> ae0dd5b8 (OVA: workaround for virt-v2v firmware detection) shutdownURL := fmt.Sprintf("http://%s:8080/shutdown", pod.Status.PodIP) resp, err = http.Post(shutdownURL, "application/json", nil) diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index f94b84e41..1b99936eb 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "context" _ "embed" "encoding/json" @@ -10,6 +11,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strconv" "strings" ) @@ -24,6 +26,9 @@ const ( LUKSDIR = "/etc/luks" ) +var UEFI_RE = regexp.MustCompile(`(?i)UEFI\s+bootloader?`) +var firmware = "bios" + var ( xmlFilePath string server *http.Server @@ -327,33 +332,65 @@ func LinkDisks(diskKind string, num int) (err error) { return } -func executeVirtV2v(args []string) error { - v2vCmd := exec.Command("virt-v2v", args...) - monitorCmd := exec.Command("/usr/local/bin/virt-v2v-monitor") - monitorCmd.Stdout = os.Stdout - monitorCmd.Stderr = os.Stderr +func executeVirtV2v(args []string, source string) (err error) { + virtV2vCmd := exec.Command(args[0], args[1:]...) + virtV2vStdoutPipe, err := virtV2vCmd.StdoutPipe() + if err != nil { + fmt.Printf("Error setting up stdout pipe: %v\n", err) + return + } + teeOut := io.TeeReader(virtV2vStdoutPipe, os.Stdout) + + var teeErr io.Reader + if source == OVA { + virtV2vStderrPipe, err := virtV2vCmd.StderrPipe() + if err != nil { + fmt.Printf("Error setting up stdout pipe: %v\n", err) + return err + } + teeErr = io.TeeReader(virtV2vStderrPipe, os.Stderr) + } else { + virtV2vCmd.Stderr = os.Stderr + } + + fmt.Println("exec ", virtV2vCmd) + if err = virtV2vCmd.Start(); err != nil { + fmt.Printf("Error executing command: %v\n", err) + return + } - var writer *io.PipeWriter - monitorCmd.Stdin, writer = io.Pipe() - v2vCmd.Stdout = writer - v2vCmd.Stderr = writer - defer writer.Close() + virtV2vMonitorCmd := exec.Command("/usr/local/bin/virt-v2v-monitor") + virtV2vMonitorCmd.Stdin = teeOut + virtV2vMonitorCmd.Stdout = os.Stdout + virtV2vMonitorCmd.Stderr = os.Stderr - if err := monitorCmd.Start(); err != nil { + if err = virtV2vMonitorCmd.Start(); err != nil { fmt.Printf("Error executing monitor command: %v\n", err) return err } - fmt.Println("exec:", v2vCmd) - if err := v2vCmd.Run(); err != nil { - fmt.Printf("Error executing v2v command: %v\n", err) - return err + if source == OVA { + scanner := bufio.NewScanner(teeErr) + const maxCapacity = 1024 * 1024 + buf := make([]byte, 0, 64*1024) + scanner.Buffer(buf, maxCapacity) + + for scanner.Scan() { + line := scanner.Bytes() + if match := UEFI_RE.FindSubmatch(line); match != nil { + fmt.Println("UEFI firmware detected") + return err + } + } + + if err = virtV2vCmd.Wait(); err != nil { + fmt.Printf("Error waiting for virt-v2v to finish: %v\n", err) + return } // virt-v2v is done, we can close the pipe to virt-v2v-monitor writer.Close() - if err := monitorCmd.Wait(); err != nil { fmt.Printf("Error waiting for virt-v2v-monitor to finish: %v\n", err) return err } @@ -363,7 +400,6 @@ func executeVirtV2v(args []string) error { func getXMLFile(dir, fileExtension string) (string, error) { files, err := filepath.Glob(filepath.Join(dir, "*."+fileExtension)) - if err != nil { return "", err } if len(files) > 0 { @@ -388,7 +424,15 @@ func ovfHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("Error writing response: %v\n", err) http.Error(w, "Error writing response", http.StatusInternalServerError) } +} +func firmwareHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + if _, err := w.Write([]byte(firmware)); err != nil { + fmt.Printf("Error writing response: %v\n", err) + http.Error(w, "Error writing response", http.StatusInternalServerError) + return + } } func shutdownHandler(w http.ResponseWriter, r *http.Request) { From 839db988c69b1746f3dd17acc540efe42f063d00 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Sun, 10 Mar 2024 16:17:07 +0200 Subject: [PATCH 02/11] OVA: use -o kubevirt output for virt-v2v Signed-off-by: Bella Khizgiyaev --- virt-v2v/cold/entrypoint.go | 110 ++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 22 deletions(-) diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index 1b99936eb..be58d364d 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -30,8 +30,8 @@ var UEFI_RE = regexp.MustCompile(`(?i)UEFI\s+bootloader?`) var firmware = "bios" var ( - xmlFilePath string - server *http.Server + yamlFilePath string + server *http.Server ) const LETTERS = "abcdefghijklmnopqrstuvwxyz" @@ -194,7 +194,7 @@ func buildCommand() []string { fmt.Println("Error creating directory ", err) os.Exit(1) } - virtV2vArgs = append(virtV2vArgs, "-o", "local", "-os", DIR) + virtV2vArgs = append(virtV2vArgs, "-o", "kubevirt", "-os", DIR) //Disks on filesystem storage. if err := LinkDisks(FS, 15); err != nil { @@ -379,6 +379,12 @@ func executeVirtV2v(args []string, source string) (err error) { line := scanner.Bytes() if match := UEFI_RE.FindSubmatch(line); match != nil { fmt.Println("UEFI firmware detected") + firmware = "efi" + } + } + + if err = scanner.Err(); err != nil { + fmt.Println("Output query failed:", err) return err } } @@ -398,40 +404,36 @@ func executeVirtV2v(args []string, source string) (err 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 } 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) - if err != nil { - fmt.Printf("Error: %v\n", err) - http.Error(w, err.Error(), http.StatusInternalServerError) +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 } - w.Header().Set("Content-Type", "application/xml") - _, err = w.Write(xmlData) - if err == nil { - w.WriteHeader(http.StatusOK) - } else { - fmt.Printf("Error writing response: %v\n", err) - http.Error(w, "Error writing response", http.StatusInternalServerError) + yamlData, err := os.ReadFile(yamlFilePath) + if err != nil { + fmt.Printf("Error reading yaml file: %v\n", err) + http.Error(w, "Error reading Yaml file", http.StatusInternalServerError) + return } -} -func firmwareHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - if _, err := w.Write([]byte(firmware)); err != nil { + 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) - return } } @@ -451,3 +453,67 @@ func isValidSource(source string) bool { return false } } + +func addFirmwareToYaml(filePath string) (err error) { + var newFirmwareData string + if firmware == "bios" { + newFirmwareData = (` firmware: + bootloader: + bios: {}`) + } else { + newFirmwareData = (` firmware: + bootloader: + efi: + secureBoot: false`) + } + + file, err := os.Open(filePath) + if err != nil { + return + } + defer file.Close() + + tempFilePath := filePath + ".tmp" + + tempFile, err := os.Create(tempFilePath) + if err != nil { + return + } + defer tempFile.Close() + + scanner := bufio.NewScanner(file) + domainFound := false + + for scanner.Scan() { + line := scanner.Text() + + if strings.Contains(line, "domain:") { + domainFound = true + } + + _, err = tempFile.WriteString(line + "\n") + if err != nil { + return + } + + if domainFound { + _, err = tempFile.WriteString(newFirmwareData + "\n") + if err != nil { + return + } + domainFound = false + } + } + + if err = scanner.Err(); err != nil { + return + } + + err = os.Rename(tempFilePath, filePath) + if err != nil { + return + } + + fmt.Println("YAML file has been modified successfully.") + return +} From 7d84d6b113d44b09fd529a0d9ce9b1f6c7dadf53 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Sun, 10 Mar 2024 17:15:16 +0200 Subject: [PATCH 03/11] OVA: adjust the controller to use kubevirt yaml for firmware detection Signed-off-by: Bella Khizgiyaev --- pkg/controller/plan/BUILD.bazel | 1 + pkg/controller/plan/adapter/ova/BUILD.bazel | 3 +- .../plan/adapter/ova/kubevirtvmparser.go | 56 +++++++ pkg/controller/plan/adapter/ova/ovfparser.go | 146 ------------------ pkg/controller/plan/kubevirt.go | 16 +- 5 files changed, 74 insertions(+), 148 deletions(-) create mode 100644 pkg/controller/plan/adapter/ova/kubevirtvmparser.go delete mode 100644 pkg/controller/plan/adapter/ova/ovfparser.go diff --git a/pkg/controller/plan/BUILD.bazel b/pkg/controller/plan/BUILD.bazel index 5b3eb99de..82ff64394 100644 --- a/pkg/controller/plan/BUILD.bazel +++ b/pkg/controller/plan/BUILD.bazel @@ -22,6 +22,7 @@ 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", diff --git a/pkg/controller/plan/adapter/ova/BUILD.bazel b/pkg/controller/plan/adapter/ova/BUILD.bazel index 7e226422e..5b97257ae 100644 --- a/pkg/controller/plan/adapter/ova/BUILD.bazel +++ b/pkg/controller/plan/adapter/ova/BUILD.bazel @@ -7,7 +7,7 @@ go_library( "builder.go", "client.go", "destinationclient.go", - "ovfparser.go", + "kubevirtvmparser.go", "validator.go", ], importpath = "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/ova", @@ -29,6 +29,7 @@ go_library( "//pkg/lib/itinerary", "//pkg/lib/logging", "//vendor/github.com/go-logr/logr", + "//vendor/gopkg.in/yaml.v2:yaml_v2", "//vendor/k8s.io/api/core/v1:core", "//vendor/k8s.io/apimachinery/pkg/api/resource", "//vendor/k8s.io/utils/ptr", diff --git a/pkg/controller/plan/adapter/ova/kubevirtvmparser.go b/pkg/controller/plan/adapter/ova/kubevirtvmparser.go new file mode 100644 index 000000000..1297eac16 --- /dev/null +++ b/pkg/controller/plan/adapter/ova/kubevirtvmparser.go @@ -0,0 +1,56 @@ +package ova + +import ( + "gopkg.in/yaml.v2" +) + +type VirtualMachineInstance struct { + ApiVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Metadata Metadata `yaml:"metadata"` + Spec Spec `yaml:"spec"` +} + +type Metadata struct { + Name string `yaml:"name"` +} + +type Spec struct { + Domain Domain `yaml:"domain"` +} + +type Domain struct { + Firmware Firmware `yaml:"firmware,omitempty"` +} + +type Firmware struct { + Bootloader Bootloader `yaml:"bootloader,omitempty"` +} + +type Bootloader struct { + Bios *Bios `yaml:"bios,omitempty"` + EFI *EFI `yaml:"efi,omitempty"` +} + +type Bios struct{} + +type EFI struct { + SecureBoot bool `yaml:"secureBoot"` +} + +func ReadConfFromYaml(yamlData []byte) (firmware string, err error) { + var vmi VirtualMachineInstance + if err = yaml.Unmarshal(yamlData, &vmi); err != nil { + return + } + + if vmi.Spec.Domain.Firmware.Bootloader.Bios != nil { + firmware = "bios" + return + } + if vmi.Spec.Domain.Firmware.Bootloader.EFI != nil { + firmware = "efi" + return + } + return +} 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 f0ebff3bc..6f8cad1f2 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -38,6 +38,7 @@ 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" + yamlparser "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" @@ -940,7 +941,7 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s return } - url := fmt.Sprintf("http://%s:8080/firmware", 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. @@ -958,17 +959,22 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s } defer resp.Body.Close() +<<<<<<< HEAD <<<<<<< HEAD vmConfigBytes, err := io.ReadAll(resp.Body) ======= vmFirmware, err := io.ReadAll(resp.Body) >>>>>>> ae0dd5b8 (OVA: workaround for virt-v2v firmware detection) +======= + vmConf, err := io.ReadAll(resp.Body) +>>>>>>> 867f6944 (OVA: adjust the controller to use kubevirt yaml for firmware detection) if err != nil { err = liberr.Wrap(err) return } vmConfigXML := string(vmConfigBytes) +<<<<<<< HEAD <<<<<<< HEAD switch r.Source.Provider.Type() { case api.Ova: @@ -985,6 +991,14 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s ======= vm.Firmware = string(vmFirmware) r.Log.Info("Setting the vm fimware", +======= + vm.Firmware, err = yamlparser.ReadConfFromYaml(vmConf) + if err != nil { + r.Log.Error(err, "failed to get firmware configuration") + } + + r.Log.Info("Setting the vm firmware", +>>>>>>> 867f6944 (OVA: adjust the controller to use kubevirt yaml for firmware detection) "vm", vm.String()) >>>>>>> ae0dd5b8 (OVA: workaround for virt-v2v firmware detection) From ac0deb1afe7c30eff45e1e082df77362932529c0 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Mon, 11 Mar 2024 20:04:46 +0200 Subject: [PATCH 04/11] OVA, vSphere: pass vm new name generation to virt-v2v As part of switching the virt-v2v conversion output to -o kubevirt, we need to ensure that the converted VM name meets DNS1123 requirements otherwise, the conversion will fail. This change involves passing the newly generated VM name to the conversion pod as an environment variable and updating it as part of the virt-v2v command execution Signed-off-by: Bella Khizgiyaev --- .../bases/forklift.konveyor.io_migrations.yaml | 3 +++ .../crd/bases/forklift.konveyor.io_plans.yaml | 4 ++++ pkg/apis/forklift/v1beta1/plan/vm.go | 5 +++++ pkg/controller/plan/kubevirt.go | 18 +++++++++++------- pkg/controller/plan/migration.go | 8 ++++++++ virt-v2v/cold/entrypoint.go | 7 ++++++- 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml index fc1f38d66..3bcae59ea 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml @@ -380,6 +380,9 @@ spec: operatingSystem: description: The Operating System detected by virt-v2v. type: string + newName: + description: The new name of the VM after matching DNS1123 requirements. + type: string phase: description: Phase 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..27ed8e655 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_plans.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_plans.yaml @@ -891,6 +891,10 @@ spec: operatingSystem: description: The Operating System detected by virt-v2v. type: string + newName: + description: The new name of the VM after matching DNS1123 + requirements. + type: string phase: description: Phase type: string diff --git a/pkg/apis/forklift/v1beta1/plan/vm.go b/pkg/apis/forklift/v1beta1/plan/vm.go index a92820add..f06dcf72f 100644 --- a/pkg/apis/forklift/v1beta1/plan/vm.go +++ b/pkg/apis/forklift/v1beta1/plan/vm.go @@ -70,8 +70,13 @@ type VMStatus struct { RestorePowerState VMPowerState `json:"restorePowerState,omitempty"` // The firmware type detected from the OVF file produced by virt-v2v. Firmware string `json:"firmware,omitempty"` +<<<<<<< HEAD // 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"` +>>>>>>> 7bc1382b (OVA, vSphere: move vm new name generation to the init phase to pass it to virt-v2v.) // Conditions. libcnd.Conditions `json:",inline"` diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 6f8cad1f2..f0640f21f 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -28,7 +28,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" @@ -1284,13 +1283,9 @@ 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) } @@ -1738,6 +1733,7 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, if err != nil { return } +<<<<<<< HEAD if vm.RootDisk != "" { environment = append(environment, @@ -1746,6 +1742,14 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, Value: vm.RootDisk, }) } +======= + environment = append(environment, + core.EnvVar{ + Name: "V2V_NewName", + Value: vm.NewName, + }) + +>>>>>>> 7bc1382b (OVA, vSphere: move vm new name generation to the init phase to pass it to virt-v2v.) // 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/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index be58d364d..587d22c42 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -194,8 +194,13 @@ func buildCommand() []string { fmt.Println("Error creating directory ", err) os.Exit(1) } - virtV2vArgs = append(virtV2vArgs, "-o", "kubevirt", "-os", DIR) + virtV2vArgs = append(virtV2vArgs, "-o", "kubevirt") + if checkEnvVariablesSet("V2V_NewName") { + virtV2vArgs = append(virtV2vArgs, "-on", os.Getenv("V2V_NewName")) + } + + virtV2vArgs = append(virtV2vArgs, "-os", DIR) //Disks on filesystem storage. if err := LinkDisks(FS, 15); err != nil { os.Exit(1) From e70cfc89e92ac78b6e7e9376961f1245c9719fb6 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Tue, 12 Mar 2024 11:08:45 +0200 Subject: [PATCH 05/11] minor changes Signed-off-by: Bella Khizgiyaev --- pkg/controller/plan/adapter/ova/kubevirtvmparser.go | 4 +++- pkg/controller/plan/kubevirt.go | 8 ++++++++ virt-v2v/cold/entrypoint.go | 12 +++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pkg/controller/plan/adapter/ova/kubevirtvmparser.go b/pkg/controller/plan/adapter/ova/kubevirtvmparser.go index 1297eac16..1c2955fe5 100644 --- a/pkg/controller/plan/adapter/ova/kubevirtvmparser.go +++ b/pkg/controller/plan/adapter/ova/kubevirtvmparser.go @@ -1,6 +1,7 @@ package ova import ( + liberr "github.com/konveyor/forklift-controller/pkg/lib/error" "gopkg.in/yaml.v2" ) @@ -38,7 +39,7 @@ type EFI struct { SecureBoot bool `yaml:"secureBoot"` } -func ReadConfFromYaml(yamlData []byte) (firmware string, err error) { +func GetFirmwareFromYaml(yamlData []byte) (firmware string, err error) { var vmi VirtualMachineInstance if err = yaml.Unmarshal(yamlData, &vmi); err != nil { return @@ -52,5 +53,6 @@ func ReadConfFromYaml(yamlData []byte) (firmware string, err error) { firmware = "efi" return } + err = liberr.New("Firmware type was not detected") return } diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index f0640f21f..b0366c466 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -973,6 +973,7 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s } vmConfigXML := string(vmConfigBytes) +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD switch r.Source.Provider.Type() { @@ -992,15 +993,22 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s r.Log.Info("Setting the vm fimware", ======= vm.Firmware, err = yamlparser.ReadConfFromYaml(vmConf) +======= + vm.Firmware, err = yamlparser.GetFirmwareFromYaml(vmConf) +>>>>>>> eaa8800f (minor changes) if err != nil { r.Log.Error(err, "failed to get firmware configuration") } +<<<<<<< HEAD r.Log.Info("Setting the vm firmware", >>>>>>> 867f6944 (OVA: adjust the controller to use kubevirt yaml for firmware detection) "vm", vm.String()) >>>>>>> ae0dd5b8 (OVA: workaround for virt-v2v firmware detection) +======= + r.Log.Info("Setting the vm firmware ", vm.Firmware, "vmId", vm.ID) +>>>>>>> eaa8800f (minor changes) shutdownURL := fmt.Sprintf("http://%s:8080/shutdown", pod.Status.PodIP) resp, err = http.Post(shutdownURL, "application/json", nil) diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index 587d22c42..cf3ea294f 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -28,6 +28,7 @@ const ( var UEFI_RE = regexp.MustCompile(`(?i)UEFI\s+bootloader?`) var firmware = "bios" +var nameChanged bool var ( yamlFilePath string @@ -198,6 +199,7 @@ func buildCommand() []string { if checkEnvVariablesSet("V2V_NewName") { virtV2vArgs = append(virtV2vArgs, "-on", os.Getenv("V2V_NewName")) + nameChanged = true } virtV2vArgs = append(virtV2vArgs, "-os", DIR) @@ -318,13 +320,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) @@ -442,6 +451,7 @@ func vmHandler(w http.ResponseWriter, r *http.Request) { } } + func shutdownHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("Shutdown request received. Shutting down server.") w.WriteHeader(http.StatusNoContent) From 745c2e3c5f06f25db4760a4bde5806bbe3c1e867 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Wed, 10 Apr 2024 12:38:09 +0300 Subject: [PATCH 06/11] minor changes Signed-off-by: Bella Khizgiyaev --- pkg/apis/forklift/v1beta1/plan/vm.go | 3 - .../plan/adapter/ova/kubevirtvmparser.go | 27 ++------ pkg/controller/plan/kubevirt.go | 47 ++------------ virt-v2v/cold/entrypoint.go | 65 ------------------- 4 files changed, 13 insertions(+), 129 deletions(-) diff --git a/pkg/apis/forklift/v1beta1/plan/vm.go b/pkg/apis/forklift/v1beta1/plan/vm.go index f06dcf72f..45f5c8b3b 100644 --- a/pkg/apis/forklift/v1beta1/plan/vm.go +++ b/pkg/apis/forklift/v1beta1/plan/vm.go @@ -70,13 +70,10 @@ type VMStatus struct { RestorePowerState VMPowerState `json:"restorePowerState,omitempty"` // The firmware type detected from the OVF file produced by virt-v2v. Firmware string `json:"firmware,omitempty"` -<<<<<<< HEAD // 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"` ->>>>>>> 7bc1382b (OVA, vSphere: move vm new name generation to the init phase to pass it to virt-v2v.) // Conditions. libcnd.Conditions `json:",inline"` diff --git a/pkg/controller/plan/adapter/ova/kubevirtvmparser.go b/pkg/controller/plan/adapter/ova/kubevirtvmparser.go index 1c2955fe5..4071d80a7 100644 --- a/pkg/controller/plan/adapter/ova/kubevirtvmparser.go +++ b/pkg/controller/plan/adapter/ova/kubevirtvmparser.go @@ -19,24 +19,12 @@ type Metadata struct { type Spec struct { Domain Domain `yaml:"domain"` } - type Domain struct { - Firmware Firmware `yaml:"firmware,omitempty"` -} - -type Firmware struct { - Bootloader Bootloader `yaml:"bootloader,omitempty"` + OS OS `yaml:"os"` } -type Bootloader struct { - Bios *Bios `yaml:"bios,omitempty"` - EFI *EFI `yaml:"efi,omitempty"` -} - -type Bios struct{} - -type EFI struct { - SecureBoot bool `yaml:"secureBoot"` +type OS struct { + Firmware string `yaml:"firmware,omitempty"` } func GetFirmwareFromYaml(yamlData []byte) (firmware string, err error) { @@ -45,14 +33,11 @@ func GetFirmwareFromYaml(yamlData []byte) (firmware string, err error) { return } - if vmi.Spec.Domain.Firmware.Bootloader.Bios != nil { - firmware = "bios" - return - } - if vmi.Spec.Domain.Firmware.Bootloader.EFI != nil { - firmware = "efi" + if vmi.Spec.Domain.OS.Firmware != "" { + firmware = vmi.Spec.Domain.OS.Firmware return } + err = liberr.New("Firmware type was not detected") return } diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index b0366c466..21dd39c8c 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" @@ -37,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" - yamlparser "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" @@ -958,58 +958,26 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s } defer resp.Body.Close() -<<<<<<< HEAD -<<<<<<< HEAD - vmConfigBytes, err := io.ReadAll(resp.Body) -======= - vmFirmware, err := io.ReadAll(resp.Body) ->>>>>>> ae0dd5b8 (OVA: workaround for virt-v2v firmware detection) -======= vmConf, err := io.ReadAll(resp.Body) ->>>>>>> 867f6944 (OVA: adjust the controller to use kubevirt yaml for firmware detection) if err != nil { err = liberr.Wrap(err) return } - vmConfigXML := string(vmConfigBytes) - -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD 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 } + r.Log.Info("Setting the vm firmware ", vm.Firmware, "vmId", vm.ID) case api.VSphere: - if vm.OperatingSystem, err = ovfparser.GetOperationSystemFromConfig(vmConfigXML); err != nil { + if vm.OperatingSystem, err = util.GetOperationSystemFromYaml(vmConf); err != nil { err = liberr.Wrap(err) return } - } -======= - vm.Firmware = string(vmFirmware) - r.Log.Info("Setting the vm fimware", -======= - vm.Firmware, err = yamlparser.ReadConfFromYaml(vmConf) -======= - vm.Firmware, err = yamlparser.GetFirmwareFromYaml(vmConf) ->>>>>>> eaa8800f (minor changes) - if err != nil { - r.Log.Error(err, "failed to get firmware configuration") + r.Log.Info("Setting the vm OS ", vm.OperatingSystem, "vmId", vm.ID) } -<<<<<<< HEAD - r.Log.Info("Setting the vm firmware", ->>>>>>> 867f6944 (OVA: adjust the controller to use kubevirt yaml for firmware detection) - "vm", - vm.String()) ->>>>>>> ae0dd5b8 (OVA: workaround for virt-v2v firmware detection) -======= - r.Log.Info("Setting the vm firmware ", vm.Firmware, "vmId", vm.ID) ->>>>>>> eaa8800f (minor changes) - shutdownURL := fmt.Sprintf("http://%s:8080/shutdown", pod.Status.PodIP) resp, err = http.Post(shutdownURL, "application/json", nil) if err == nil { @@ -1294,6 +1262,7 @@ func (r *KubeVirt) virtualMachine(vm *plan.VMStatus) (object *cnv.VirtualMachine if vm.NewName != "" { originalName = vm.Name vm.Name = vm.NewName + r.Log.Info("VM name is incompatible with DNS1123 RFC, renaming", "originalName", originalName, "newName", vm.Name) } @@ -1741,7 +1710,6 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, if err != nil { return } -<<<<<<< HEAD if vm.RootDisk != "" { environment = append(environment, @@ -1750,14 +1718,13 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, Value: vm.RootDisk, }) } -======= + environment = append(environment, core.EnvVar{ Name: "V2V_NewName", Value: vm.NewName, }) ->>>>>>> 7bc1382b (OVA, vSphere: move vm new name generation to the init phase to pass it to virt-v2v.) // pod annotations annotations := map[string]string{} if r.Plan.Spec.TransferNetwork != nil { diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index cf3ea294f..6ea119aeb 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -13,7 +13,6 @@ import ( "path/filepath" "regexp" "strconv" - "strings" ) const ( @@ -468,67 +467,3 @@ func isValidSource(source string) bool { return false } } - -func addFirmwareToYaml(filePath string) (err error) { - var newFirmwareData string - if firmware == "bios" { - newFirmwareData = (` firmware: - bootloader: - bios: {}`) - } else { - newFirmwareData = (` firmware: - bootloader: - efi: - secureBoot: false`) - } - - file, err := os.Open(filePath) - if err != nil { - return - } - defer file.Close() - - tempFilePath := filePath + ".tmp" - - tempFile, err := os.Create(tempFilePath) - if err != nil { - return - } - defer tempFile.Close() - - scanner := bufio.NewScanner(file) - domainFound := false - - for scanner.Scan() { - line := scanner.Text() - - if strings.Contains(line, "domain:") { - domainFound = true - } - - _, err = tempFile.WriteString(line + "\n") - if err != nil { - return - } - - if domainFound { - _, err = tempFile.WriteString(newFirmwareData + "\n") - if err != nil { - return - } - domainFound = false - } - } - - if err = scanner.Err(); err != nil { - return - } - - err = os.Rename(tempFilePath, filePath) - if err != nil { - return - } - - fmt.Println("YAML file has been modified successfully.") - return -} From aa22f8b879adc81c29f847de997a01846d21e6c3 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Mon, 22 Jul 2024 19:52:51 +0300 Subject: [PATCH 07/11] change mtv to use virt-v2v kubevirt changes Signed-off-by: Bella Khizgiyaev --- pkg/controller/plan/BUILD.bazel | 2 +- pkg/controller/plan/adapter/ova/BUILD.bazel | 3 - .../plan/adapter/ova/kubevirtvmparser.go | 43 ------ pkg/controller/plan/util/BUILD.bazel | 4 + pkg/controller/plan/util/kubevirtvmparser.go | 133 ++++++++++++++++++ virt-v2v/cold/entrypoint.go | 90 ++++-------- 6 files changed, 163 insertions(+), 112 deletions(-) delete mode 100644 pkg/controller/plan/adapter/ova/kubevirtvmparser.go create mode 100644 pkg/controller/plan/util/kubevirtvmparser.go 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 5b97257ae..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", - "kubevirtvmparser.go", "validator.go", ], importpath = "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/ova", @@ -27,9 +26,7 @@ go_library( "//pkg/lib/error", "//pkg/lib/inventory/web", "//pkg/lib/itinerary", - "//pkg/lib/logging", "//vendor/github.com/go-logr/logr", - "//vendor/gopkg.in/yaml.v2:yaml_v2", "//vendor/k8s.io/api/core/v1:core", "//vendor/k8s.io/apimachinery/pkg/api/resource", "//vendor/k8s.io/utils/ptr", diff --git a/pkg/controller/plan/adapter/ova/kubevirtvmparser.go b/pkg/controller/plan/adapter/ova/kubevirtvmparser.go deleted file mode 100644 index 4071d80a7..000000000 --- a/pkg/controller/plan/adapter/ova/kubevirtvmparser.go +++ /dev/null @@ -1,43 +0,0 @@ -package ova - -import ( - liberr "github.com/konveyor/forklift-controller/pkg/lib/error" - "gopkg.in/yaml.v2" -) - -type VirtualMachineInstance struct { - ApiVersion string `yaml:"apiVersion"` - Kind string `yaml:"kind"` - Metadata Metadata `yaml:"metadata"` - Spec Spec `yaml:"spec"` -} - -type Metadata struct { - Name string `yaml:"name"` -} - -type Spec struct { - Domain Domain `yaml:"domain"` -} -type Domain struct { - OS OS `yaml:"os"` -} - -type OS struct { - Firmware string `yaml:"firmware,omitempty"` -} - -func GetFirmwareFromYaml(yamlData []byte) (firmware string, err error) { - var vmi VirtualMachineInstance - if err = yaml.Unmarshal(yamlData, &vmi); err != nil { - return - } - - if vmi.Spec.Domain.OS.Firmware != "" { - firmware = vmi.Spec.Domain.OS.Firmware - return - } - - err = liberr.New("Firmware type was not detected") - return -} diff --git a/pkg/controller/plan/util/BUILD.bazel b/pkg/controller/plan/util/BUILD.bazel index 195f4cb78..7251e3d05 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,10 @@ go_library( "//pkg/apis/forklift/v1beta1", "//pkg/controller/provider/web/openstack", "//pkg/controller/provider/web/ovirt", + "//pkg/lib/error", + "//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..509a86c70 --- /dev/null +++ b/pkg/controller/plan/util/kubevirtvmparser.go @@ -0,0 +1,133 @@ +package util + +import ( + "fmt" + "strings" + + liberr "github.com/konveyor/forklift-controller/pkg/lib/error" + "github.com/konveyor/forklift-controller/pkg/lib/logging" + "gopkg.in/yaml.v2" +) + +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 VirtualMachine struct { + ApiVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Metadata Metadata `yaml:"metadata"` + Spec Spec `yaml:"spec"` +} + +type Metadata struct { + Name string `yaml:"name"` + Labels map[string]string `yaml:"labels"` +} + +type Spec struct { + Domain Domain `yaml:"domain"` +} + +type Domain struct { + OS OS `yaml:"os"` +} + +type OS struct { + Firmware string `yaml:"firmware"` +} + +type Bios struct{} + +type EFI struct { + SecureBoot bool `yaml:"secureBoot"` +} + +func GetFirmwareFromYaml(yamlData []byte) (firmware string, err error) { + var vm VirtualMachine + if err = yaml.Unmarshal(yamlData, &vm); err != nil { + return + } + + if vm.Spec.Domain.OS.Firmware != "" { + return + } + err = liberr.New("Firmware type was not detected") + return +} + +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[os] + if !ok { + log.Info(fmt.Sprintf("Received %s, mapped to: %s", labelOS, os)) + os = "otherGuest64" + } + return +} diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index 6ea119aeb..15362b76b 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -1,7 +1,6 @@ package main import ( - "bufio" "context" _ "embed" "encoding/json" @@ -11,8 +10,8 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strconv" + "strings" ) const ( @@ -25,10 +24,6 @@ const ( LUKSDIR = "/etc/luks" ) -var UEFI_RE = regexp.MustCompile(`(?i)UEFI\s+bootloader?`) -var firmware = "bios" -var nameChanged bool - var ( yamlFilePath string server *http.Server @@ -37,6 +32,9 @@ var ( const LETTERS = "abcdefghijklmnopqrstuvwxyz" const LETTERS_LENGTH = len(LETTERS) +var firmware = "bios" +var nameChanged bool + func main() { source := os.Getenv("V2V_source") if source == vSphere { @@ -63,18 +61,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"} @@ -202,6 +200,7 @@ func buildCommand() []string { } virtV2vArgs = append(virtV2vArgs, "-os", DIR) + //Disks on filesystem storage. if err := LinkDisks(FS, 15); err != nil { os.Exit(1) @@ -345,71 +344,33 @@ func LinkDisks(diskKind string, num int) (err error) { return } -func executeVirtV2v(args []string, source string) (err error) { - virtV2vCmd := exec.Command(args[0], args[1:]...) - virtV2vStdoutPipe, err := virtV2vCmd.StdoutPipe() - if err != nil { - fmt.Printf("Error setting up stdout pipe: %v\n", err) - return - } - teeOut := io.TeeReader(virtV2vStdoutPipe, os.Stdout) - - var teeErr io.Reader - if source == OVA { - virtV2vStderrPipe, err := virtV2vCmd.StderrPipe() - if err != nil { - fmt.Printf("Error setting up stdout pipe: %v\n", err) - return err - } - teeErr = io.TeeReader(virtV2vStderrPipe, os.Stderr) - } else { - virtV2vCmd.Stderr = os.Stderr - } - - fmt.Println("exec ", virtV2vCmd) - if err = virtV2vCmd.Start(); err != nil { - fmt.Printf("Error executing command: %v\n", err) - return - } +func executeVirtV2v(args []string) error { + v2vCmd := exec.Command("virt-v2v", args...) + monitorCmd := exec.Command("/usr/local/bin/virt-v2v-monitor") + monitorCmd.Stdout = os.Stdout + monitorCmd.Stderr = os.Stderr - virtV2vMonitorCmd := exec.Command("/usr/local/bin/virt-v2v-monitor") - virtV2vMonitorCmd.Stdin = teeOut - virtV2vMonitorCmd.Stdout = os.Stdout - virtV2vMonitorCmd.Stderr = os.Stderr + var writer *io.PipeWriter + monitorCmd.Stdin, writer = io.Pipe() + v2vCmd.Stdout = writer + v2vCmd.Stderr = writer + defer writer.Close() - if err = virtV2vMonitorCmd.Start(); err != nil { + if err := monitorCmd.Start(); err != nil { fmt.Printf("Error executing monitor command: %v\n", err) return err } - if source == OVA { - scanner := bufio.NewScanner(teeErr) - const maxCapacity = 1024 * 1024 - buf := make([]byte, 0, 64*1024) - scanner.Buffer(buf, maxCapacity) - - for scanner.Scan() { - line := scanner.Bytes() - if match := UEFI_RE.FindSubmatch(line); match != nil { - fmt.Println("UEFI firmware detected") - firmware = "efi" - } - } - - if err = scanner.Err(); err != nil { - fmt.Println("Output query failed:", err) - return err - } - } - - if err = virtV2vCmd.Wait(); err != nil { - fmt.Printf("Error waiting for virt-v2v to finish: %v\n", err) - return + fmt.Println("exec:", v2vCmd) + if err := v2vCmd.Run(); err != nil { + fmt.Printf("Error executing v2v command: %v\n", err) + return err } // virt-v2v is done, we can close the pipe to virt-v2v-monitor writer.Close() + if err := monitorCmd.Wait(); err != nil { fmt.Printf("Error waiting for virt-v2v-monitor to finish: %v\n", err) return err } @@ -450,7 +411,6 @@ func vmHandler(w http.ResponseWriter, r *http.Request) { } } - func shutdownHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("Shutdown request received. Shutting down server.") w.WriteHeader(http.StatusNoContent) From 9cd5d112560831243618eddaebc9228ab403c572 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Tue, 23 Jul 2024 14:47:46 +0300 Subject: [PATCH 08/11] minor changes virt-v2v ymal parser Signed-off-by: Bella Khizgiyaev --- pkg/controller/plan/util/BUILD.bazel | 1 - pkg/controller/plan/util/kubevirtvmparser.go | 52 ++++++++++---------- virt-v2v/cold/entrypoint.go | 3 +- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/pkg/controller/plan/util/BUILD.bazel b/pkg/controller/plan/util/BUILD.bazel index 7251e3d05..dff289312 100644 --- a/pkg/controller/plan/util/BUILD.bazel +++ b/pkg/controller/plan/util/BUILD.bazel @@ -14,7 +14,6 @@ go_library( "//pkg/apis/forklift/v1beta1", "//pkg/controller/provider/web/openstack", "//pkg/controller/provider/web/ovirt", - "//pkg/lib/error", "//pkg/lib/logging", "//pkg/settings", "//vendor/gopkg.in/yaml.v2:yaml_v2", diff --git a/pkg/controller/plan/util/kubevirtvmparser.go b/pkg/controller/plan/util/kubevirtvmparser.go index 509a86c70..d54b79250 100644 --- a/pkg/controller/plan/util/kubevirtvmparser.go +++ b/pkg/controller/plan/util/kubevirtvmparser.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - liberr "github.com/konveyor/forklift-controller/pkg/lib/error" "github.com/konveyor/forklift-controller/pkg/lib/logging" "gopkg.in/yaml.v2" ) @@ -53,47 +52,50 @@ var osV2VMap = map[string]string{ "win2k22": "windows2022srvNext_64Guest", } -type VirtualMachine struct { - ApiVersion string `yaml:"apiVersion"` - Kind string `yaml:"kind"` - Metadata Metadata `yaml:"metadata"` - Spec Spec `yaml:"spec"` +type OS struct { + Firmware string `yaml:"firmware"` } -type Metadata struct { - Name string `yaml:"name"` - Labels map[string]string `yaml:"labels"` +type Domain struct { + OS OS `yaml:"os"` } -type Spec struct { +type TemplateSpec struct { Domain Domain `yaml:"domain"` } -type Domain struct { - OS OS `yaml:"os"` +type Template struct { + Spec TemplateSpec `yaml:"spec"` } -type OS struct { - Firmware string `yaml:"firmware"` +type VirtualMachineSpec struct { + Template Template `yaml:"template"` } -type Bios struct{} +type VirtualMachine struct { + APIVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Metadata Metadata `yaml:"metadata"` + Spec VirtualMachineSpec `yaml:"spec"` +} -type EFI struct { - SecureBoot bool `yaml:"secureBoot"` +type Metadata struct { + Name string `yaml:"name"` + Labels map[string]string `yaml:"labels"` } -func GetFirmwareFromYaml(yamlData []byte) (firmware string, err error) { +func GetFirmwareFromYaml(yamlData []byte) (string, error) { var vm VirtualMachine - if err = yaml.Unmarshal(yamlData, &vm); err != nil { - return + if err := yaml.Unmarshal(yamlData, &vm); err != nil { + return "", err } - if vm.Spec.Domain.OS.Firmware != "" { - return + firmware := vm.Spec.Template.Spec.Domain.OS.Firmware + if firmware == "" { + log.Info("Firmware type was not detected") } - err = liberr.New("Firmware type was not detected") - return + + return firmware, nil } func GetOperationSystemFromYaml(yamlData []byte) (os string, err error) { @@ -124,7 +126,7 @@ func mapOs(labelOS string) (os string) { distro = "fedora" } - os, ok := osV2VMap[os] + os, ok := osV2VMap[distro] if !ok { log.Info(fmt.Sprintf("Received %s, mapped to: %s", labelOS, os)) os = "otherGuest64" diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index 15362b76b..46aaf6bba 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -32,7 +32,6 @@ var ( const LETTERS = "abcdefghijklmnopqrstuvwxyz" const LETTERS_LENGTH = len(LETTERS) -var firmware = "bios" var nameChanged bool func main() { @@ -194,6 +193,8 @@ func buildCommand() []string { } 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 From a27f2b08d7c38e29effb5127ca0b3d0152f8ff06 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Tue, 20 Aug 2024 14:25:30 +0300 Subject: [PATCH 09/11] minor changes for -o yaml support Signed-off-by: Bella Khizgiyaev --- .../crd/bases/forklift.konveyor.io_migrations.yaml | 6 +++--- .../crd/bases/forklift.konveyor.io_plans.yaml | 6 +++--- pkg/controller/plan/kubevirt.go | 14 ++++++++------ pkg/controller/plan/util/kubevirtvmparser.go | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml index 3bcae59ea..ff0554bc6 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml @@ -377,12 +377,12 @@ spec: The VM Namespace Only relevant for an openshift source. type: string - operatingSystem: - description: The Operating System detected by virt-v2v. - 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 phase: description: Phase 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 27ed8e655..f6da3b769 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_plans.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_plans.yaml @@ -888,13 +888,13 @@ spec: The VM Namespace Only relevant for an openshift source. type: string - operatingSystem: - description: The Operating System detected by virt-v2v. - 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 phase: description: Phase type: string diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 21dd39c8c..71d59feeb 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -970,7 +970,7 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s return } r.Log.Info("Setting the vm firmware ", vm.Firmware, "vmId", vm.ID) - case api.VSphere: + if vm.OperatingSystem, err = util.GetOperationSystemFromYaml(vmConf); err != nil { err = liberr.Wrap(err) return @@ -1719,11 +1719,13 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, }) } - environment = append(environment, - core.EnvVar{ - Name: "V2V_NewName", - Value: vm.NewName, - }) + if vm.NewName != "" { + environment = append(environment, + core.EnvVar{ + Name: "V2V_NewName", + Value: vm.NewName, + }) + } // pod annotations annotations := map[string]string{} diff --git a/pkg/controller/plan/util/kubevirtvmparser.go b/pkg/controller/plan/util/kubevirtvmparser.go index d54b79250..37ab5d95e 100644 --- a/pkg/controller/plan/util/kubevirtvmparser.go +++ b/pkg/controller/plan/util/kubevirtvmparser.go @@ -10,7 +10,7 @@ import ( const ( // Name. - Name = "virt-v2v-parser" + Name = "kubevirt-vm-parser" ) // Package logger. From 540509155f9474962bf220723a379eaeb039bde4 Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Mon, 2 Sep 2024 13:38:49 +0300 Subject: [PATCH 10/11] Bump virt-v2v cold deps Signed-off-by: Bella Khizgiyaev --- virt-v2v/cold/BUILD.bazel | 14 ++++++------- virt-v2v/cold/WORKSPACE | 42 +++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 28 deletions(-) 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", ], ) From ed26a119eaf96c4dfd11bbc39d2b6dd57554d9ba Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Mon, 2 Sep 2024 16:59:02 +0300 Subject: [PATCH 11/11] Change VM customization to use a YAML output file instead of XML. As part of the virt-v2v migration, we inspect the OS type and disk paths. Until now, this has been done with the XML output file. Due to the change to YAML format, this process needs to be adapted as well. Since we cannot use external libraries in the virt-v2v directory, it was implemented using bufio. Signed-off-by: Bella Khizgiyaev --- virt-v2v/cold/entrypoint.go | 26 +++------- virt-v2v/cold/xml-reader.go | 97 ------------------------------------ virt-v2v/cold/yaml-reader.go | 73 +++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 116 deletions(-) delete mode 100644 virt-v2v/cold/xml-reader.go create mode 100644 virt-v2v/cold/yaml-reader.go diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index 46aaf6bba..752749104 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -82,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) @@ -102,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") @@ -112,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. @@ -129,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 @@ -140,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 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 +}