Skip to content

Commit 3edff34

Browse files
committed
chore(hyperv): Build the Hyper-V images via qemu
This is alternative implementation of Vagrant for Hyper-V images building using Packer + Qemu. This doesn't require real Hyper-V provider running under Windows. Packer builds Qemu guest with given RAW disk image. The imager is converted into VHDX and packed into Vagranto for Hyper-V box. The shell-local post-processor creates the box file. Because of that an additional post-processors are added into Packer .hcl configuration files and AlmaLinux all versions. The tpl/vagrant/hyperv/box.xml Vagrant box config for Hyper-V is used. Created a new Ansible playbook that configures guest AlmaLinux OS (under Qemu guest) for Hyper-V. The "Install Hyper-V dependencies" Ansible tasks extended with new "Delete qemu-guest-agent package" one. Introduce new almalinux-8.hyperv-x86_64.ks (copy of almalinux-8.vagrant-x86_64.ks) to boot Qemu guest and create Vagrant for Hyper-V image. The kickstart has extra step to rebuild initramfs to include Hyper-V specific kernel modules (hv_*.ko*) into it. Packer variables extended with 'hyperv_boot_command_*_x86_64' ones.
1 parent 3e82c08 commit 3edff34

File tree

11 files changed

+735
-6
lines changed

11 files changed

+735
-6
lines changed

.github/actions/shared-steps/action.yml

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,28 @@ runs:
111111
output_mask=output-${packer_source}/AlmaLinux-*${{ env.version_major }}*.${{ env.alma_arch }}.raw
112112
packer_source=qemu.${packer_source}
113113
;;
114+
hyperv8)
115+
packer_source=almalinux-${{ env.version_major }}-hyperv-${{ env.alma_arch }}
116+
output_mask=AlmaLinux-${{ env.version_major }}-Vagrant-*.${{ env.alma_arch }}.hyperv.box
117+
packer_source=qemu.${packer_source}
118+
;;
119+
hyperv9)
120+
output_mask=AlmaLinux-${{ env.version_major }}-Vagrant-hyperv-*.${{ env.alma_arch }}.box
121+
packer_source=qemu.${packer_source}
122+
;;
123+
hyperv10)
124+
packer_source=almalinux_10_vagrant_hyperv_${{ env.alma_arch }}
125+
[[ ${{ env.version_major }} == *"v2"* ]] && packer_source="${packer_source}_v2"
126+
output_mask=AlmaLinux-${{ env.version_major }}-Vagrant-hyperv-*.${{ env.alma_arch }}.box
127+
packer_source=qemu.${packer_source}
128+
;;
129+
hyperv*kitten*)
130+
packer_source=almalinux_kitten_10_vagrant_hyperv_${{ env.alma_arch }}
131+
[[ ${{ env.version_major }} == *"v2"* ]] && packer_source="${packer_source}_v2"
132+
output_mask=AlmaLinux-Kitten-Vagrant-hyperv-10-*.${{ env.alma_arch }}.box
133+
aws_s3_path=images/kitten/10/vagrant/${{ env.TIME_STAMP }}
134+
packer_source=qemu.${packer_source}
135+
;;
114136
azure*kitten*)
115137
packer_source=almalinux_kitten_10_${{ inputs.type }}_${{ env.alma_arch }}
116138
[[ ${{ env.version_major }} == *"v2"* ]] && packer_source="${packer_source}_v2"
@@ -394,9 +416,20 @@ runs:
394416
esac
395417
396418
# Image file format: raw or qcow2
419+
# Image file name is redefined for the Hyper-V, as it is build using Qemu and converted to .box file
397420
case ${{ inputs.type }} in
398-
oci|gencloud|opennebula) format=qcow2 ;;
399-
azure) format=raw ;;
421+
oci|gencloud|opennebula)
422+
format=qcow2
423+
image_file=${{ env.IMAGE_FILE }}
424+
;;
425+
hyperv)
426+
format=raw
427+
image_file=$(echo ${{ env.IMAGE_FILE }} | sed 's/\.box$/.raw/')
428+
;;
429+
azure)
430+
format=raw
431+
image_file=${{ env.IMAGE_FILE }}
432+
;;
400433
*) false ;;
401434
esac
402435
rootfs_path=/mnt/rootfs
@@ -411,14 +444,14 @@ runs:
411444
sudo modprobe nbd max_part=8
412445
413446
# Make a copy of the image file
414-
sudo cp ${{ env.IMAGE_FILE }} $(dirname ${rootfs_path})
447+
sudo cp ${image_file} $(dirname ${rootfs_path})
415448
416449
# Attach the image file to the nbd device
417450
sudo qemu-nbd \
418451
--read-only \
419452
--format=${format} \
420453
--connect=/dev/nbd0 \
421-
$(dirname ${rootfs_path})/$(basename ${{ env.IMAGE_FILE }}) \
454+
$(dirname ${rootfs_path})/$(basename ${image_file}) \
422455
&& sleep 10 || false
423456
424457
# Mount need partition

