diff --git a/pkg/controller/plan/adapter/ova/ovfparser.go b/pkg/controller/plan/adapter/ova/ovfparser.go index aab4be438..eb175da5d 100644 --- a/pkg/controller/plan/adapter/ova/ovfparser.go +++ b/pkg/controller/plan/adapter/ova/ovfparser.go @@ -3,35 +3,21 @@ package ova import ( "encoding/xml" "strings" + + liberr "github.com/konveyor/forklift-controller/pkg/lib/error" ) type OvaVmconfig struct { - XMLName xml.Name `xml:"domain"` - Name string `xml:"name"` - OS OS `xml:"os"` -} - -type OS struct { - Type OSType `xml:"type"` - Loader Loader `xml:"loader"` - Nvram Nvram `xml:"nvram"` -} - -type OSType struct { - Arch string `xml:"arch,attr"` - Machine string `xml:"machine,attr"` - Content string `xml:",chardata"` + XMLName xml.Name `xml:"domain"` + Firmware Firmware `xml:"firmware"` } -type Loader struct { - Readonly string `xml:"readonly,attr"` - Type string `xml:"type,attr"` - Secure string `xml:"secure,attr"` - Path string `xml:",chardata"` +type Firmware struct { + Bootloader Bootloader `xml:"bootloader"` } -type Nvram struct { - Template string `xml:"template,attr"` +type Bootloader struct { + Type string `xml:"type,attr"` } func readConfFromXML(xmlData string) (*OvaVmconfig, error) { @@ -54,9 +40,9 @@ func GetFirmwareFromConfig(vmConfigXML string) (firmware string, err error) { return } - path := xmlConf.OS.Loader.Path - if strings.Contains(path, "OVMF") { - return UEFI, nil + firmware = xmlConf.Firmware.Bootloader.Type + if firmware == "" { + err = liberr.New("failed to get the firmware type from virt-v2v config") } - return BIOS, nil + return } diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 30c22ae56..62dbed6a8 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -913,7 +913,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. @@ -941,6 +941,8 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s return } + r.Log.Info("setting the vm firmware to ", firmware, "vmId", vm.ID) + vm.Firmware = firmware shutdownURL := fmt.Sprintf("http://%s:8080/shutdown", pod.Status.PodIP) diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index 8eb5b0cea..93cd5d7a9 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "context" "fmt" "io" @@ -8,7 +9,9 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strconv" + "strings" ) const ( @@ -25,6 +28,9 @@ var ( server *http.Server ) +var UEFI_RE = regexp.MustCompile(`(?i)UEFI\s+bootloader?`) +var firmware = "bios" + func main() { virtV2vArgs := []string{"virt-v2v", "-v", "-x"} source := os.Getenv("V2V_source") @@ -97,7 +103,7 @@ func main() { virtV2vArgs = append(virtV2vArgs, "--", os.Getenv("V2V_vmName")) } - if err := executeVirtV2v(virtV2vArgs); err != nil { + if err := executeVirtV2v(source, virtV2vArgs); err != nil { fmt.Println("Error executing virt-v2v command ", err) os.Exit(1) } @@ -110,7 +116,7 @@ func main() { os.Exit(1) } - http.HandleFunc("/ovf", ovfHandler) + http.HandleFunc("/vm", vmHandler) http.HandleFunc("/shutdown", shutdownHandler) server = &http.Server{Addr: ":8080"} @@ -169,16 +175,26 @@ func LinkDisks(diskKind string, num int) (err error) { return } -func executeVirtV2v(args []string) (err error) { +func executeVirtV2v(source string, args []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) - tee := io.TeeReader(virtV2vStdoutPipe, os.Stdout) - virtV2vCmd.Stderr = os.Stderr + 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 { @@ -187,7 +203,7 @@ func executeVirtV2v(args []string) (err error) { } virtV2vMonitorCmd := exec.Command("/usr/local/bin/virt-v2v-monitor") - virtV2vMonitorCmd.Stdin = tee + virtV2vMonitorCmd.Stdin = teeOut virtV2vMonitorCmd.Stdout = os.Stdout virtV2vMonitorCmd.Stderr = os.Stderr @@ -196,6 +212,26 @@ func executeVirtV2v(args []string) (err error) { return } + 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 @@ -214,13 +250,19 @@ func getXMLFile(dir, fileExtension string) (string, error) { return "", fmt.Errorf("XML file was not found.") } -func ovfHandler(w http.ResponseWriter, r *http.Request) { +func vmHandler(w http.ResponseWriter, r *http.Request) { if xmlFilePath == "" { fmt.Println("Error: XML file path is empty.") http.Error(w, "XML file path is empty", http.StatusInternalServerError) return } + if err := addFirmwareToXml(xmlFilePath); err != nil { + fmt.Println("Error setting the firmware configuration in the ovf ", err) + http.Error(w, "Error setting the firmware configuration in the ovf", http.StatusInternalServerError) + return + } + xmlData, err := os.ReadFile(xmlFilePath) if err != nil { fmt.Printf("Error reading XML file: %v\n", err) @@ -255,3 +297,66 @@ func isValidSource(source string) bool { return false } } + +func addFirmwareToXml(filePath string) (err error) { + var newFirmwareData string + if firmware == "bios" { + newFirmwareData = (` + + `) + } else { + newFirmwareData = (` + + `) + } + + 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, "") { + 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("XML file has been modified successfully.") + return +}