@underpostnet/underpost 2.95.8 → 2.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ packer {
2
+ required_version = ">= 1.11.0"
3
+ required_plugins {
4
+ qemu = {
5
+ version = ">= 1.1.0, < 1.1.2"
6
+ source = "github.com/hashicorp/qemu"
7
+ }
8
+ }
9
+ }
10
+
11
+ variable "filename" {
12
+ type = string
13
+ default = "rocky9.tar.gz"
14
+ description = "The filename of the tarball to produce"
15
+ }
16
+
17
+ variable ks_proxy {
18
+ type = string
19
+ default = "${env("KS_PROXY")}"
20
+ }
21
+
22
+ variable ks_mirror {
23
+ type = string
24
+ default = "${env("KS_MIRROR")}"
25
+ }
26
+
27
+ variable "timeout" {
28
+ type = string
29
+ default = "1h"
30
+ description = "Timeout for building the image"
31
+ }
32
+
33
+ variable "architecture" {
34
+ type = string
35
+ default = "amd64"
36
+ description = "The architecture to build the image for (amd64 or arm64)"
37
+ }
38
+
39
+ variable "host_is_arm" {
40
+ type = bool
41
+ default = false
42
+ description = "The host architecture is aarch64"
43
+ }
44
+
45
+ variable "ovmf_suffix" {
46
+ type = string
47
+ default = ""
48
+ description = "Suffix for OVMF CODE and VARS files. Newer systems such as Noble use _4M."
49
+ }
50
+
51
+ variable "headless" {
52
+ type = bool
53
+ default = true
54
+ description = "Run packer in headless mode"
55
+ }
56
+
57
+ locals {
58
+ iso_arch_map = {
59
+ "amd64" = "x86_64"
60
+ "x86_64" = "x86_64"
61
+ "arm64" = "aarch64"
62
+ "aarch64" = "aarch64"
63
+ }
64
+ iso_arch = lookup(local.iso_arch_map, var.architecture, "x86_64")
65
+
66
+ qemu_arch = {
67
+ "amd64" = "x86_64"
68
+ "x86_64" = "x86_64"
69
+ "arm64" = "aarch64"
70
+ "aarch64" = "aarch64"
71
+ }
72
+ uefi_imp = {
73
+ "amd64" = "OVMF"
74
+ "x86_64" = "OVMF"
75
+ "arm64" = "AAVMF"
76
+ "aarch64" = "AAVMF"
77
+ }
78
+ uefi_sfx = {
79
+ "amd64" = "${var.ovmf_suffix}"
80
+ "x86_64" = "${var.ovmf_suffix}"
81
+ "arm64" = ""
82
+ "aarch64" = ""
83
+ }
84
+ qemu_machine = {
85
+ "amd64" = "accel=kvm"
86
+ "x86_64" = "accel=kvm"
87
+ "arm64" = var.host_is_arm ? "virt,accel=kvm" : "virt"
88
+ "aarch64" = var.host_is_arm ? "virt,accel=kvm" : "virt"
89
+ }
90
+ qemu_cpu = {
91
+ "amd64" = "host"
92
+ "x86_64" = "host"
93
+ "arm64" = var.host_is_arm ? "host" : "max"
94
+ "aarch64" = var.host_is_arm ? "host" : "max"
95
+ }
96
+
97
+ ks_proxy = var.ks_proxy != "" ? "--proxy=${var.ks_proxy}" : ""
98
+ ks_os_repos = var.ks_mirror != "" ? "--url=${var.ks_mirror}/BaseOS/${local.iso_arch}/os" : "--mirrorlist='http://mirrors.rockylinux.org/mirrorlist?arch=${local.iso_arch}&repo=BaseOS-9'"
99
+ ks_appstream_repos = var.ks_mirror != "" ? "--baseurl=${var.ks_mirror}/AppStream/${local.iso_arch}/os" : "--mirrorlist='https://mirrors.rockylinux.org/mirrorlist?release=9&arch=${local.iso_arch}&repo=AppStream-9'"
100
+ ks_extras_repos = var.ks_mirror != "" ? "--baseurl=${var.ks_mirror}/extras/${local.iso_arch}/os" : "--mirrorlist='https://mirrors.rockylinux.org/mirrorlist?arch=${local.iso_arch}&repo=extras-9'"
101
+ }
102
+
103
+ source "qemu" "rocky9" {
104
+ boot_command = ["<up><wait>", "e", "<down><down><down><left>", " console=ttyS0 inst.cmdline inst.text inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/rocky9.ks <f10>"]
105
+ boot_wait = "5s"
106
+ communicator = "none"
107
+ disk_size = "45G"
108
+ format = "qcow2"
109
+ headless = var.headless
110
+ iso_checksum = "file:http://download.rockylinux.org/pub/rocky/9/isos/${local.iso_arch}/CHECKSUM"
111
+ iso_url = "http://download.rockylinux.org/pub/rocky/9/isos/${local.iso_arch}/Rocky-9-latest-${local.iso_arch}-boot.iso"
112
+ iso_target_path = "packer_cache/Rocky-9-latest-${local.iso_arch}-boot.iso"
113
+ memory = 2048
114
+ cores = 4
115
+ qemu_binary = "qemu-system-${lookup(local.qemu_arch, var.architecture, "")}"
116
+ qemuargs = [
117
+ ["-serial", "stdio"],
118
+ ["-boot", "strict=off"],
119
+ ["-device", "qemu-xhci"],
120
+ ["-device", "usb-kbd"],
121
+ ["-device", "virtio-net-pci,netdev=net0"],
122
+ ["-netdev", "user,id=net0"],
123
+ ["-device", "virtio-blk-pci,drive=drive0,bootindex=0"],
124
+ ["-device", "virtio-blk-pci,drive=cdrom0,bootindex=1"],
125
+ ["-machine", "${lookup(local.qemu_machine, var.architecture, "")}"],
126
+ ["-cpu", "${lookup(local.qemu_cpu, var.architecture, "")}"],
127
+ ["-device", "virtio-gpu-pci"],
128
+ ["-global", "driver=cfi.pflash01,property=secure,value=off"],
129
+ ["-drive", "if=pflash,format=raw,unit=0,id=ovmf_code,readonly=on,file=/usr/share/${lookup(local.uefi_imp, var.architecture, "")}/${lookup(local.uefi_imp, var.architecture, "")}_CODE${lookup(local.uefi_sfx, var.architecture, "")}.fd"],
130
+ ["-drive", "if=pflash,format=raw,unit=1,id=ovmf_vars,file=${local.iso_arch}_VARS.fd"],
131
+ ["-drive", "file=output-rocky9/packer-rocky9,if=none,id=drive0,cache=writeback,discard=ignore,format=qcow2"],
132
+ ["-drive", "file=packer_cache/Rocky-9-latest-${local.iso_arch}-boot.iso,if=none,id=cdrom0,media=cdrom"]
133
+ ]
134
+ shutdown_timeout = var.timeout
135
+ http_content = {
136
+ "/rocky9.ks" = templatefile("${path.root}/http/rocky9.ks.pkrtpl.hcl",
137
+ {
138
+ KS_PROXY = local.ks_proxy,
139
+ KS_OS_REPOS = local.ks_os_repos,
140
+ KS_APPSTREAM_REPOS = local.ks_appstream_repos,
141
+ KS_EXTRAS_REPOS = local.ks_extras_repos
142
+ }
143
+ )
144
+ }
145
+ }
146
+
147
+ build {
148
+ sources = ["source.qemu.rocky9"]
149
+
150
+ post-processor "shell-local" {
151
+ inline = [
152
+ "SOURCE=${source.name}",
153
+ "OUTPUT=${var.filename}",
154
+ "source ../../scripts/fuse-nbd",
155
+ "source ../../scripts/fuse-tar-root",
156
+ "rm -rf output-${source.name}",
157
+ ]
158
+ inline_shebang = "/bin/bash -e"
159
+ }
160
+ }
@@ -0,0 +1,64 @@
1
+ #!/bin/bash -e
2
+ # vi: ts=4 expandtab
3
+ #
4
+ # Script to mount a qcow2 image using NBD (Network Block Device)
5
+ # This is part of the MAAS image build process
6
+
7
+ OUTPUT_DIR="output-${SOURCE}"
8
+ OUTPUT_QCOW2="${OUTPUT_DIR}/packer-${SOURCE}"
9
+
10
+ if [ ! -f "${OUTPUT_QCOW2}" ]; then
11
+ echo "ERROR: ${OUTPUT_QCOW2} not found"
12
+ exit 1
13
+ fi
14
+
15
+ # Load NBD kernel module if not already loaded
16
+ if ! lsmod | grep -q nbd; then
17
+ modprobe nbd
18
+ fi
19
+
20
+ # Find an available NBD device
21
+ for i in {0..15}; do
22
+ if [ ! -e "/sys/class/block/nbd${i}/pid" ]; then
23
+ NBD_DEV="/dev/nbd${i}"
24
+ break
25
+ fi
26
+ done
27
+
28
+ if [ -z "${NBD_DEV}" ]; then
29
+ echo "ERROR: No available NBD devices found"
30
+ exit 1
31
+ fi
32
+
33
+ # Connect the QCOW2 image to the NBD device
34
+ qemu-nbd -c "${NBD_DEV}" -f qcow2 -r "${OUTPUT_QCOW2}"
35
+
36
+ # Wait for the device to be ready
37
+ sleep 2
38
+ udevadm settle
39
+
40
+ # Find the root partition (usually the largest partition)
41
+ ROOT_PART=""
42
+ MAX_SIZE=0
43
+
44
+ for part in "${NBD_DEV}"p*; do
45
+ if [ -b "${part}" ]; then
46
+ SIZE=$(blockdev --getsize64 "${part}" 2>/dev/null || echo 0)
47
+ if [ "${SIZE}" -gt "${MAX_SIZE}" ]; then
48
+ MAX_SIZE=${SIZE}
49
+ ROOT_PART="${part}"
50
+ fi
51
+ fi
52
+ done
53
+
54
+ if [ -z "${ROOT_PART}" ]; then
55
+ # If no partitions found, try the whole device
56
+ ROOT_PART="${NBD_DEV}"
57
+ fi
58
+
59
+ echo "Using NBD device: ${NBD_DEV}"
60
+ echo "Root partition: ${ROOT_PART}"
61
+
62
+ # Export variables for use in subsequent scripts
63
+ export NBD_DEV
64
+ export ROOT_PART
@@ -0,0 +1,63 @@
1
+ #!/bin/bash -e
2
+ # vi: ts=4 expandtab
3
+ #
4
+ # Script to extract root filesystem from NBD device to tarball for MAAS
5
+ # This is part of the MAAS image build process
6
+
7
+ if [ -z "${NBD_DEV}" ]; then
8
+ echo "ERROR: NBD_DEV not set. Run fuse-nbd first."
9
+ exit 1
10
+ fi
11
+
12
+ if [ -z "${ROOT_PART}" ]; then
13
+ echo "ERROR: ROOT_PART not set. Run fuse-nbd first."
14
+ exit 1
15
+ fi
16
+
17
+ if [ -z "${OUTPUT}" ]; then
18
+ echo "ERROR: OUTPUT not set."
19
+ exit 1
20
+ fi
21
+
22
+ # Create temporary mount point
23
+ MOUNT_POINT=$(mktemp -d /tmp/packer-maas-mount.XXXXXX)
24
+
25
+ cleanup() {
26
+ echo "Cleaning up..."
27
+ if mountpoint -q "${MOUNT_POINT}"; then
28
+ umount "${MOUNT_POINT}" || true
29
+ fi
30
+ if [ -n "${NBD_DEV}" ] && [ -b "${NBD_DEV}" ]; then
31
+ qemu-nbd -d "${NBD_DEV}" || true
32
+ fi
33
+ if [ -d "${MOUNT_POINT}" ]; then
34
+ rmdir "${MOUNT_POINT}" || true
35
+ fi
36
+ }
37
+
38
+ trap cleanup EXIT
39
+
40
+ # Mount the root partition
41
+ echo "Mounting ${ROOT_PART} to ${MOUNT_POINT}..."
42
+ mount -o ro "${ROOT_PART}" "${MOUNT_POINT}"
43
+
44
+ # Create tarball from the mounted filesystem
45
+ echo "Creating tarball ${OUTPUT}..."
46
+ tar -czf "${OUTPUT}" -C "${MOUNT_POINT}" \
47
+ --exclude='./dev/*' \
48
+ --exclude='./proc/*' \
49
+ --exclude='./sys/*' \
50
+ --exclude='./tmp/*' \
51
+ --exclude='./run/*' \
52
+ --exclude='./mnt/*' \
53
+ --exclude='./media/*' \
54
+ --exclude='./lost+found' \
55
+ --exclude='./boot/efi/*' \
56
+ --numeric-owner \
57
+ .
58
+
59
+ echo "Tarball created successfully: ${OUTPUT}"
60
+ echo "Size: $(du -h "${OUTPUT}" | cut -f1)"
61
+
62
+ # Cleanup will be called by trap
63
+ exit 0
@@ -99,7 +99,18 @@ echo "Configuring DHCP for fabric-1 (untagged VLAN)..."
99
99
  SUBNET_CIDR="192.168.1.0/24"