.github/workflows/build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ on:
3131
- azure
3232
# - digitalocean # TODO: require data to work with the cloud, such as: bucket, access key, secret key, etc.
3333
- gencloud
34+
- hyperv
3435
- oci
3536
- opennebula
3637

@@ -129,6 +130,9 @@ jobs:
129130
VARIANTS_GH+=("opennebula-x86_64")
130131
VARIANTS_SH+=("opennebula-aarch64")
131132
fi
133+
if [ "${{ inputs.image_type }}" = "hyperv" -o "${{ inputs.image_type }}" = "ALL" ]; then
134+
VARIANTS_GH+=("hyperv-x86_64")
135+
fi
132136
133137
# Vagrant Images
134138
if [ "${{ inputs.vagrant_type }}" = "vagrant_libvirt" -o "${{ inputs.vagrant_type }}" = "ALL" ]; then

almalinux-8-vagrant.pkr.hcl

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,37 @@ source "hyperv-iso" "almalinux-8" {
7878
headless = var.headless
7979
}
8080

81+
source "qemu" "almalinux-8-hyperv-x86_64" {
82+
iso_url = local.iso_url_8_x86_64
83+
iso_checksum = local.iso_checksum_8_x86_64
84+
http_directory = var.http_directory
85+
shutdown_command = var.vagrant_shutdown_command
86+
ssh_username = var.vagrant_ssh_username
87+
ssh_password = var.vagrant_ssh_password
88+
ssh_timeout = var.ssh_timeout
89+
boot_command = var.hyperv_boot_command_8_x86_64
90+
boot_wait = var.boot_wait
91+
accelerator = "kvm"
92+
disk_interface = "virtio-scsi"
93+
disk_size = var.vagrant_disk_size
94+
disk_cache = "unsafe"
95+
disk_discard = "unmap"
96+
disk_detect_zeroes = "unmap"
97+
format = "raw"
98+
headless = var.headless
99+
machine_type = "q35"
100+
memory = var.memory_x86_64
101+
net_device = "virtio-net"
102+
qemu_binary = var.qemu_binary
103+
vm_name = "almalinux-8-hyperv-x86_64.raw"
104+
cpu_model = "host"
105+
cpus = var.cpus
106+
efi_boot = true
107+
efi_firmware_code = var.ovmf_code
108+
efi_firmware_vars = var.ovmf_vars
109+
efi_drop_efivars = true
110+
}
111+
81112
source "vmware-iso" "almalinux-8" {
82113
iso_url = local.iso_url_8_x86_64
83114
iso_checksum = local.iso_checksum_8_x86_64
@@ -129,6 +160,7 @@ build {
129160
"source.qemu.almalinux-8",
130161
"source.virtualbox-iso.almalinux-8",
131162
"source.hyperv-iso.almalinux-8",
163+
"source.qemu.almalinux-8-hyperv-x86_64",
132164
"source.vmware-iso.almalinux-8",
133165
"source.parallels-iso.almalinux-8",
134166
]
@@ -177,10 +209,34 @@ build {
177209
only = ["hyperv-iso.almalinux-8"]
178210
}
179211

212+
provisioner "ansible" {
213+
user = "vagrant"
214+
use_proxy = false
215+
galaxy_file = "./ansible/requirements.yml"
216+
galaxy_force_install = true
217+
collections_path = "./ansible/collections"
218+
roles_path = "./ansible/roles"
219+
playbook_file = "./ansible/hyperv.yml"
220+
ansible_env_vars = [
221+
"ANSIBLE_PIPELINING=True",
222+
"ANSIBLE_REMOTE_TEMP=/tmp",
223+
"ANSIBLE_SCP_EXTRA_ARGS=-O",
224+
"ANSIBLE_HOST_KEY_CHECKING=False",
225+
]
226+
extra_arguments = [
227+
"--extra-vars",
228+
"packer_provider=${source.type} ansible_ssh_pass=vagrant is_unified_boot=true",
229+
]
230+
only = ["qemu.almalinux-8-hyperv-x86_64"]
231+
}
232+
180233
provisioner "shell" {
181234
expect_disconnect = true
182235
inline = ["sudo rm -fr /etc/ssh/*host*key*"]
183-
only = ["hyperv-iso.almalinux-8"]
236+
only = [
237+
"hyperv-iso.almalinux-8",
238+
"qemu.almalinux-8-hyperv-x86_64",
239+
]
184240
}
185241

186242
provisioner "ansible" {
@@ -220,5 +276,31 @@ build {
220276
"parallels-iso.almalinux-8",
221277
]
222278
}
279+
280+
# The shell-local post-processor creates the box file for the Hyper-V, with the following content:
281+
# - Virtual Machines/box.xml, copied from the templated file
282+
# - Virtual Hard Disks/almalinux.vhdx, converted with qemu-img from the raw image
283+
# - Vagrantfile, empty file
284+
# - metadata.json, with the architecture and provider information
285+
#
286+
# These files are packed by the tar+gzip to match Hyper-V box file format
287+
#
288+
# The predefined vagrant post-processor is not used, because it skips 'Virtual Machines' and 'Virtual Hard Disks' directories.
289+
#
290+
# The raw image is not used by the Hyper-V, but it is kept for the GitHub Actions workflow to extract packages list from it
291+
#
292+
post-processor "shell-local" {
293+
inline = [
294+
"mkdir -p '${source.name}/Virtual Machines' '${source.name}/Virtual Hard Disks'",
295+
"touch ${source.name}/Vagrantfile",
296+
"echo '{\"architecture\":\"amd64\",\"provider\":\"hyperv\"}' > ${source.name}/metadata.json",
297+
"cp -a ./tpl/vagrant/hyperv/box.xml '${source.name}/Virtual Machines/box.xml'",
298+
"qemu-img convert -f raw -O vhdx output-${source.name}/${source.name}.raw '${source.name}/Virtual Hard Disks/almalinux.vhdx'",
299+
"cd ${source.name}",
300+
"tar --use-compress-program='gzip -9' -cvf ../AlmaLinux-8-Vagrant-${var.os_ver_8}-${formatdate("YYYYMMDD", timestamp())}.x86_64.hyperv.box .",
301+
"mv ../output-${source.name}/${source.name}.raw ../AlmaLinux-8-Vagrant-${var.os_ver_8}-${formatdate("YYYYMMDD", timestamp())}.x86_64.hyperv.raw"
302+
]
303+
only = ["qemu.almalinux-8-hyperv-x86_64"]
304+
}
223305
}
224306
}

