Skip to content

Commit

Permalink
check if disks have os installed in lexical order (release) (#140)
Browse files Browse the repository at this point in the history
* check if disks have os installed in lexical order

* change guestfish command

* missing detach volume calls

* copy utils folder inside container

* fix unit tests

* fix detach calls

* cpu limit decrease

* change boot disk VMCreate

* add remaining disks

* windows

* setup oras in actions

* print netplan output
  • Loading branch information
OmkarDeshpande7 authored Jan 15, 2025
1 parent 8e9d4ef commit bbc70f9
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 47 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/packer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ jobs:
if: env.release_found == 'true'
id: validate
run: "packer validate ./image_builder/vjailbreak-image.pkr.hcl"


- name: setup-oras
uses: oras-project/[email protected]

- name: Download base image
if: env.release_found == 'true'
run: |
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
SHELL := /bin/bash

export REPO ?= platform9
export TAG ?= latest
export UI_IMG ?= ${REPO}/vjailbreak-ui:${TAG}
Expand All @@ -14,6 +16,10 @@ v2v-helper:
docker build --platform linux/amd64 -t $(V2V_IMG) v2v-helper/
docker push $(V2V_IMG)

.PHONY: test-v2v-helper
test-v2v-helper:
cd v2v-helper && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go test ./... -v

.PHONY: vjail-controller
vjail-controller: v2v-helper
make -C k8s/migration/ docker-build docker-push
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ func (r *MigrationPlanReconciler) CreatePod(ctx context.Context,
corev1.ResourceEphemeralStorage: resource.MustParse("200Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("4000m"),
corev1.ResourceCPU: resource.MustParse("2000m"),
corev1.ResourceMemory: resource.MustParse("5Gi"),
corev1.ResourceEphemeralStorage: resource.MustParse("2Gi"),
},
Expand Down
1 change: 1 addition & 0 deletions v2v-helper/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ COPY reporter/ reporter/
COPY vcenter/ vcenter/
COPY vm/ vm/
COPY virtv2v/ virtv2v/
COPY utils/ utils/

# Test
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go test ./... -v
Expand Down
132 changes: 94 additions & 38 deletions v2v-helper/migrate/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import (
"log"
"net/http"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"time"
"vjailbreak/openstack"

"vjailbreak/nbd"
"vjailbreak/utils"
"vjailbreak/vcenter"
"vjailbreak/virtv2v"
"vjailbreak/vm"
Expand Down Expand Up @@ -73,7 +75,7 @@ func (migobj *Migrate) CreateVolumes(vminfo vm.VMInfo) (vm.VMInfo, error) {
return vminfo, fmt.Errorf("failed to create volume: %s", err)
}
vminfo.VMDisks[idx].OpenstackVol = volume
if idx == 0 {
if vminfo.VMDisks[idx].Boot {
err = openstackops.SetVolumeBootable(volume)
if err != nil {
return vminfo, fmt.Errorf("failed to set volume as bootable: %s", err)
Expand Down Expand Up @@ -374,65 +376,100 @@ func (migobj *Migrate) LiveReplicateDisks(ctx context.Context, vminfo vm.VMInfo)

func (migobj *Migrate) ConvertVolumes(ctx context.Context, vminfo vm.VMInfo) error {
migobj.logMessage("Converting disk")
path, err := migobj.AttachVolume(vminfo.VMDisks[0])
if err != nil {
return fmt.Errorf("failed to attach volume: %s", err)
}
osRelease := ""
if vminfo.OSType == "linux" {
osRelease, err = virtv2v.GetOsRelease(path)
bootVolumeIndex := 0
getBootCommand := ""

if vminfo.OSType == "windows" {
getBootCommand = "ls /Windows"
} else if vminfo.OSType == "linux" {
getBootCommand = "ls /boot"
} else {
getBootCommand = "inspect-os"
}

for idx, _ := range vminfo.VMDisks {
path, err := migobj.AttachVolume(vminfo.VMDisks[idx])
if err != nil {
return fmt.Errorf("failed to get os release: %s", err)
return fmt.Errorf("failed to attach volume: %s", err)
}
}
if migobj.Convert {
firstbootscripts := []string{}
// Fix NTFS
if vminfo.OSType == "windows" {
err = virtv2v.NTFSFix(path)
ans, err := RunCommandInGuest(path, getBootCommand)
if err != nil {
fmt.Printf("Error running '%s'. Error: '%s', Output: %s\n", getBootCommand, err, ans)
detachError := migobj.DetachVolume(vminfo.VMDisks[idx])
if detachError != nil {
return fmt.Errorf("failed to detach volume: %s", detachError)
}
continue
}

fmt.Printf("Output from '%s' - '%s'\n", getBootCommand, ans)

if ans == "" {
err := migobj.DetachVolume(vminfo.VMDisks[idx])
if err != nil {
return fmt.Errorf("failed to run ntfsfix: %s", err)
return fmt.Errorf("failed to detach volume: %s", err)
}
continue
}
// Turn on DHCP for interfaces in rhel VMs

if vminfo.OSType == "linux" {
if strings.Contains(osRelease, "rhel") {
firstbootscriptname := "rhel_enable_dhcp"
firstbootscript := `#!/bin/bash
nmcli -t -f NAME connection show | while read -r conn; do
nmcli con modify "$conn" ipv4.method auto ipv4.address "" ipv4.gateway ""
nmcli con modify "$conn" ipv6.method auto ipv6.address "" ipv6.gateway ""
nmcli con reload
nmcli con down "$conn"
nmcli con up "$conn"
done
systemctl enable --now [email protected]`
firstbootscripts = append(firstbootscripts, firstbootscriptname)
err = virtv2v.AddFirstBootScript(firstbootscript, firstbootscriptname)
osRelease, err = virtv2v.GetOsRelease(path)
if err != nil {
return fmt.Errorf("failed to get os release: %s", err)
}
}

// save the index of bootVolume
bootVolumeIndex = idx
log.Printf("Setting up boot volume as: %s", vminfo.VMDisks[bootVolumeIndex].Name)

vminfo.VMDisks[bootVolumeIndex].Boot = true
if migobj.Convert {
firstbootscripts := []string{}
// Fix NTFS
if vminfo.OSType == "windows" {
err = virtv2v.NTFSFix(path)
if err != nil {
return fmt.Errorf("failed to add first boot script: %s", err)
return fmt.Errorf("failed to run ntfsfix: %s", err)
}
}
}
err := virtv2v.ConvertDisk(ctx, path, vminfo.OSType, migobj.Virtiowin, firstbootscripts)
if err != nil {
return fmt.Errorf("failed to run virt-v2v: %s", err)
// Turn on DHCP for interfaces in rhel VMs
if vminfo.OSType == "linux" {
if strings.Contains(osRelease, "rhel") {
firstbootscriptname := "rhel_enable_dhcp"
firstbootscript := utils.RhelFirstBootScript
firstbootscripts = append(firstbootscripts, firstbootscriptname)
err = virtv2v.AddFirstBootScript(firstbootscript, firstbootscriptname)
if err != nil {
return fmt.Errorf("failed to add first boot script: %s", err)
}
}
}
err := virtv2v.ConvertDisk(ctx, path, vminfo.OSType, migobj.Virtiowin, firstbootscripts)
if err != nil {
return fmt.Errorf("failed to run virt-v2v: %s", err)
}
openstackops := migobj.Openstackclients
err = openstackops.SetVolumeBootable(vminfo.VMDisks[bootVolumeIndex].OpenstackVol)
if err != nil {
return fmt.Errorf("failed to set volume as bootable: %s", err)
}
}
}

//TODO(omkar): can disable DHCP here
if vminfo.OSType == "linux" {
if strings.Contains(osRelease, "ubuntu") {
// Add Wildcard Netplan
log.Println("Adding wildcard netplan")
err := virtv2v.AddWildcardNetplan(path)
err := virtv2v.AddWildcardNetplan(vminfo.VMDisks[bootVolumeIndex].Path)
if err != nil {
return fmt.Errorf("failed to add wildcard netplan: %s", err)
}
log.Println("Wildcard netplan added successfully")
}
}
err = migobj.DetachVolume(vminfo.VMDisks[0])
err := migobj.DetachVolume(vminfo.VMDisks[bootVolumeIndex])
if err != nil {
return fmt.Errorf("failed to detach volume: %s", err)
}
Expand Down Expand Up @@ -660,7 +697,7 @@ func (migobj *Migrate) MigrateVM(ctx context.Context) error {
return fmt.Errorf("number of mac addresses does not match number of network names")
}

// Graceful Termination
// Graceful Termination clean-up volumes and snapshots
go migobj.gracefulTerminate(vminfo, cancel)

// Create and Add Volumes to Host
Expand Down Expand Up @@ -719,3 +756,22 @@ func (migobj *Migrate) cleanup(vminfo vm.VMInfo) {
log.Printf("Failed to delete snapshot of source VM: %s\n", err)
}
}

// Runs command inside temporary qemu-kvm that virt-v2v creates
func RunCommandInGuest(path string, command string) (string, error) {
// Get the os-release file
os.Setenv("LIBGUESTFS_BACKEND", "direct")
cmd := exec.Command(
"guestfish",
"--ro",
"-a",
path,
"-i")
cmd.Stdin = strings.NewReader(command)
log.Printf("Executing %s", cmd.String()+" "+command)
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to run command (%s): %v", command, err)
}
return strings.ToLower(string(out)), nil
}
2 changes: 1 addition & 1 deletion v2v-helper/migrate/migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestCreateVolumes(t *testing.T) {
mockOpenStackOps.EXPECT().
SetVolumeBootable(&volumes.Volume{ID: "id1", Name: "test-vm-disk1"}).
Return(nil).
Times(1)
AnyTimes()
gomock.InOrder(
mockOpenStackOps.EXPECT().AttachVolumeToVM("id1").Return(nil).Times(1),
mockOpenStackOps.EXPECT().AttachVolumeToVM("id2").Return(nil).Times(1),
Expand Down
18 changes: 16 additions & 2 deletions v2v-helper/openstack/openstackops.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,11 +495,25 @@ func (osclient *OpenStackClients) CreatePort(network *networks.Network, mac, ip,
}

func (osclient *OpenStackClients) CreateVM(flavor *flavors.Flavor, networkIDs, portIDs []string, vminfo vm.VMInfo) (*servers.Server, error) {

uuid := ""
bootableDiskIndex := 0
for idx, disk := range vminfo.VMDisks {
if disk.Boot {
uuid = disk.OpenstackVol.ID
bootableDiskIndex = idx
break
}
}
if uuid == "" {
return nil, fmt.Errorf("unable to determine boot volume for VM: %s", vminfo.Name)
}

blockDevice := bootfromvolume.BlockDevice{
DeleteOnTermination: false,
DestinationType: bootfromvolume.DestinationVolume,
SourceType: bootfromvolume.SourceVolume,
UUID: vminfo.VMDisks[0].OpenstackVol.ID,
UUID: uuid,
}
// Create the server
openstacknws := []servers.Network{}
Expand Down Expand Up @@ -542,7 +556,7 @@ func (osclient *OpenStackClients) CreateVM(flavor *flavors.Flavor, networkIDs, p

log.Println("Attaching Additional Disks")

for _, disk := range vminfo.VMDisks[1:] {
for _, disk := range append(vminfo.VMDisks[:bootableDiskIndex], vminfo.VMDisks[bootableDiskIndex+1:]...) {
_, err := volumeattach.Create(osclient.ComputeClient, server.ID, volumeattach.CreateOpts{
VolumeID: disk.OpenstackVol.ID,
DeleteOnTermination: false,
Expand Down
13 changes: 13 additions & 0 deletions v2v-helper/utils/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package utils

const (
RhelFirstBootScript = `#!/bin/bash
nmcli -t -f NAME connection show | while read -r conn; do
nmcli con modify "$conn" ipv4.method auto ipv4.address "" ipv4.gateway ""
nmcli con modify "$conn" ipv6.method auto ipv6.address "" ipv6.gateway ""
nmcli con reload
nmcli con down "$conn"
nmcli con up "$conn"
done
systemctl enable --now [email protected]`
)
7 changes: 3 additions & 4 deletions v2v-helper/virtv2v/virtv2vops.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ func ConvertDisk(ctx context.Context, path, ostype, virtiowindriver string, firs
log.Println("Downloaded virtio windrivers")
defer os.Remove(filePath)
os.Setenv("VIRTIO_WIN", filePath)

}
os.Setenv("LIBGUESTFS_BACKEND", "direct")
args := []string{"--firstboot", "/home/fedora/scripts/user_firstboot.sh"}
Expand Down Expand Up @@ -165,7 +164,7 @@ func GetOsRelease(path string) (string, error) {
log.Printf("Executing %s", cmd.String()+" "+input)
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get os-release: %s", err)
return "", fmt.Errorf("failed to get os-release: %s, %s", out, err)
}
return strings.ToLower(string(out)), nil
}
Expand Down Expand Up @@ -196,9 +195,9 @@ DHCP=yes`
input := `upload /home/fedora/99-wildcard.network /etc/systemd/network/99-wildcard.network`
cmd.Stdin = strings.NewReader(input)
log.Printf("Executing %s", cmd.String()+" "+input)
err = cmd.Run()
ans, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to upload netplan file: %s", err)
return fmt.Errorf("failed to upload netplan file: %s, Output: %s", err, ans)
}
return nil
}
Expand Down
1 change: 1 addition & 0 deletions v2v-helper/vm/vmops.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type VMDisk struct {
Snapname string
SnapBackingDisk string
ChangeID string
Boot bool
}

type VMOps struct {
Expand Down

0 comments on commit bbc70f9

Please sign in to comment.