100
100
  SUBNET_ID=$(maas "$MAAS_ADMIN_USERNAME" subnets read | jq -r '.[] | select(.cidr == "'"$SUBNET_CIDR"'") | .id')
101
101
  FABRIC_ID=$(maas "$MAAS_ADMIN_USERNAME" fabrics read | jq -r '.[] | select(.name == "fabric-1") | .id')
102
- RACK_CONTROLLER_ID=$(maas "$MAAS_ADMIN_USERNAME" rack-controllers read | jq -r '.[] | select(.ip_addresses[] == "'"$IP_ADDRESS"'") | .system_id')
102
+ RACK_CONTROLLER_ID=$(maas "$MAAS_ADMIN_USERNAME" rack-controllers read | jq -r '[.[] | select(.ip_addresses[] == "'"$IP_ADDRESS"'") | .system_id] | .[0]')
103
+
104
+ if [ -z "$RACK_CONTROLLER_ID" ] || [ "$RACK_CONTROLLER_ID" == "null" ]; then
105
+ echo "Warning: Could not find Rack Controller by IP $IP_ADDRESS. Attempting to use the first available Rack Controller..."
106
+ RACK_CONTROLLER_ID=$(maas "$MAAS_ADMIN_USERNAME" rack-controllers read | jq -r '.[0].system_id')
107
+ fi
108
+
109
+ if [ -z "$RACK_CONTROLLER_ID" ] || [ "$RACK_CONTROLLER_ID" == "null" ]; then
110
+ echo "Error: Could not find any Rack Controller."
111
+ exit 1
112
+ fi
113
+
103
114
  START_IP="192.168.1.191"
