diff options
| -rw-r--r-- | classes/xen-guest-cross-install.bbclass | 437 | ||||
| -rw-r--r-- | recipes-extended/images/xen-guest-image-minimal.bb | 26 | ||||
| -rw-r--r-- | recipes-extended/xen-guest-bundles/example-xen-guest-bundle_1.0.bb | 54 |
3 files changed, 492 insertions, 25 deletions
diff --git a/classes/xen-guest-cross-install.bbclass b/classes/xen-guest-cross-install.bbclass new file mode 100644 index 00000000..f36182f8 --- /dev/null +++ b/classes/xen-guest-cross-install.bbclass | |||
| @@ -0,0 +1,437 @@ | |||
| 1 | # SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield | ||
| 2 | # | ||
| 3 | # SPDX-License-Identifier: MIT | ||
| 4 | # | ||
| 5 | # xen-guest-cross-install.bbclass | ||
| 6 | # =========================================================================== | ||
| 7 | # Xen guest bundling class for Dom0 images | ||
| 8 | # =========================================================================== | ||
| 9 | # | ||
| 10 | # This class enables bundling Xen guest images into a Dom0 host image during | ||
| 11 | # build time. It follows the same pattern as container-cross-install.bbclass: | ||
| 12 | # variable-driven, parse-time dependency generation, and a structured | ||
| 13 | # ROOTFS_POSTPROCESS_COMMAND. | ||
| 14 | # | ||
| 15 | # Usage (in image recipe): | ||
| 16 | # inherit xen-guest-cross-install | ||
| 17 | # | ||
| 18 | # Configuration (in local.conf or image recipe): | ||
| 19 | # BUNDLED_XEN_GUESTS = "xen-guest-image-minimal" | ||
| 20 | # BUNDLED_XEN_GUESTS = "xen-guest-image-minimal:autostart" | ||
| 21 | # BUNDLED_XEN_GUESTS = "xen-guest-image-minimal my-other-guest:autostart" | ||
| 22 | # BUNDLED_XEN_GUESTS = "prebuilt-guest:external" | ||
| 23 | # | ||
| 24 | # Guest format: recipe-name[:autostart][:external] | ||
| 25 | # - recipe-name: Yocto image recipe name that produces the guest rootfs | ||
| 26 | # - autostart: Optional. Creates symlink in /etc/xen/auto/ for xendomains | ||
| 27 | # - external: Optional. Skip dependency generation (3rd-party guest) | ||
| 28 | # | ||
| 29 | # Per-guest configuration via varflags: | ||
| 30 | # XEN_GUEST_MEMORY[guest-name] = "1024" | ||
| 31 | # XEN_GUEST_VCPUS[guest-name] = "2" | ||
| 32 | # XEN_GUEST_VIF[guest-name] = "bridge=xenbr0" | ||
| 33 | # XEN_GUEST_EXTRA[guest-name] = "root=/dev/xvda ro console=hvc0 ip=dhcp" | ||
| 34 | # XEN_GUEST_DISK_DEVICE[guest-name] = "xvda" | ||
| 35 | # XEN_GUEST_NAME[guest-name] = "my-custom-name" | ||
| 36 | # | ||
| 37 | # Custom config file (replaces auto-generation entirely): | ||
| 38 | # XEN_GUEST_CONFIG_FILE[guest-name] = "${UNPACKDIR}/custom.cfg" | ||
| 39 | # | ||
| 40 | # Explicit rootfs/kernel paths (for external/3rd-party guests): | ||
| 41 | # XEN_GUEST_ROOTFS[my-vendor-guest] = "vendor-rootfs.ext4" | ||
| 42 | # XEN_GUEST_KERNEL[my-vendor-guest] = "vendor-kernel" | ||
| 43 | # | ||
| 44 | # Artifacts installed on target: | ||
| 45 | # /var/lib/xen/images/ - guest rootfs and kernel files | ||
| 46 | # /etc/xen/<guest>.cfg - Xen guest configuration files | ||
| 47 | # /etc/xen/auto/ - symlinks for autostart guests (xendomains) | ||
| 48 | # | ||
| 49 | # Guests can be launched after boot with: | ||
| 50 | # xl create -c /etc/xen/<guest>.cfg | ||
| 51 | # | ||
| 52 | |||
| 53 | # =========================================================================== | ||
| 54 | # Default variables | ||
| 55 | # =========================================================================== | ||
| 56 | |||
| 57 | BUNDLED_XEN_GUESTS ?= "" | ||
| 58 | XEN_GUEST_IMAGE_FSTYPE ?= "ext4" | ||
| 59 | XEN_GUEST_MEMORY_DEFAULT ?= "512" | ||
| 60 | XEN_GUEST_VCPUS_DEFAULT ?= "1" | ||
| 61 | XEN_GUEST_VIF_DEFAULT ?= "bridge=xenbr0" | ||
| 62 | XEN_GUEST_EXTRA_DEFAULT ?= "root=/dev/xvda ro ip=dhcp" | ||
| 63 | XEN_GUEST_DISK_DEVICE_DEFAULT ?= "xvda" | ||
| 64 | |||
| 65 | # =========================================================================== | ||
| 66 | # Parse-time dependency generation | ||
| 67 | # =========================================================================== | ||
| 68 | |||
| 69 | python __anonymous() { | ||
| 70 | bundled = (d.getVar('BUNDLED_XEN_GUESTS') or "").split() | ||
| 71 | if not bundled: | ||
| 72 | return | ||
| 73 | |||
| 74 | deps = "" | ||
| 75 | for entry in bundled: | ||
| 76 | parts = entry.split(':') | ||
| 77 | guest_name = parts[0] | ||
| 78 | |||
| 79 | # Check for :external tag | ||
| 80 | is_external = 'external' in parts | ||
| 81 | |||
| 82 | # Skip dependency for external guests | ||
| 83 | if is_external: | ||
| 84 | continue | ||
| 85 | |||
| 86 | # Generate dependency on guest recipe | ||
| 87 | deps += " %s:do_image_complete" % guest_name | ||
| 88 | |||
| 89 | if deps: | ||
| 90 | d.appendVarFlag('do_rootfs', 'depends', deps) | ||
| 91 | } | ||
| 92 | |||
| 93 | # =========================================================================== | ||
| 94 | # Python helpers - build varflag maps for shell access | ||
| 95 | # =========================================================================== | ||
| 96 | |||
| 97 | # Build XEN_GUEST_CONFIG_FILE_MAP from varflags | ||
| 98 | # Format: guest1=/path/to/file1;guest2=/path/to/file2 | ||
| 99 | def get_xen_guest_config_map(d): | ||
| 100 | bundled = (d.getVar('BUNDLED_XEN_GUESTS') or "").split() | ||
| 101 | if not bundled: | ||
| 102 | return "" | ||
| 103 | |||
| 104 | mappings = [] | ||
| 105 | for entry in bundled: | ||
| 106 | parts = entry.split(':') | ||
| 107 | guest_name = parts[0] | ||
| 108 | custom_file = d.getVarFlag('XEN_GUEST_CONFIG_FILE', guest_name) | ||
| 109 | if custom_file: | ||
| 110 | mappings.append("%s=%s" % (guest_name, custom_file)) | ||
| 111 | |||
| 112 | return ";".join(mappings) | ||
| 113 | |||
| 114 | XEN_GUEST_CONFIG_FILE_MAP = "${@get_xen_guest_config_map(d)}" | ||
| 115 | |||
| 116 | # Build XEN_GUEST_PARAMS_MAP from varflags | ||
| 117 | # Format: guest1=memory|vcpus|vif|extra|disk_device|name|rootfs|kernel;guest2=... | ||
| 118 | def get_xen_guest_params(d): | ||
| 119 | bundled = (d.getVar('BUNDLED_XEN_GUESTS') or "").split() | ||
| 120 | if not bundled: | ||
| 121 | return "" | ||
| 122 | |||
| 123 | mem_default = d.getVar('XEN_GUEST_MEMORY_DEFAULT') | ||
| 124 | vcpus_default = d.getVar('XEN_GUEST_VCPUS_DEFAULT') | ||
| 125 | vif_default = d.getVar('XEN_GUEST_VIF_DEFAULT') | ||
| 126 | extra_default = d.getVar('XEN_GUEST_EXTRA_DEFAULT') | ||
| 127 | disk_default = d.getVar('XEN_GUEST_DISK_DEVICE_DEFAULT') | ||
| 128 | |||
| 129 | mappings = [] | ||
| 130 | for entry in bundled: | ||
| 131 | parts = entry.split(':') | ||
| 132 | guest_name = parts[0] | ||
| 133 | |||
| 134 | memory = d.getVarFlag('XEN_GUEST_MEMORY', guest_name) or mem_default | ||
| 135 | vcpus = d.getVarFlag('XEN_GUEST_VCPUS', guest_name) or vcpus_default | ||
| 136 | vif = d.getVarFlag('XEN_GUEST_VIF', guest_name) or vif_default | ||
| 137 | extra = d.getVarFlag('XEN_GUEST_EXTRA', guest_name) or extra_default | ||
| 138 | disk_device = d.getVarFlag('XEN_GUEST_DISK_DEVICE', guest_name) or disk_default | ||
| 139 | name = d.getVarFlag('XEN_GUEST_NAME', guest_name) or guest_name | ||
| 140 | rootfs = d.getVarFlag('XEN_GUEST_ROOTFS', guest_name) or "" | ||
| 141 | kernel = d.getVarFlag('XEN_GUEST_KERNEL', guest_name) or "" | ||
| 142 | |||
| 143 | params = "|".join([memory, vcpus, vif, extra, disk_device, name, rootfs, kernel]) | ||
| 144 | mappings.append("%s=%s" % (guest_name, params)) | ||
| 145 | |||
| 146 | return ";".join(mappings) | ||
| 147 | |||
| 148 | XEN_GUEST_PARAMS_MAP = "${@get_xen_guest_params(d)}" | ||
| 149 | |||
| 150 | # =========================================================================== | ||
| 151 | # Shell function: merge_installed_xen_bundles | ||
| 152 | # Processes packages created by xen-guest-bundle.bbclass | ||
| 153 | # =========================================================================== | ||
| 154 | |||
| 155 | merge_installed_xen_bundles() { | ||
| 156 | set +e | ||
| 157 | |||
| 158 | BUNDLES_DIR="${IMAGE_ROOTFS}${datadir}/xen-guest-bundles" | ||
| 159 | |||
| 160 | if [ ! -d "${BUNDLES_DIR}" ]; then | ||
| 161 | bbnote "No Xen guest bundles found in ${BUNDLES_DIR}" | ||
| 162 | return 0 | ||
| 163 | fi | ||
| 164 | |||
| 165 | bbnote "Processing installed Xen guest bundles from ${BUNDLES_DIR}" | ||
| 166 | |||
| 167 | DEST_DIR="${IMAGE_ROOTFS}/var/lib/xen/images" | ||
| 168 | CONFIG_DIR="${IMAGE_ROOTFS}/etc/xen" | ||
| 169 | AUTO_DIR="${IMAGE_ROOTFS}/etc/xen/auto" | ||
| 170 | |||
| 171 | mkdir -p "$DEST_DIR" | ||
| 172 | mkdir -p "$CONFIG_DIR" | ||
| 173 | |||
| 174 | for bundle_dir in ${BUNDLES_DIR}/*/; do | ||
| 175 | [ -d "$bundle_dir" ] || continue | ||
| 176 | |||
| 177 | manifest="${bundle_dir}manifest" | ||
| 178 | if [ ! -f "$manifest" ]; then | ||
| 179 | bbwarn "No manifest found in $bundle_dir, skipping" | ||
| 180 | continue | ||
| 181 | fi | ||
| 182 | |||
| 183 | bundle_name=$(basename "$bundle_dir") | ||
| 184 | bbnote "Processing bundle: $bundle_name" | ||
| 185 | |||
| 186 | while IFS=: read -r guest_name rootfs_file kernel_file autostart_flag || [ -n "$guest_name" ]; do | ||
| 187 | [ -z "$guest_name" ] && continue | ||
| 188 | |||
| 189 | bbnote " Guest: $guest_name (rootfs=$rootfs_file kernel=$kernel_file autostart=$autostart_flag)" | ||
| 190 | |||
| 191 | # Copy rootfs | ||
| 192 | if [ -f "${bundle_dir}images/${rootfs_file}" ]; then | ||
| 193 | cp "${bundle_dir}images/${rootfs_file}" "$DEST_DIR/" | ||
| 194 | bbnote " Installed rootfs: $rootfs_file" | ||
| 195 | else | ||
| 196 | bbwarn " Rootfs not found: ${bundle_dir}images/${rootfs_file}" | ||
| 197 | fi | ||
| 198 | |||
| 199 | # Copy kernel | ||
| 200 | if [ -f "${bundle_dir}images/${kernel_file}" ]; then | ||
| 201 | cp "${bundle_dir}images/${kernel_file}" "$DEST_DIR/" | ||
| 202 | bbnote " Installed kernel: $kernel_file" | ||
| 203 | else | ||
| 204 | bbwarn " Kernel not found: ${bundle_dir}images/${kernel_file}" | ||
| 205 | fi | ||
| 206 | |||
| 207 | # Copy config | ||
| 208 | if [ -f "${bundle_dir}configs/${guest_name}.cfg" ]; then | ||
| 209 | cp "${bundle_dir}configs/${guest_name}.cfg" "${CONFIG_DIR}/" | ||
| 210 | bbnote " Installed config: ${guest_name}.cfg" | ||
| 211 | else | ||
| 212 | bbwarn " Config not found: ${bundle_dir}configs/${guest_name}.cfg" | ||
| 213 | fi | ||
| 214 | |||
| 215 | # Handle autostart | ||
| 216 | if [ "$autostart_flag" = "autostart" ]; then | ||
| 217 | mkdir -p "$AUTO_DIR" | ||
| 218 | ln -sf "/etc/xen/${guest_name}.cfg" "${AUTO_DIR}/${guest_name}.cfg" | ||
| 219 | bbnote " Autostart enabled: ${guest_name}.cfg" | ||
| 220 | fi | ||
| 221 | |||
| 222 | bbnote " Guest '$guest_name' merged successfully" | ||
| 223 | done < "$manifest" | ||
| 224 | done | ||
| 225 | |||
| 226 | # Clean up bundle files from final image | ||
| 227 | rm -rf "${BUNDLES_DIR}" | ||
| 228 | bbnote "Cleaned up Xen guest bundle files" | ||
| 229 | |||
| 230 | return 0 | ||
| 231 | } | ||
| 232 | |||
| 233 | # =========================================================================== | ||
| 234 | # Shell function: bundle_xen_guests | ||
| 235 | # ROOTFS_POSTPROCESS_COMMAND | ||
| 236 | # =========================================================================== | ||
| 237 | |||
| 238 | bundle_xen_guests() { | ||
| 239 | set +e | ||
| 240 | |||
| 241 | # ======================================================================== | ||
| 242 | # Helper functions | ||
| 243 | # Defined inside bundle_xen_guests() for bitbake execution context | ||
| 244 | # ======================================================================== | ||
| 245 | |||
| 246 | # Extract a per-guest parameter from the params map | ||
| 247 | # Args: guest_name field_index (0=memory, 1=vcpus, 2=vif, 3=extra, | ||
| 248 | # 4=disk_device, 5=name, 6=rootfs_override, 7=kernel_override) | ||
| 249 | get_guest_param() { | ||
| 250 | local guest="$1" | ||
| 251 | local field="$2" | ||
| 252 | local params_map="${XEN_GUEST_PARAMS_MAP}" | ||
| 253 | |||
| 254 | # Find this guest's params in the map | ||
| 255 | local guest_params=$(echo "$params_map" | tr ';' '\n' | grep "^${guest}=" | cut -d= -f2-) | ||
| 256 | if [ -z "$guest_params" ]; then | ||
| 257 | return 1 | ||
| 258 | fi | ||
| 259 | |||
| 260 | # Extract field by index (pipe-separated) | ||
| 261 | echo "$guest_params" | cut -d'|' -f$(expr $field + 1) | ||
| 262 | } | ||
| 263 | |||
| 264 | # Resolve guest rootfs path | ||
| 265 | # Checks varflag override first, then standard Yocto naming | ||
| 266 | resolve_guest_rootfs() { | ||
| 267 | local guest="$1" | ||
| 268 | local override=$(get_guest_param "$guest" 6) | ||
| 269 | |||
| 270 | if [ -n "$override" ]; then | ||
| 271 | # Explicit rootfs filename provided | ||
| 272 | local path="${DEPLOY_DIR_IMAGE}/$override" | ||
| 273 | if [ -e "$path" ]; then | ||
| 274 | readlink -f "$path" | ||
| 275 | return 0 | ||
| 276 | fi | ||
| 277 | bbwarn "XEN_GUEST_ROOTFS override '$override' not found at $path" | ||
| 278 | return 1 | ||
| 279 | fi | ||
| 280 | |||
| 281 | # Standard Yocto naming: <recipe>-<MACHINE>.<fstype> | ||
| 282 | local path="${DEPLOY_DIR_IMAGE}/${guest}-${MACHINE}.${XEN_GUEST_IMAGE_FSTYPE}" | ||
| 283 | if [ -e "$path" ]; then | ||
| 284 | readlink -f "$path" | ||
| 285 | return 0 | ||
| 286 | fi | ||
| 287 | |||
| 288 | # Fallback: <recipe>-<MACHINE>.rootfs.<fstype> | ||
| 289 | path="${DEPLOY_DIR_IMAGE}/${guest}-${MACHINE}.rootfs.${XEN_GUEST_IMAGE_FSTYPE}" | ||
| 290 | if [ -e "$path" ]; then | ||
| 291 | readlink -f "$path" | ||
| 292 | return 0 | ||
| 293 | fi | ||
| 294 | |||
| 295 | bbwarn "Guest rootfs not found for '$guest'. Searched:" | ||
| 296 | bbwarn " ${DEPLOY_DIR_IMAGE}/${guest}-${MACHINE}.${XEN_GUEST_IMAGE_FSTYPE}" | ||
| 297 | bbwarn " ${DEPLOY_DIR_IMAGE}/${guest}-${MACHINE}.rootfs.${XEN_GUEST_IMAGE_FSTYPE}" | ||
| 298 | return 1 | ||
| 299 | } | ||
| 300 | |||
| 301 | # Resolve guest kernel path | ||
| 302 | # Checks varflag override first, then uses KERNEL_IMAGETYPE | ||
| 303 | resolve_guest_kernel() { | ||
| 304 | local guest="$1" | ||
| 305 | local override=$(get_guest_param "$guest" 7) | ||
| 306 | |||
| 307 | if [ -n "$override" ]; then | ||
| 308 | # Explicit kernel filename provided | ||
| 309 | local path="${DEPLOY_DIR_IMAGE}/$override" | ||
| 310 | if [ -e "$path" ]; then | ||
| 311 | readlink -f "$path" | ||
| 312 | return 0 | ||
| 313 | fi | ||
| 314 | bbwarn "XEN_GUEST_KERNEL override '$override' not found at $path" | ||
| 315 | return 1 | ||
| 316 | fi | ||
| 317 | |||
| 318 | # Default: shared kernel (same MACHINE) | ||
| 319 | local path="${DEPLOY_DIR_IMAGE}/${KERNEL_IMAGETYPE}" | ||
| 320 | if [ -e "$path" ]; then | ||
| 321 | readlink -f "$path" | ||
| 322 | return 0 | ||
| 323 | fi | ||
| 324 | |||
| 325 | bbwarn "Guest kernel not found at ${DEPLOY_DIR_IMAGE}/${KERNEL_IMAGETYPE}" | ||
| 326 | return 1 | ||
| 327 | } | ||
| 328 | |||
| 329 | # Generate a Xen guest configuration file | ||
| 330 | # Args: guest_name rootfs_path kernel_path | ||
| 331 | generate_xen_guest_config() { | ||
| 332 | local guest="$1" | ||
| 333 | local rootfs_basename="$2" | ||
| 334 | local kernel_basename="$3" | ||
| 335 | local outfile="$4" | ||
| 336 | |||
| 337 | local memory=$(get_guest_param "$guest" 0) | ||
| 338 | local vcpus=$(get_guest_param "$guest" 1) | ||
| 339 | local vif=$(get_guest_param "$guest" 2) | ||
| 340 | local extra=$(get_guest_param "$guest" 3) | ||
| 341 | local disk_device=$(get_guest_param "$guest" 4) | ||
| 342 | local name=$(get_guest_param "$guest" 5) | ||
| 343 | |||
| 344 | cat > "$outfile" << EOF | ||
| 345 | name = "$name" | ||
| 346 | memory = $memory | ||
| 347 | vcpus = $vcpus | ||
| 348 | disk = ['file:/var/lib/xen/images/$rootfs_basename,$disk_device,rw'] | ||
| 349 | vif = ['$vif'] | ||
| 350 | kernel = "/var/lib/xen/images/$kernel_basename" | ||
| 351 | extra = "$extra" | ||
| 352 | EOF | ||
| 353 | } | ||
| 354 | |||
| 355 | # ======================================================================== | ||
| 356 | # Main loop | ||
| 357 | # ======================================================================== | ||
| 358 | |||
| 359 | if [ -z "${BUNDLED_XEN_GUESTS}" ]; then | ||
| 360 | bbnote "No Xen bundled guests specified" | ||
| 361 | return 0 | ||
| 362 | fi | ||
| 363 | |||
| 364 | bbnote "Processing Xen bundled guests: ${BUNDLED_XEN_GUESTS}" | ||
| 365 | |||
| 366 | DEST_DIR="${IMAGE_ROOTFS}/var/lib/xen/images" | ||
| 367 | CONFIG_DIR="${IMAGE_ROOTFS}/etc/xen" | ||
| 368 | AUTO_DIR="${IMAGE_ROOTFS}/etc/xen/auto" | ||
| 369 | |||
| 370 | mkdir -p "$DEST_DIR" | ||
| 371 | mkdir -p "$CONFIG_DIR" | ||
| 372 | |||
| 373 | for entry in ${BUNDLED_XEN_GUESTS}; do | ||
| 374 | # Parse: recipe-name[:autostart][:external] | ||
| 375 | guest_name="$(echo $entry | cut -d: -f1)" | ||
| 376 | |||
| 377 | # Check for tags | ||
| 378 | autostart="" | ||
| 379 | if echo "$entry" | grep -q ':autostart'; then | ||
| 380 | autostart="1" | ||
| 381 | fi | ||
| 382 | |||
| 383 | bbnote "Processing guest: $guest_name (autostart=$autostart)" | ||
| 384 | |||
| 385 | # Resolve rootfs | ||
| 386 | rootfs_path=$(resolve_guest_rootfs "$guest_name") | ||
| 387 | if [ $? -ne 0 ] || [ -z "$rootfs_path" ]; then | ||
| 388 | bbfatal "Cannot resolve rootfs for guest '$guest_name'" | ||
| 389 | fi | ||
| 390 | rootfs_basename=$(basename "$rootfs_path") | ||
| 391 | |||
| 392 | # Resolve kernel | ||
| 393 | kernel_path=$(resolve_guest_kernel "$guest_name") | ||
| 394 | if [ $? -ne 0 ] || [ -z "$kernel_path" ]; then | ||
| 395 | bbfatal "Cannot resolve kernel for guest '$guest_name'" | ||
| 396 | fi | ||
| 397 | kernel_basename=$(basename "$kernel_path") | ||
| 398 | |||
| 399 | # Copy rootfs and kernel to target | ||
| 400 | bbnote "Copying rootfs: $rootfs_path -> $DEST_DIR/" | ||
| 401 | cp "$rootfs_path" "$DEST_DIR/" | ||
| 402 | |||
| 403 | bbnote "Copying kernel: $kernel_path -> $DEST_DIR/" | ||
| 404 | cp "$kernel_path" "$DEST_DIR/" | ||
| 405 | |||
| 406 | # Generate or install config file | ||
| 407 | config_map="${XEN_GUEST_CONFIG_FILE_MAP}" | ||
| 408 | custom_config=$(echo "$config_map" | tr ';' '\n' | grep "^${guest_name}=" | cut -d= -f2-) | ||
| 409 | |||
| 410 | if [ -n "$custom_config" ] && [ -f "$custom_config" ]; then | ||
| 411 | bbnote "Installing custom config: $custom_config" | ||
| 412 | # Fix paths in custom config to point to /var/lib/xen/images/ | ||
| 413 | sed -E \ | ||
| 414 | -e "s#^(disk = \[)[^,]+#\1'file:/var/lib/xen/images/$rootfs_basename#" \ | ||
| 415 | -e "s#^(kernel = )\"[^\"]+\"#\1\"/var/lib/xen/images/$kernel_basename\"#" \ | ||
| 416 | "$custom_config" > "${CONFIG_DIR}/${guest_name}.cfg" | ||
| 417 | else | ||
| 418 | bbnote "Generating config for $guest_name" | ||
| 419 | generate_xen_guest_config "$guest_name" "$rootfs_basename" "$kernel_basename" \ | ||
| 420 | "${CONFIG_DIR}/${guest_name}.cfg" | ||
| 421 | fi | ||
| 422 | |||
| 423 | # Handle autostart | ||
| 424 | if [ -n "$autostart" ]; then | ||
| 425 | mkdir -p "$AUTO_DIR" | ||
| 426 | ln -sf "/etc/xen/${guest_name}.cfg" "${AUTO_DIR}/${guest_name}.cfg" | ||
| 427 | bbnote "Autostart enabled: ${AUTO_DIR}/${guest_name}.cfg -> /etc/xen/${guest_name}.cfg" | ||
| 428 | fi | ||
| 429 | |||
| 430 | bbnote "Guest '$guest_name' bundled successfully" | ||
| 431 | done | ||
| 432 | |||
| 433 | bbnote "Done processing all Xen bundled guests" | ||
| 434 | } | ||
| 435 | |||
| 436 | # First merge any bundles installed via IMAGE_INSTALL, then process BUNDLED_XEN_GUESTS | ||
| 437 | ROOTFS_POSTPROCESS_COMMAND += "merge_installed_xen_bundles; bundle_xen_guests;" | ||
diff --git a/recipes-extended/images/xen-guest-image-minimal.bb b/recipes-extended/images/xen-guest-image-minimal.bb index 76f320e4..1afc4219 100644 --- a/recipes-extended/images/xen-guest-image-minimal.bb +++ b/recipes-extended/images/xen-guest-image-minimal.bb | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | DESCRIPTION = "A Xen guest image." | 1 | DESCRIPTION = "A Xen guest image." |
| 2 | 2 | ||
| 3 | inherit core-image features_check deploy | 3 | inherit core-image features_check |
| 4 | inherit kernel-artifact-names | ||
| 5 | 4 | ||
| 6 | IMAGE_INSTALL += " \ | 5 | IMAGE_INSTALL += " \ |
| 7 | packagegroup-core-boot \ | 6 | packagegroup-core-boot \ |
| @@ -26,26 +25,3 @@ LICENSE = "MIT" | |||
| 26 | APPEND += "console=hvc0" | 25 | APPEND += "console=hvc0" |
| 27 | 26 | ||
| 28 | IMAGE_FSTYPES = "tar.bz2 ext4 ext4.qcow2" | 27 | IMAGE_FSTYPES = "tar.bz2 ext4 ext4.qcow2" |
| 29 | |||
| 30 | XEN_GUEST_AUTO_BUNDLE ?= "" | ||
| 31 | |||
| 32 | # When a xen-guest-image-minimal is built with the | ||
| 33 | # XEN_GUEST_AUTO_BUNDLE varaible set to True, a configuration file for | ||
| 34 | # automatic guest bundling will be generated and the guest bundled | ||
| 35 | # automatically when a xen host image is built. | ||
| 36 | do_deploy() { | ||
| 37 | if [ -n "${XEN_GUEST_AUTO_BUNDLE}" ]; then | ||
| 38 | outname="xen-guest-bundle-${IMAGE_BASENAME}${IMAGE_MACHINE_SUFFIX}-${IMAGE_VERSION_SUFFIX}.cfg" | ||
| 39 | cat <<EOF >>${DEPLOYDIR}/$outname | ||
| 40 | name = "xen-guest" | ||
| 41 | memory = 512 | ||
| 42 | vcpus = 1 | ||
| 43 | disk = ['file:${IMAGE_LINK_NAME}.ext4,xvda,rw'] | ||
| 44 | vif = ['bridge=xenbr0'] | ||
| 45 | kernel = "${KERNEL_IMAGETYPE}" | ||
| 46 | extra = "root=/dev/xvda ro ip=dhcp" | ||
| 47 | EOF | ||
| 48 | fi | ||
| 49 | } | ||
| 50 | |||
| 51 | addtask deploy after do_compile | ||
diff --git a/recipes-extended/xen-guest-bundles/example-xen-guest-bundle_1.0.bb b/recipes-extended/xen-guest-bundles/example-xen-guest-bundle_1.0.bb new file mode 100644 index 00000000..96e17275 --- /dev/null +++ b/recipes-extended/xen-guest-bundles/example-xen-guest-bundle_1.0.bb | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | # example-xen-guest-bundle_1.0.bb | ||
| 2 | # =========================================================================== | ||
| 3 | # Example Xen guest bundle recipe demonstrating xen-guest-bundle.bbclass | ||
| 4 | # =========================================================================== | ||
| 5 | # | ||
| 6 | # This recipe shows how to create a package that bundles Xen guest images. | ||
| 7 | # When installed via IMAGE_INSTALL into a Dom0 image that inherits | ||
| 8 | # xen-guest-cross-install, the guests are automatically deployed. | ||
| 9 | # | ||
| 10 | # Usage in image recipe (e.g., xen-image-minimal.bb): | ||
| 11 | # IMAGE_INSTALL += "example-xen-guest-bundle" | ||
| 12 | # | ||
| 13 | # Or in local.conf (use pn- override for specific images): | ||
| 14 | # IMAGE_INSTALL:append:pn-xen-image-minimal = " example-xen-guest-bundle" | ||
| 15 | # | ||
| 16 | # IMPORTANT: Do NOT use global IMAGE_INSTALL:append without pn- override! | ||
| 17 | # This causes circular dependencies when guest images try to include | ||
| 18 | # the bundle that depends on them. | ||
| 19 | # | ||
| 20 | # =========================================================================== | ||
| 21 | |||
| 22 | SUMMARY = "Example Xen guest bundle" | ||
| 23 | DESCRIPTION = "Demonstrates xen-guest-bundle.bbclass by bundling the \ | ||
| 24 | xen-guest-image-minimal guest. Use this as a template \ | ||
| 25 | for your own Xen guest bundles." | ||
| 26 | LICENSE = "MIT" | ||
| 27 | LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" | ||
| 28 | |||
| 29 | inherit xen-guest-bundle | ||
| 30 | |||
| 31 | # Define guests to bundle | ||
| 32 | # Format: recipe-name[:autostart][:external] | ||
| 33 | # | ||
| 34 | # recipe-name: Yocto image recipe that produces the guest rootfs | ||
| 35 | # autostart: Creates symlink in /etc/xen/auto/ for xendomains | ||
| 36 | # external: Skip dependency generation (pre-built/3rd-party guest) | ||
| 37 | XEN_GUEST_BUNDLES = "\ | ||
| 38 | xen-guest-image-minimal:autostart \ | ||
| 39 | " | ||
| 40 | |||
| 41 | # Per-guest configuration via varflags (optional): | ||
| 42 | XEN_GUEST_MEMORY[xen-guest-image-minimal] = "1024" | ||
| 43 | # XEN_GUEST_VCPUS[xen-guest-image-minimal] = "2" | ||
| 44 | # XEN_GUEST_VIF[xen-guest-image-minimal] = "bridge=xenbr0" | ||
| 45 | # XEN_GUEST_EXTRA[xen-guest-image-minimal] = "root=/dev/xvda ro console=hvc0 ip=dhcp" | ||
| 46 | |||
| 47 | # Custom config file (replaces auto-generation): | ||
| 48 | # SRC_URI += "file://my-custom-guest.cfg" | ||
| 49 | # XEN_GUEST_CONFIG_FILE[xen-guest-image-minimal] = "${UNPACKDIR}/my-custom-guest.cfg" | ||
| 50 | |||
| 51 | # External guest example (rootfs/kernel already in DEPLOY_DIR_IMAGE): | ||
| 52 | # XEN_GUEST_BUNDLES += "my-vendor-guest:external" | ||
| 53 | # XEN_GUEST_ROOTFS[my-vendor-guest] = "vendor-rootfs.ext4" | ||
| 54 | # XEN_GUEST_KERNEL[my-vendor-guest] = "vendor-kernel" | ||
