Skip to content

Commit

Permalink
MTV-1366 | Add virt-customize to warm migration and OVA
Browse files Browse the repository at this point in the history
Issue:
Right now we run the virt-customize only on the cold local migration.
But we would wanted to run it on all vmware migrations.

Fix:
This commit adds a new stage to the converison pod which scans the guest
to get the operating system. It is using the tool `virt-v2v-inspector`
which generates inspection xml file. This file contains lot of
information about guest, including the OS. This tool is run after the
`virt-v2v` step because for the OVA and cold local migration we dont have
the disk available before that step.
Additionally this patch adds a new `inspection` endpoint to the conversion
pod. The controller requests the endpoint to get the operating system so
it could be set as a preference to the kubevirt.

New known issues:
The OVA won't be able to preserve static IP addresses as we get the IP
adress configuration by form the vmware guest agent when the VM is
running. There might be some information inside the ova file but right
now this is not fixed.

Signed-off-by: Martin Necas <mnecas@redhat.com>
  • Loading branch information
mnecas committed Sep 17, 2024
1 parent 9f33072 commit c8a80f1
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 281 deletions.
1 change: 1 addition & 0 deletions pkg/controller/plan/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ go_library(
"//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",
"//pkg/controller/plan/scheduler",
Expand Down
62 changes: 0 additions & 62 deletions pkg/controller/plan/adapter/ova/ovfparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ova

import (
"encoding/xml"
"fmt"
"strings"

"github.com/konveyor/forklift-controller/pkg/lib/logging"
Expand All @@ -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 OvaVmconfig struct {
XMLName xml.Name `xml:"domain"`
Name string `xml:"name"`
Expand Down Expand Up @@ -119,28 +82,3 @@ func GetFirmwareFromConfig(vmConfigXML string) (firmware string, err error) {
}
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
}
2 changes: 2 additions & 0 deletions pkg/controller/plan/adapter/vsphere/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ go_library(
"client.go",
"destinationclient.go",
"host.go",
"inspectionparser.go",
"validator.go",
],
importpath = "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/vsphere",
Expand All @@ -28,6 +29,7 @@ go_library(
"//pkg/lib/condition",
"//pkg/lib/error",
"//pkg/lib/itinerary",
"//pkg/lib/logging",
"//pkg/lib/ref",
"//pkg/settings",
"//vendor/github.com/vmware/govmomi",
Expand Down
85 changes: 85 additions & 0 deletions pkg/controller/plan/adapter/vsphere/inspectionparser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package vsphere

import (
"encoding/xml"
"fmt"

"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 InspectionOS struct {
Name string `xml:"name"`
Distro string `xml:"distro"`
Osinfo string `xml:"osinfo"`
Arch string `xml:"arch"`
}

type InspectionV2V struct {
OS InspectionOS `xml:"operatingsystem"`
}

func ParseInspectionFromString(xmlData string) (InspectionV2V, error) {
var xmlConf InspectionV2V
err := xml.Unmarshal([]byte(xmlData), &xmlConf)
if err != nil {
return InspectionV2V{}, fmt.Errorf("Error unmarshalling XML: %v\n", err)
}
return xmlConf, nil
}

func GetOperationSystemFromConfig(vmConfigXML string) (string, error) {
inspection, err := ParseInspectionFromString(vmConfigXML)
if err != nil {
return "", err
}
os, ok := osV2VMap[inspection.OS.Osinfo]
if !ok {
log.Info(fmt.Sprintf("Received %s, mapped to: %s", os, os))
os = "otherGuest64"
}
return os, nil
}
55 changes: 36 additions & 19 deletions pkg/controller/plan/kubevirt.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"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"
inspectionparser "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/vsphere"
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"
Expand Down Expand Up @@ -935,14 +936,30 @@ func (r *KubeVirt) GetGuestConversionPod(vm *plan.VMStatus) (pod *core.Pod, err
return
}

func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, step *plan.Step) (err error) {
if pod.Status.PodIP == "" {
func (r *KubeVirt) getInspectionXml(pod *core.Pod) (string, error) {
if pod == nil {
return "", liberr.New("no pod found to get the inspection")
}
inspectionUrl := fmt.Sprintf("http://%s:8080/inspection", pod.Status.PodIP)
resp, err := http.Get(inspectionUrl)
if err != nil {
return "", liberr.Wrap(err)
}
defer resp.Body.Close()
inspectionBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", liberr.Wrap(err)
}
return string(inspectionBytes), nil
}

func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, step *plan.Step) error {
if pod == nil || pod.Status.PodIP == "" {
//we need the IP for fetching the configuration of the convered VM.
return
return nil
}

url := fmt.Sprintf("http://%s:8080/ovf", 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.
The server will be accessible via virt-v2v only after the command has finished.
Expand All @@ -953,29 +970,29 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s
resp, err := http.Get(url)
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
err = nil
return nil
}
return
return err
}
defer resp.Body.Close()

vmConfigBytes, err := io.ReadAll(resp.Body)
if err != nil {
err = liberr.Wrap(err)
return
}
vmConfigXML := string(vmConfigBytes)

switch r.Source.Provider.Type() {
case api.Ova:
vmConfigBytes, err := io.ReadAll(resp.Body)
if err != nil {
return liberr.Wrap(err)
}
vmConfigXML := string(vmConfigBytes)
if vm.Firmware, err = ovfparser.GetFirmwareFromConfig(vmConfigXML); err != nil {
err = liberr.Wrap(err)
return
return liberr.Wrap(err)
}
case api.VSphere:
if vm.OperatingSystem, err = ovfparser.GetOperationSystemFromConfig(vmConfigXML); err != nil {
err = liberr.Wrap(err)
return
inspectionXML, err := r.getInspectionXml(pod)
if err != nil {
return err
}
if vm.OperatingSystem, err = inspectionparser.GetOperationSystemFromConfig(inspectionXML); err != nil {
return liberr.Wrap(err)
}
}

Expand All @@ -991,7 +1008,7 @@ func (r *KubeVirt) UpdateVmByConvertedConfig(vm *plan.VMStatus, pod *core.Pod, s
}
step.MarkCompleted()
step.Progress.Completed = step.Progress.Total
return
return err
}

// Delete the PVC consumer pod on the destination cluster.
Expand Down
Loading

0 comments on commit c8a80f1

Please sign in to comment.