104
115
  END_IP="192.168.1.254"
105
116
 
@@ -110,7 +121,7 @@ fi
110
121
 
111
122
  # Create a Dynamic IP Range for enlistment, commissioning, and deployment
112
123
  echo "Creating dynamic IP range from $START_IP to $END_IP..."
113
- maas "$MAAS_ADMIN_USERNAME" ipranges create type=dynamic start_ip="$START_IP" end_ip="$END_IP"
124
+ maas "$MAAS_ADMIN_USERNAME" ipranges create type=dynamic start_ip="$START_IP" end_ip="$END_IP" || echo "Dynamic IP range likely already exists or conflicts. Proceeding..."
114
125
 
115
126
  # Enable DHCP on the untagged VLAN (VLAN tag 0)
116
127
  echo "Enabling DHCP on VLAN 0 for fabric-1 (ID: $FABRIC_ID)..."
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env bash
2
+ # Script to upload boot resources to MAAS using the REST API with OAuth
3
+ # Usage: ./maas-upload-boot-resource.sh <profile> <name> <title> <architecture> <base_image> <filetype> <file_path>
4
+
5
+ set -euo pipefail
6
+
7
+ if [ $# -lt 7 ]; then
8
+ echo "Usage: $0 <profile> <name> <title> <architecture> <base_image> <filetype> <file_path>"
9
+ echo ""
10
+ echo "Example:"
11
+ echo " $0 maas custom/rocky9 'Rocky 9 Custom' amd64/generic rhel/9 tgz rocky9.tar.gz"
12
+ exit 1
13
+ fi
14
+
15
+ PROFILE="$1"
16
+ NAME="$2"
17
+ TITLE="$3"
18
+ ARCHITECTURE="$4"
19
+ BASE_IMAGE="$5"
20
+ FILETYPE="$6"
21
+ FILE_PATH="$7"
22
+
23
+ # Verify file exists
24
+ if [ ! -f "$FILE_PATH" ]; then
25
+ echo "Error: File not found: $FILE_PATH"
26
+ exit 1
27
+ fi
28
+
29
+ # Verify jq is installed
30
+ if ! command -v jq &> /dev/null; then
31
+ echo "Error: jq is required for this script but not installed."
32
+ exit 1
33
+ fi
34
+
35
+ # Get MAAS API URL and credentials from profile
36
+ MAAS_INFO=$(maas list | grep "^${PROFILE}" || true)
37
+ if [ -z "$MAAS_INFO" ]; then
38
+ echo "Error: MAAS profile '${PROFILE}' not found"
39
+ echo "Available profiles:"
40
+ maas list
41
+ exit 1
42
+ fi
43
+
44
+ API_URL=$(echo "$MAAS_INFO" | awk '{print $2}')
45
+ API_KEY=$(echo "$MAAS_INFO" | awk '{print $3}')
46
+
47
+ # Parse OAuth credentials
48
+ CONSUMER_KEY=$(echo "$API_KEY" | cut -d: -f1)
49
+ TOKEN_KEY=$(echo "$API_KEY" | cut -d: -f2)
50
+ TOKEN_SECRET=$(echo "$API_KEY" | cut -d: -f3)
51
+
52
+ # Calculate file hash and size
53
+ echo "Calculating SHA256 checksum..."
54
+ SHA256=$(sha256sum "$FILE_PATH" | awk '{print $1}')
55
+ SIZE=$(stat -c%s "$FILE_PATH")
56
+
57
+ echo "File: $FILE_PATH"
58
+ echo "Size: $SIZE bytes ($(numfmt --to=iec-i --suffix=B $SIZE))"
59
+ echo "SHA256: $SHA256"
60
+ echo ""
61
+
62
+ # Endpoint for boot resources
63
+ ENDPOINT="${API_URL}boot-resources/"
64
+
65
+ # Generate OAuth timestamp and nonce
66
+ TIMESTAMP=$(date +%s)
67
+ NONCE=$(openssl rand -hex 16)
68
+
69
+ # OAuth parameters
70
+ OAUTH_VERSION="1.0"
71
+ OAUTH_SIGNATURE_METHOD="PLAINTEXT"
72
+ OAUTH_SIGNATURE="&${TOKEN_SECRET}"
73
+
74
+ echo "Initiating upload to MAAS..."
75
+ echo "API URL: $ENDPOINT"
76
+ echo "Name: $NAME"
77
+ echo "Title: $TITLE"
78
+ echo "Architecture: $ARCHITECTURE"
79
+ echo "Base Image: $BASE_IMAGE"
80
+ echo "Filetype: $FILETYPE"
81
+ echo ""
82
+
83
+ # 1. Initiate Upload (POST metadata)
84
+ # We do NOT send the content here, just the metadata to create the resource and get the upload URI.
85
+ RESPONSE=$(curl -s -X POST "${ENDPOINT}" \
86
+ -H "Authorization: OAuth oauth_version=\"${OAUTH_VERSION}\", oauth_signature_method=\"${OAUTH_SIGNATURE_METHOD}\", oauth_consumer_key=\"${CONSUMER_KEY}\", oauth_token=\"${TOKEN_KEY}\", oauth_signature=\"${OAUTH_SIGNATURE}\", oauth_timestamp=\"${TIMESTAMP}\", oauth_nonce=\"${NONCE}\"" \
87
+ -F "name=${NAME}" \
88
+ -F "title=${TITLE}" \
89
+ -F "architecture=${ARCHITECTURE}" \
90
+ -F "base_image=${BASE_IMAGE}" \
91
+ -F "filetype=${FILETYPE}" \
92
+ -F "sha256=${SHA256}" \
93
+ -F "size=${SIZE}")
94
+ CURL_RET=$?
95
+
96
+ if [ $CURL_RET -ne 0 ]; then
97
+ echo "Error: curl failed with exit code $CURL_RET"
98
+ exit 1
99
+ fi
100
+
101
+ # Validate JSON before parsing
102
+ if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then
103
+ echo "Error: MAAS returned invalid JSON."
104
+ echo "Raw response:"
105
+ echo "$RESPONSE"
106
+ exit 1
107
+ fi
108
+
109
+ # Extract Upload URI
110
+ UPLOAD_URI=$(echo "$RESPONSE" | jq -r '.sets | to_entries | sort_by(.key) | reverse | .[0].value.files | to_entries | .[0].value.upload_uri // empty')
111
+
112
+ if [ -z "$UPLOAD_URI" ]; then
113
+ echo "✗ Failed to get upload URI from MAAS response."
114
+ echo "Response:"
115
+ echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
116
+ exit 1
117
+ fi
118
+
119
+ echo "Upload URI obtained: $UPLOAD_URI"
120
+
121
+ # Construct the full upload URL
122
+ if [[ "$UPLOAD_URI" == http* ]]; then
123
+ FULL_UPLOAD_URL="$UPLOAD_URI"
124
+ else
125
+ # Extract scheme and authority from API_URL
126
+ # e.g. http://192.168.1.5:5240/MAAS/api/2.0/ -> http://192.168.1.5:5240
127
+ MAAS_ROOT=$(echo "$API_URL" | sed -E 's|^(https?://[^/]+).*|\1|')
128
+
129
+ # Ensure UPLOAD_URI starts with /
130
+ [[ "$UPLOAD_URI" != /* ]] && UPLOAD_URI="/$UPLOAD_URI"
131
+
132
+ FULL_UPLOAD_URL="${MAAS_ROOT}${UPLOAD_URI}"
133
+ fi
134
+
135
+ echo "Full Upload URL: $FULL_UPLOAD_URL"
136
+
137
+ # 2. Split file into chunks
138
+ CHUNK_SIZE=$((4 * 1024 * 1024)) # 4MB
139
+ TMP_DIR=$(mktemp -d)
140
+ echo "Splitting file into 4MB chunks in $TMP_DIR..."
141
+ split -b ${CHUNK_SIZE} "${FILE_PATH}" "${TMP_DIR}/chunk_"
142
+
143
+ # 3. Upload chunks
144
+ echo "Starting chunked upload..."
145
+ CHUNK_COUNT=$(ls "${TMP_DIR}"/chunk_* | wc -l)
146
+ CURRENT_CHUNK=0
147
+
148
+ for chunk in "${TMP_DIR}"/chunk_*; do
149
+ CURRENT_CHUNK=$((CURRENT_CHUNK + 1))
150
+ CHUNK_SIZE_BYTES=$(stat -c%s "$chunk")
151
+
152
+ # Progress indicator
153
+ echo -ne "Uploading chunk $CURRENT_CHUNK of $CHUNK_COUNT ($CHUNK_SIZE_BYTES bytes)...\r"
154
+
155
+ # Generate new nonce/timestamp for each request
156
+ TIMESTAMP=$(date +%s)
157
+ NONCE=$(openssl rand -hex 16)
158
+
159
+ # Upload chunk
160
+ CHUNK_RESPONSE=$(curl -s -X PUT "${FULL_UPLOAD_URL}" \
161
+ -H "Content-Type: application/octet-stream" \
162
+ -H "Content-Length: ${CHUNK_SIZE_BYTES}" \
163
+ -H "Authorization: OAuth oauth_version=\"${OAUTH_VERSION}\", oauth_signature_method=\"${OAUTH_SIGNATURE_METHOD}\", oauth_consumer_key=\"${CONSUMER_KEY}\", oauth_token=\"${TOKEN_KEY}\", oauth_signature=\"${OAUTH_SIGNATURE}\", oauth_timestamp=\"${TIMESTAMP}\", oauth_nonce=\"${NONCE}\"" \
164
+ --data-binary @"${chunk}" \
165
+ -w "%{http_code}")
166
+
167
+ HTTP_CODE="${CHUNK_RESPONSE: -3}"
168
+
169
+ if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
170
+ echo ""
171
+ echo "✗ Chunk upload failed with status: $HTTP_CODE"
172
+ echo "Response: ${CHUNK_RESPONSE::-3}"
173
+ rm -r "${TMP_DIR}"
174
+ exit 1
175
+ fi
176
+
177
+ rm "$chunk"
178
+ done
179
+
180
+ echo ""
181
+ echo "✓ Upload complete!"
182
+ rm -r "${TMP_DIR}"
183
+ exit 0
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Script to Initialize VARS files for Packer builds
3
+ # Usage: sudo ./scripts/packer-setup.sh
4
+
5
+ set -euo pipefail
6
+
7
+ # Packer needs writable VARS files for UEFI boot
8
+ PACKER_DIR="$(dirname "$0")/../packer/images"
9
+
10
+ # Find all packer image directories and create VARS files
11
+ for image_dir in "$PACKER_DIR"/*; do
12
+ if [ -d "$image_dir" ]; then
13
+ image_name=$(basename "$image_dir")
14
+ echo "Checking UEFI VARS files for $image_name..."
15
+
16
+ # Create x86_64 VARS file if it doesn't exist
17
+ if [ -f /usr/share/edk2/ovmf/OVMF_VARS.fd ] && [ ! -f "$image_dir/x86_64_VARS.fd" ]; then
18
+ cp /usr/share/edk2/ovmf/OVMF_VARS.fd "$image_dir/x86_64_VARS.fd"
19
+ echo "Created $image_dir/x86_64_VARS.fd"
20
+ fi
21
+
22
+ # Create aarch64 VARS file if it doesn't exist
23
+ if [ -f /usr/share/edk2/aarch64/AAVMF_VARS.fd ] && [ ! -f "$image_dir/aarch64_VARS.fd" ]; then
24
+ cp /usr/share/edk2/aarch64/AAVMF_VARS.fd "$image_dir/aarch64_VARS.fd"
25
+ echo "Created $image_dir/aarch64_VARS.fd"
26
+ fi
27
+ fi
28
+ done
29
+
30
+ echo "Packer and QEMU setup complete!"
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env bash
2
+ # Script to install Packer and libvirt/qemu tooling on Rocky Linux.
3
+ # Usage: sudo ./scripts/packer-setup.sh
4
+
5
+ set -euo pipefail
6
+
7
+
8
+ # 1) Replace/add the correct HashiCorp repo for RHEL/Rocky
9
+ sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
10
+
11
+ # Refresh dnf metadata
12
+ sudo dnf clean all
13
+ sudo dnf makecache --refresh
14
+
15
+ # 2) Try to install packer from the repo
16
+ sudo dnf install -y packer || echo "packer install from repo failed, try manual install (see below)"
17
+
18
+ # 3) Install libvirt/qemu tooling then start the service
19
+ sudo dnf install -y libvirt libvirt-daemon qemu-kvm qemu-img virt-install bridge-utils
20
+ sudo systemctl enable --now libvirtd
21
+ sudo systemctl status libvirtd --no-pager
22
+
23
+ # 3a) Install NBD and filesystem tools required for MAAS image creation
24
+ sudo dnf install -y libnbd nbdkit e2fsprogs kmod-kvdo kmod
25
+
26
+ # 4) Install UEFI firmware for x86_64 and ARM64
27
+ sudo dnf install -y edk2-ovmf edk2-aarch64
28
+
29
+ # 5) Create symlinks for qemu-system-* binaries (Rocky/RHEL uses qemu-kvm instead)
30
+ # Packer expects standard qemu-system-* names, but RHEL-based distros use qemu-kvm
31
+ if [ -f /usr/libexec/qemu-kvm ] && [ ! -f /usr/bin/qemu-system-x86_64 ]; then
32
+ echo "Creating symlink: /usr/bin/qemu-system-x86_64 -> /usr/libexec/qemu-kvm"
33
+ sudo ln -sf /usr/libexec/qemu-kvm /usr/bin/qemu-system-x86_64
34
+ fi
35
+
36
+ if [ -f /usr/libexec/qemu-kvm ] && [ ! -f /usr/bin/qemu-system-aarch64 ]; then
37
+ echo "Creating symlink: /usr/bin/qemu-system-aarch64 -> /usr/libexec/qemu-kvm"
38
+ sudo ln -sf /usr/libexec/qemu-kvm /usr/bin/qemu-system-aarch64
39
+ fi
40
+
41
+ # 6) Create symlinks for OVMF/AAVMF firmware files in expected locations
42
+ # Rocky/RHEL stores OVMF in /usr/share/edk2/ovmf, but Packer expects /usr/share/OVMF
43
+ if [ -f /usr/share/edk2/ovmf/OVMF_CODE.fd ] && [ ! -f /usr/share/OVMF/OVMF_CODE.fd ]; then
44
+ echo "Creating symlink: /usr/share/OVMF/OVMF_CODE.fd -> /usr/share/edk2/ovmf/OVMF_CODE.fd"
45
+ sudo ln -sf /usr/share/edk2/ovmf/OVMF_CODE.fd /usr/share/OVMF/OVMF_CODE.fd
46
+ fi
47
+
48
+ # Create AAVMF symlinks for ARM64 support
49
+ if [ -d /usr/share/edk2/aarch64 ] && [ ! -d /usr/share/AAVMF ]; then
50
+ echo "Creating symlink: /usr/share/AAVMF -> /usr/share/edk2/aarch64"
51
+ sudo ln -sf /usr/share/edk2/aarch64 /usr/share/AAVMF
52
+ fi