almalinux-9-vagrant.pkr.hcl

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,37 @@ source "hyperv-iso" "almalinux-9" {
7878
headless = var.headless
7979
}
8080

81+
source "qemu" "almalinux-9-hyperv-x86_64" {
82+
iso_url = local.iso_url_9_x86_64
83+
iso_checksum = local.iso_checksum_9_x86_64
84+
http_directory = var.http_directory
85+
shutdown_command = var.vagrant_shutdown_command
86+
ssh_username = var.vagrant_ssh_username
87+
ssh_password = var.vagrant_ssh_password
88+
ssh_timeout = var.ssh_timeout
89+
boot_command = var.hyperv_boot_command_9_x86_64
90+
boot_wait = var.boot_wait
91+
accelerator = "kvm"
92+
disk_interface = "virtio-scsi"
93+
disk_size = var.vagrant_disk_size
94+
disk_cache = "unsafe"
95+
disk_discard = "unmap"
96+
disk_detect_zeroes = "unmap"
97+
format = "raw"
98+
headless = var.headless
99+
machine_type = "q35"
100+
memory = var.memory_x86_64
101+
net_device = "virtio-net"
102+
qemu_binary = var.qemu_binary
103+
vm_name = "almalinux-9-hyperv-x86_64.raw"
104+
cpu_model = "host"
105+
cpus = var.cpus
106+
efi_boot = true
107+
efi_firmware_code = var.ovmf_code
108+
efi_firmware_vars = var.ovmf_vars
109+
efi_drop_efivars = true
110+
}
111+
81112
source "vmware-iso" "almalinux-9" {
82113
iso_url = local.iso_url_9_x86_64
83114
iso_checksum = local.iso_checksum_9_x86_64
@@ -193,6 +224,7 @@ build {
193224
"source.qemu.almalinux-9",
194225
"source.virtualbox-iso.almalinux-9",
195226
"source.hyperv-iso.almalinux-9",
227+
"source.qemu.almalinux-9-hyperv-x86_64",
196228
"source.vmware-iso.almalinux-9",
197229
"source.parallels-iso.almalinux-9",
198230
"source.virtualbox-iso.almalinux-9-aarch64",
@@ -248,10 +280,34 @@ build {
248280
only = ["hyperv-iso.almalinux-9"]
249281
}
250282

283+
provisioner "ansible" {
284+
user = "vagrant"
285+
use_proxy = false
286+
galaxy_file = "./ansible/requirements.yml"
287+
galaxy_force_install = true
288+
collections_path = "./ansible/collections"
289+
roles_path = "./ansible/roles"
290+
playbook_file = "./ansible/hyperv.yml"
291+
ansible_env_vars = [
292+
"ANSIBLE_PIPELINING=True",
293+
"ANSIBLE_REMOTE_TEMP=/tmp",
294+
"ANSIBLE_SCP_EXTRA_ARGS=-O",
295+
"ANSIBLE_HOST_KEY_CHECKING=False",
296+
]
297+
extra_arguments = [
298+
"--extra-vars",
299+
"packer_provider=${source.type} ansible_ssh_pass=vagrant",
300+
]
301+
only = ["qemu.almalinux-9-hyperv-x86_64"]
302+
}
303+
251304
provisioner "shell" {
252305
expect_disconnect = true
253306
inline = ["sudo rm -fr /etc/ssh/*host*key*"]
254-
only = ["hyperv-iso.almalinux-9"]
307+
only = [
308+
"hyperv-iso.almalinux-9",
309+
"qemu.almalinux-9-hyperv-x86_64",
310+
]
255311
}
256312

257313
post-processors {
@@ -283,5 +339,31 @@ build {
283339
output = "AlmaLinux-9-Vagrant-{{.Provider}}-${var.os_ver_9}-${formatdate("YYYYMMDD", timestamp())}.x86_64.box"
284340
only = ["qemu.almalinux-9"]
285341
}
342+
343+
# The shell-local post-processor creates the box file for the Hyper-V, with the following content:
344+
# - Virtual Machines/box.xml, copied from the templated file
345+
# - Virtual Hard Disks/almalinux.vhdx, converted with qemu-img from the raw image
346+
# - Vagrantfile, empty file
347+
# - metadata.json, with the architecture and provider information
348+
#
349+
# These files are packed by the tar+gzip to match Hyper-V box file format
350+
#
351+
# The predefined vagrant post-processor is not used, because it skips 'Virtual Machines' and 'Virtual Hard Disks' directories.
352+
#
353+
# The raw image is not used by the Hyper-V, but it is kept for the GitHub Actions workflow to extract packages list from it
354+
#
355+
post-processor "shell-local" {
356+
inline = [
357+
"mkdir -p '${source.name}/Virtual Machines' '${source.name}/Virtual Hard Disks'",
358+
"touch ${source.name}/Vagrantfile",
359+
"echo '{\"architecture\":\"amd64\",\"provider\":\"hyperv\"}' > ${source.name}/metadata.json",
360+
"cp -a ./tpl/vagrant/hyperv/box.xml '${source.name}/Virtual Machines/box.xml'",
361+
"qemu-img convert -f raw -O vhdx output-${source.name}/${source.name}.raw '${source.name}/Virtual Hard Disks/almalinux.vhdx'",
362+
"cd ${source.name}",
363+
"tar --use-compress-program='gzip -9' -cvf ../AlmaLinux-9-Vagrant-hyperv-${var.os_ver_9}-${formatdate("YYYYMMDD", timestamp())}.x86_64.box .",
364+
"mv ../output-${source.name}/${source.name}.raw ../AlmaLinux-9-Vagrant-hyperv-${var.os_ver_9}-${formatdate("YYYYMMDD", timestamp())}.x86_64.raw"
365+
]
366+
only = ["qemu.almalinux-9-hyperv-x86_64"]
367+
}
286368
}
287369
}

0 commit comments

Comments
 (0)