From 693a75f865c96ca9ccd27d0a83a3fc9bcca53a9d Mon Sep 17 00:00:00 2001 From: Bella Khizgiyaev Date: Thu, 15 Feb 2024 12:15:00 +0200 Subject: [PATCH] Change virt-v2v bash script to use go wrapper. Signed-off-by: Bella Khizgiyaev --- virt-v2v/cold/BUILD.bazel | 23 +++- virt-v2v/cold/entrypoint | 101 --------------- virt-v2v/cold/entrypoint.go | 236 ++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+), 104 deletions(-) delete mode 100755 virt-v2v/cold/entrypoint create mode 100644 virt-v2v/cold/entrypoint.go diff --git a/virt-v2v/cold/BUILD.bazel b/virt-v2v/cold/BUILD.bazel index d56699cca..b7746651f 100644 --- a/virt-v2v/cold/BUILD.bazel +++ b/virt-v2v/cold/BUILD.bazel @@ -1,3 +1,4 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") load( "@io_bazel_rules_docker//container:container.bzl", "container_image", @@ -89,15 +90,31 @@ container_image( visibility = ["//visibility:public"], ) +go_library( + name = "cold_lib", + srcs = ["entrypoint.go"], + importpath = "github.com/konveyor/forklift-controller/virt-v2v/cold", + visibility = ["//visibility:private"], +) + +go_binary( + name = "virt-v2v-wrapper", + embed = [":cold_lib"], + visibility = ["//visibility:public"], +) + container_image( name = "forklift-virt-v2v", base = ":virt-v2v-layer", directory = "/usr/local/bin/", empty_dirs = ["/disks"], - entrypoint = ["/usr/local/bin/entrypoint"], - env = {"LIBGUESTFS_BACKEND": "direct"}, + entrypoint = ["/usr/local/bin/virt-v2v-wrapper"], + env = { + "LIBGUESTFS_BACKEND": "direct", + "LIBGUESTFS_PATH": "/usr/lib64/guestfs/appliance", + }, files = [ - "entrypoint", + ":virt-v2v-wrapper", "@forklift//cmd/image-converter", "@forklift//cmd/virt-v2v-monitor", ], diff --git a/virt-v2v/cold/entrypoint b/virt-v2v/cold/entrypoint deleted file mode 100755 index 5ed8b1818..000000000 --- a/virt-v2v/cold/entrypoint +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail -shopt -s nullglob - -if [ -z "$V2V_source" ] ; then - echo "Following environment needs to be defined:" - echo - echo " V2V_source" - exit 1 -fi - -# This variable is used to build the command with args for virt-v2v. -args=(virt-v2v -v -x) - -if [ "$V2V_source" == "vSphere" ] ; then - if [ -z "$V2V_libvirtURL" ] || \ - [ -z "$V2V_secretKey" ] || \ - [ -z "$V2V_vmName" ] ; then - echo "Following environment needs to be defined:" - echo - echo " V2V_libvirtURL, V2V_secretKey, V2V_vmName" - exit 1 - fi - args+=(-i libvirt -ic "$V2V_libvirtURL") -fi - -if [ "$V2V_source" == "ova" ] ; then - if [ -z "$V2V_diskPath" ] || \ - [ -z "$V2V_vmName" ] ; then - echo "Following environment needs to be defined:" - echo - echo " V2V_diskPath, V2V_vmName" - exit 1 - fi - args+=(-i ova "$V2V_diskPath") -fi - -export LIBGUESTFS_PATH=/usr/lib64/guestfs/appliance - -echo "Preparing virt-v2v" - -# Temporary storage location -DIR="/var/tmp/v2v" -mkdir -p $DIR -args+=(-o local -os "$DIR") - -# Generate disk name suffix from disk number. E.g. "c" (as in "sdc") for -# 3rd disk. -gen_name() { - local chars=({a..z}) - local cnt="${#chars[@]}" - local i=$(($1 % cnt)) - local rest=$(($1 / cnt)) - local prefix="" - if [ $rest -gt 0 ] ; then - prefix="$(gen_name $rest)" - fi - echo "$prefix${chars[$i-1]}" -} - -# Disks on filesystem storage. -# e.g.: /mnt/disks/disk0/disk.img -> vmName-sda -for disk in /mnt/disks/disk[0-9]* ; do - num="${disk:15}" - ln -s "$disk/disk.img" "$DIR/$V2V_vmName-sd$(gen_name "$((num+1))")" -done -# Disks on block storage. -# e.g.: /dev/block0 -> vmName-sda -for disk in /dev/block[0-9]* ; do - num="${disk:10}" - ln -s "$disk" "$DIR/$V2V_vmName-sd$(gen_name "$((num+1))")" -done - -if [ "$V2V_source" == "vSphere" ] ; then - if [ -e "/etc/secret/cacert" ]; then - # use the specified certificate - ln -sf /etc/secret/cacert /opt/ca-bundle.crt - else - # otherwise, keep system pool certificates - ln -sf /etc/pki/tls/certs/ca-bundle.crt.bak /opt/ca-bundle.crt - fi - - # password - args+=(-ip "/etc/secret/secretKey") - - # Use VDDK if present - if [ -d "/opt/vmware-vix-disklib-distrib" ]; then - args+=( - -it vddk - -io vddk-libdir=/opt/vmware-vix-disklib-distrib - -io "vddk-thumbprint=$V2V_fingerprint" - ) - fi - args+=(-- "$V2V_vmName") -fi - -echo "Starting virt-v2v" -set -x -ls -l "$DIR" - -exec "${args[@]}" |& /usr/local/bin/virt-v2v-monitor diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go new file mode 100644 index 000000000..b4dbc7ab6 --- /dev/null +++ b/virt-v2v/cold/entrypoint.go @@ -0,0 +1,236 @@ +package main + +import ( + "context" + "fmt" + "net/http" + "os" + "os/exec" + "path/filepath" + "strconv" +) + +const ( + OVA = "ova" + vSphere = "vSphere" + DIR = "/var/tmp/v2v" + FS = "/mnt/disks/disk[0-9]*" + Block = "/dev/block[0-9]*" + VDDK = "/opt/vmware-vix-disklib-distrib" +) + +var ( + xmlFilePath string + server *http.Server +) + +func main() { + virtV2vArgs := []string{"virt-v2v", "-v", "-x"} + source := os.Getenv("V2V_source") + + requiredEnvVars := map[string][]string{ + vSphere: {"V2V_libvirtURL", "V2V_secretKey", "V2V_vmName"}, + OVA: {"V2V_diskPath", "V2V_vmName"}, + } + + if envVars, ok := requiredEnvVars[source]; ok { + if !checkEnvVariablesSet(envVars...) { + fmt.Printf("Following environment variables need to be defined: %v\n", envVars) + os.Exit(1) + } + } + + fmt.Println("Preparing virt-v2v") + switch source { + case vSphere: + virtV2vArgs = append(virtV2vArgs, "-i", "libvirt", "-ic", os.Getenv("V2V_libvirtURL")) + case OVA: + virtV2vArgs = append(virtV2vArgs, "-i", "ova", os.Getenv("V2V_diskPath")) + } + + if err := os.MkdirAll(DIR, os.ModePerm); err != nil { + fmt.Println("Error creating directory ", err) + os.Exit(1) + } + virtV2vArgs = append(virtV2vArgs, "-o", "local", "-os", DIR) + + //Disks on filesystem storage. + if err := LinkDisks(FS, 15); err != nil { + os.Exit(1) + } + //Disks on block storage. + if err := LinkDisks(Block, 10); err != nil { + os.Exit(1) + } + + if source == vSphere { + if _, err := os.Stat("/etc/secret/cacert"); err == nil { + err = os.Symlink("/etc/secret/cacert", "/opt/ca-bundle.crt") + if err != nil { + fmt.Println("Error creating ca cert link ", err) + os.Exit(1) + } + } else { + err := os.Symlink("/etc/pki/tls/certs/ca-bundle.crt.bak", "/opt/ca-bundle.crt") + if err != nil { + fmt.Println("Error creating ca cert link ", err) + os.Exit(1) + } + } + virtV2vArgs = append(virtV2vArgs, "-ip", "/etc/secret/secretKey") + + if info, err := os.Stat(VDDK); err == nil && info.IsDir() { + virtV2vArgs = append(virtV2vArgs, + "-it", "vddk", + "-io", fmt.Sprintf("vddk-libdir=%s", VDDK), + "-io", fmt.Sprintf("vddk-thumbprint=%s", os.Getenv("V2V_fingerprint")), + ) + } + virtV2vArgs = append(virtV2vArgs, "--", os.Getenv("V2V_vmName")) + } + + if err := executeVirtV2v(virtV2vArgs); err != nil { + fmt.Println("Error executing virt-v2v command ", err) + os.Exit(1) + } + + if source == OVA { + var err error + xmlFilePath, err = getXMLFile(DIR, "xml") + if err != nil { + fmt.Println("Error gettin XML file:", err) + os.Exit(1) + } + + http.HandleFunc("/xml", xmlHandler) + http.HandleFunc("/shutdown", shutdownHandler) + server = &http.Server{Addr: ":8080"} + + fmt.Println("Starting server on :8080") + if err := server.ListenAndServe(); err != http.ErrServerClosed { + fmt.Printf("Error starting server: %v\n", err) + os.Exit(1) + } + } +} + +func checkEnvVariablesSet(envVars ...string) bool { + for _, v := range envVars { + if os.Getenv(v) == "" { + return false + } + } + return true +} + +func genName(diskNum int) string { + if diskNum <= 0 { + return "" + } + + letters := []int32("abcdefghijklmnopqrstuvwxyz") + index := (diskNum - 1) % len(letters) + cycels := (diskNum - 1) % len(letters) + + return genName(cycels) + string(letters[index]) +} + +func LinkDisks(diskKind string, num int) (err error) { + disks, err := filepath.Glob(diskKind) + if err != nil { + fmt.Println("Error getting disks ", err) + return + } + + for _, disk := range disks { + diskNum, err := strconv.Atoi(disk[num:]) + if err != nil { + fmt.Println("Error geting disks names ", err) + return err + } + diskLink := fmt.Sprintf("%s/%s-sd%s", DIR, os.Getenv("V2V_vmName"), genName(diskNum+1)) + diskImgPath := disk + if diskKind == FS { + diskImgPath = fmt.Sprintf("%s/disk.img", disk) + } + if err = os.Symlink(diskImgPath, diskLink); err != nil { + fmt.Println("Error creating disk link ", err) + return err + } + } + return +} + +func executeVirtV2v(args []string) (err error) { + virtV2vCmd := exec.Command(args[0], args[1:]...) + virtV2vCmd.Stdout = os.Stdout + virtV2vCmd.Stderr = os.Stderr + + fmt.Println("exec ", virtV2vCmd) + if err = virtV2vCmd.Start(); err != nil { + fmt.Printf("Error executing command: %v\n", err) + return + } + + virtV2vMonitorCmd := exec.Command("/usr/local/bin/virt-v2v-monitor") + virtV2vMonitorCmd.Stdin, _ = virtV2vCmd.StdoutPipe() + virtV2vMonitorCmd.Stdout = os.Stdout + virtV2vMonitorCmd.Stderr = os.Stderr + + if err = virtV2vMonitorCmd.Start(); err != nil { + fmt.Printf("Error executing monitor command: %v\n", err) + return + } + + if err = virtV2vCmd.Wait(); err != nil { + fmt.Printf("Error waiting for virt-v2v to finish : %v\n", err) + return + } + return +} + +func getXMLFile(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.") +} + +func xmlHandler(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 + } + + xmlData, err := os.ReadFile(xmlFilePath) + if err != nil { + fmt.Printf("Error reading XML file: %v\n", err) + http.Error(w, "Error reading XML file", 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) + } + +} + +func shutdownHandler(w http.ResponseWriter, r *http.Request) { + fmt.Println("Shutdown request received. Shutting down server.") + err := server.Shutdown(context.Background()) + if err != nil { + fmt.Printf("Error shutting down server: %v\n", err) + } else { + fmt.Println("Server shut down successfully.") + } +}