diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-18 14:07:49 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-26 01:05:01 +0000 |
| commit | 9377aede3157a3e7b702dc389c15f27523b673e7 (patch) | |
| tree | 9ea01493815cfb58e642b65b5b31472235b5a09a /recipes-core | |
| parent | fa4b171a436559787cfcebd4046a1354a1f5cacf (diff) | |
| download | meta-virtualization-9377aede3157a3e7b702dc389c15f27523b673e7.tar.gz | |
vxn: add containerd OCI runtime integration
Add shell-based OCI runtime (vxn-oci-runtime) that enables containerd
to manage Xen DomU containers through the standard runc shim. Non-terminal
container output flows back to ctr via the shim's pipe mechanism.
New files:
- vxn-oci-runtime: OCI runtime (create/start/state/kill/delete/features/logs)
- vxn-sendtty.c: SCM_RIGHTS helper for terminal mode PTY passing
- containerd-shim-vxn-v2: PATH trick wrapper for runc shim coexistence
- containerd-config-vxn.toml: CRI config (vxn default, runc fallback)
- vctr: convenience wrapper injecting --runtime io.containerd.vxn.v2
Key design:
- Monitor subprocess uses wait on xl console (not sleep-polling) for
instant reaction when domain dies, then extracts output markers and
writes to stdout (shim pipe -> containerd FIFO -> ctr client)
- cmd_state checks monitor PID liveness (not domain status) to prevent
premature cleanup race that killed monitor before output
- cmd_delete always destroys remnant domains (no --force needed)
- Coexists with runc: /usr/libexec/vxn/shim/runc symlink + PATH trick
Verified: vctr run --rm, vctr run -d, vxn standalone, vxn daemon mode.
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-core')
| -rw-r--r-- | recipes-core/vxn/vxn_1.0.bb | 196 |
1 files changed, 149 insertions, 47 deletions
diff --git a/recipes-core/vxn/vxn_1.0.bb b/recipes-core/vxn/vxn_1.0.bb index 2a36274a..d16cbc77 100644 --- a/recipes-core/vxn/vxn_1.0.bb +++ b/recipes-core/vxn/vxn_1.0.bb | |||
| @@ -14,9 +14,13 @@ | |||
| 14 | # - vcontainer-common.sh (shared CLI code) | 14 | # - vcontainer-common.sh (shared CLI code) |
| 15 | # - Kernel, initramfs, and rootfs blobs for booting DomU guests | 15 | # - Kernel, initramfs, and rootfs blobs for booting DomU guests |
| 16 | # | 16 | # |
| 17 | # The blobs are sourced from the vxn-initramfs-create recipe which | 17 | # Blobs are sourced directly from the vruntime multiconfig deploy |
| 18 | # reuses the same rootfs images built by vdkr/vpdmn (the init scripts | 18 | # directory. The rootfs squashfs is unsquashed, vxn init scripts are |
| 19 | # detect the hypervisor at boot time). | 19 | # injected, and it is re-squashed — all within do_compile. This keeps |
| 20 | # the entire dependency chain in one recipe so that SRC_URI hash | ||
| 21 | # tracking and mcdepends work correctly without relying on a separate | ||
| 22 | # deploy-only recipe (which cannot be task-depended on from a packaged | ||
| 23 | # recipe without breaking image do_rootfs sstate manifest checks). | ||
| 20 | # | 24 | # |
| 21 | # =========================================================================== | 25 | # =========================================================================== |
| 22 | # BUILD INSTRUCTIONS | 26 | # BUILD INSTRUCTIONS |
| @@ -49,21 +53,31 @@ LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda | |||
| 49 | inherit features_check | 53 | inherit features_check |
| 50 | REQUIRED_DISTRO_FEATURES = "xen" | 54 | REQUIRED_DISTRO_FEATURES = "xen" |
| 51 | 55 | ||
| 56 | # Host scripts + guest init scripts (all from the vcontainer files dir) | ||
| 52 | SRC_URI = "\ | 57 | SRC_URI = "\ |
| 53 | file://vxn.sh \ | 58 | file://vxn.sh \ |
| 54 | file://vrunner.sh \ | 59 | file://vrunner.sh \ |
| 55 | file://vrunner-backend-xen.sh \ | 60 | file://vrunner-backend-xen.sh \ |
| 56 | file://vrunner-backend-qemu.sh \ | 61 | file://vrunner-backend-qemu.sh \ |
| 57 | file://vcontainer-common.sh \ | 62 | file://vcontainer-common.sh \ |
| 63 | file://vxn-init.sh \ | ||
| 64 | file://vcontainer-init-common.sh \ | ||
| 65 | file://vxn-oci-runtime \ | ||
| 66 | file://vxn-sendtty.c \ | ||
| 67 | file://containerd-config-vxn.toml \ | ||
| 68 | file://containerd-shim-vxn-v2 \ | ||
| 69 | file://vctr \ | ||
| 58 | " | 70 | " |
| 59 | 71 | ||
| 60 | FILESEXTRAPATHS:prepend := "${THISDIR}/../../recipes-containers/vcontainer/files:" | 72 | FILESEXTRAPATHS:prepend := "${THISDIR}/../../recipes-containers/vcontainer/files:" |
| 61 | 73 | ||
| 62 | S = "${UNPACKDIR}" | 74 | S = "${UNPACKDIR}" |
| 75 | B = "${WORKDIR}/build" | ||
| 63 | 76 | ||
| 64 | # Runtime dependencies on Dom0 | 77 | # Runtime dependencies on Dom0 |
| 65 | RDEPENDS:${PN} = "\ | 78 | RDEPENDS:${PN} = "\ |
| 66 | xen-tools-xl \ | 79 | xen-tools-xl \ |
| 80 | xen-tools-xenstore \ | ||
| 67 | bash \ | 81 | bash \ |
| 68 | jq \ | 82 | jq \ |
| 69 | socat \ | 83 | socat \ |
| @@ -73,23 +87,12 @@ RDEPENDS:${PN} = "\ | |||
| 73 | skopeo \ | 87 | skopeo \ |
| 74 | " | 88 | " |
| 75 | 89 | ||
| 76 | # Blobs are sourced from vxn-initramfs-create deploy output. | 90 | # squashfs-tools-native for unsquash/resquash of rootfs.img |
| 77 | # Build blobs first: bitbake vxn-initramfs-create | 91 | DEPENDS += "squashfs-tools-native" |
| 78 | # No task dependency here - vxn-initramfs-create is deploy-only (no packages). | 92 | |
| 79 | # Adding any dependency from a packaged recipe to a deploy-only recipe | 93 | # =========================================================================== |
| 80 | # breaks do_rootfs (sstate manifest not found for package_write_rpm). | 94 | # Architecture and multiconfig helpers |
| 81 | 95 | # =========================================================================== | |
| 82 | # Blobs come from DEPLOY_DIR which is untracked by sstate hash. | ||
| 83 | # nostamp on do_install alone is insufficient — do_package and | ||
| 84 | # do_package_write_rpm have unchanged sstate hashes so they restore | ||
| 85 | # the OLD RPM from cache, discarding the fresh do_install output. | ||
| 86 | # Force the entire install→package→RPM chain to always re-run. | ||
| 87 | do_install[nostamp] = "1" | ||
| 88 | do_package[nostamp] = "1" | ||
| 89 | do_packagedata[nostamp] = "1" | ||
| 90 | do_package_write_rpm[nostamp] = "1" | ||
| 91 | do_package_write_ipk[nostamp] = "1" | ||
| 92 | do_package_write_deb[nostamp] = "1" | ||
| 93 | 96 | ||
| 94 | def vxn_get_blob_arch(d): | 97 | def vxn_get_blob_arch(d): |
| 95 | arch = d.getVar('TARGET_ARCH') | 98 | arch = d.getVar('TARGET_ARCH') |
| @@ -109,15 +112,114 @@ def vxn_get_kernel_image_name(d): | |||
| 109 | return 'zImage' | 112 | return 'zImage' |
| 110 | return 'Image' | 113 | return 'Image' |
| 111 | 114 | ||
| 115 | def vxn_get_multiconfig_name(d): | ||
| 116 | arch = d.getVar('TARGET_ARCH') | ||
| 117 | if arch == 'aarch64': | ||
| 118 | return 'vruntime-aarch64' | ||
| 119 | elif arch in ['x86_64', 'i686', 'i586']: | ||
| 120 | return 'vruntime-x86-64' | ||
| 121 | return 'vruntime-aarch64' | ||
| 122 | |||
| 112 | BLOB_ARCH = "${@vxn_get_blob_arch(d)}" | 123 | BLOB_ARCH = "${@vxn_get_blob_arch(d)}" |
| 113 | KERNEL_IMAGETYPE_VXN = "${@vxn_get_kernel_image_name(d)}" | 124 | KERNEL_IMAGETYPE_VXN = "${@vxn_get_kernel_image_name(d)}" |
| 125 | VXN_MULTICONFIG = "${@vxn_get_multiconfig_name(d)}" | ||
| 126 | VXN_RUNTIME = "vdkr" | ||
| 127 | |||
| 128 | # Multiconfig deploy directory (where vdkr-rootfs-image deploys blobs) | ||
| 129 | VXN_MC_DEPLOY = "${TOPDIR}/tmp-${VXN_MULTICONFIG}/deploy/images/${MACHINE}" | ||
| 130 | |||
| 131 | # =========================================================================== | ||
| 132 | # Multiconfig dependencies — ensures blobs are built before do_compile. | ||
| 133 | # mcdepends are cross-config and don't pollute the same-config package | ||
| 134 | # dependency tree, so they don't break image do_rootfs sstate checks. | ||
| 135 | # =========================================================================== | ||
| 136 | |||
| 137 | python () { | ||
| 138 | mc = d.getVar('VXN_MULTICONFIG') | ||
| 139 | runtime = d.getVar('VXN_RUNTIME') | ||
| 140 | bbmulticonfig = (d.getVar('BBMULTICONFIG') or "").split() | ||
| 141 | if mc in bbmulticonfig: | ||
| 142 | mcdeps = ' '.join([ | ||
| 143 | 'mc::%s:%s-tiny-initramfs-image:do_image_complete' % (mc, runtime), | ||
| 144 | 'mc::%s:%s-rootfs-image:do_image_complete' % (mc, runtime), | ||
| 145 | 'mc::%s:virtual/kernel:do_deploy' % mc, | ||
| 146 | ]) | ||
| 147 | d.setVarFlag('do_compile', 'mcdepends', mcdeps) | ||
| 148 | } | ||
| 149 | |||
| 150 | # =========================================================================== | ||
| 151 | # do_compile: source blobs from MC deploy, inject init scripts into rootfs | ||
| 152 | # =========================================================================== | ||
| 153 | # SRC_URI includes vxn-init.sh and vcontainer-init-common.sh, so their | ||
| 154 | # content hashes are part of the task signature. When they change, this | ||
| 155 | # task re-runs and produces a fresh rootfs.img with the updated scripts. | ||
| 156 | |||
| 157 | do_compile() { | ||
| 158 | mkdir -p ${B} | ||
| 159 | |||
| 160 | # Compile vxn-sendtty (SCM_RIGHTS helper for OCI terminal mode) | ||
| 161 | ${CC} ${CFLAGS} ${S}/vxn-sendtty.c ${LDFLAGS} -o ${B}/vxn-sendtty | ||
| 162 | |||
| 163 | MC_DEPLOY="${VXN_MC_DEPLOY}" | ||
| 164 | |||
| 165 | # --- Initramfs --- | ||
| 166 | INITRAMFS_SRC="${MC_DEPLOY}/${VXN_RUNTIME}-tiny-initramfs-image-${MACHINE}.cpio.gz" | ||
| 167 | if [ ! -f "${INITRAMFS_SRC}" ]; then | ||
| 168 | bbfatal "Initramfs not found at ${INITRAMFS_SRC}. Build with: bitbake mc:${VXN_MULTICONFIG}:${VXN_RUNTIME}-tiny-initramfs-image" | ||
| 169 | fi | ||
| 170 | cp "${INITRAMFS_SRC}" ${B}/initramfs.cpio.gz | ||
| 114 | 171 | ||
| 115 | VXN_DEPLOY = "${DEPLOY_DIR_IMAGE}" | 172 | # --- Rootfs (unsquash, inject scripts, resquash) --- |
| 173 | ROOTFS_SRC="${MC_DEPLOY}/${VXN_RUNTIME}-rootfs-image-${MACHINE}.rootfs.squashfs" | ||
| 174 | if [ ! -f "${ROOTFS_SRC}" ]; then | ||
| 175 | bbfatal "Rootfs not found at ${ROOTFS_SRC}. Build with: bitbake mc:${VXN_MULTICONFIG}:${VXN_RUNTIME}-rootfs-image" | ||
| 176 | fi | ||
| 177 | |||
| 178 | UNSQUASH_DIR="${B}/rootfs-unsquash" | ||
| 179 | rm -rf "${UNSQUASH_DIR}" | ||
| 180 | unsquashfs -d "${UNSQUASH_DIR}" "${ROOTFS_SRC}" | ||
| 181 | |||
| 182 | # Inject vxn init scripts (tracked via SRC_URI → task hash changes on edit) | ||
| 183 | install -m 0755 ${S}/vxn-init.sh ${UNSQUASH_DIR}/vxn-init.sh | ||
| 184 | install -m 0755 ${S}/vcontainer-init-common.sh ${UNSQUASH_DIR}/vcontainer-init-common.sh | ||
| 185 | |||
| 186 | rm -f ${B}/rootfs.img | ||
| 187 | mksquashfs "${UNSQUASH_DIR}" ${B}/rootfs.img -noappend -comp xz | ||
| 188 | rm -rf "${UNSQUASH_DIR}" | ||
| 189 | |||
| 190 | # --- Kernel --- | ||
| 191 | KERNEL_FILE="${DEPLOY_DIR_IMAGE}/${KERNEL_IMAGETYPE_VXN}" | ||
| 192 | if [ -f "${KERNEL_FILE}" ]; then | ||
| 193 | cp "${KERNEL_FILE}" ${B}/kernel | ||
| 194 | else | ||
| 195 | bbwarn "Kernel not found at ${KERNEL_FILE}" | ||
| 196 | fi | ||
| 197 | } | ||
| 198 | |||
| 199 | # =========================================================================== | ||
| 200 | # do_install: install CLI scripts (from SRC_URI) and blobs (from do_compile) | ||
| 201 | # =========================================================================== | ||
| 116 | 202 | ||
| 117 | do_install() { | 203 | do_install() { |
| 118 | # Install CLI wrapper | 204 | # Install CLI wrapper, OCI runtime, and sendtty helper |
| 119 | install -d ${D}${bindir} | 205 | install -d ${D}${bindir} |
| 120 | install -m 0755 ${S}/vxn.sh ${D}${bindir}/vxn | 206 | install -m 0755 ${S}/vxn.sh ${D}${bindir}/vxn |
| 207 | install -m 0755 ${S}/vxn-oci-runtime ${D}${bindir}/vxn-oci-runtime | ||
| 208 | install -m 0755 ${B}/vxn-sendtty ${D}${bindir}/vxn-sendtty | ||
| 209 | |||
| 210 | # Install containerd config (makes vxn-oci-runtime the default CRI runtime) | ||
| 211 | install -d ${D}${sysconfdir}/containerd | ||
| 212 | install -m 0644 ${S}/containerd-config-vxn.toml ${D}${sysconfdir}/containerd/config.toml | ||
| 213 | |||
| 214 | # Install vxn shim wrapper: PATH trick makes runc shim find vxn-oci-runtime | ||
| 215 | install -m 0755 ${S}/containerd-shim-vxn-v2 ${D}${bindir}/containerd-shim-vxn-v2 | ||
| 216 | |||
| 217 | # Private shim dir: runc symlink so the runc shim execs vxn-oci-runtime | ||
| 218 | install -d ${D}${libexecdir}/vxn/shim | ||
| 219 | ln -sf ${bindir}/vxn-oci-runtime ${D}${libexecdir}/vxn/shim/runc | ||
| 220 | |||
| 221 | # Install vctr convenience wrapper | ||
| 222 | install -m 0755 ${S}/vctr ${D}${bindir}/vctr | ||
| 121 | 223 | ||
| 122 | # Install shared scripts into libdir | 224 | # Install shared scripts into libdir |
| 123 | install -d ${D}${libdir}/vxn | 225 | install -d ${D}${libdir}/vxn |
| @@ -126,39 +228,39 @@ do_install() { | |||
| 126 | install -m 0755 ${S}/vrunner-backend-qemu.sh ${D}${libdir}/vxn/ | 228 | install -m 0755 ${S}/vrunner-backend-qemu.sh ${D}${libdir}/vxn/ |
| 127 | install -m 0644 ${S}/vcontainer-common.sh ${D}${libdir}/vxn/ | 229 | install -m 0644 ${S}/vcontainer-common.sh ${D}${libdir}/vxn/ |
| 128 | 230 | ||
| 129 | # Install blobs from vxn-initramfs-create deployment | 231 | # Install blobs from do_compile output |
| 130 | # Layout must match what vrunner backends expect: $BLOB_DIR/<arch>/{Image,initramfs.cpio.gz,rootfs.img} | ||
| 131 | install -d ${D}${datadir}/vxn/${BLOB_ARCH} | 232 | install -d ${D}${datadir}/vxn/${BLOB_ARCH} |
| 132 | 233 | ||
| 133 | VXN_BLOB_SRC="${VXN_DEPLOY}/vxn/${BLOB_ARCH}" | 234 | if [ -f "${B}/kernel" ]; then |
| 134 | if [ -d "${VXN_BLOB_SRC}" ]; then | 235 | install -m 0644 "${B}/kernel" ${D}${datadir}/vxn/${BLOB_ARCH}/${KERNEL_IMAGETYPE_VXN} |
| 135 | if [ -f "${VXN_BLOB_SRC}/${KERNEL_IMAGETYPE_VXN}" ]; then | 236 | bbnote "Installed kernel ${KERNEL_IMAGETYPE_VXN}" |
| 136 | install -m 0644 "${VXN_BLOB_SRC}/${KERNEL_IMAGETYPE_VXN}" ${D}${datadir}/vxn/${BLOB_ARCH}/ | 237 | else |
| 137 | bbnote "Installed kernel ${KERNEL_IMAGETYPE_VXN}" | 238 | bbwarn "Kernel blob not found in build dir" |
| 138 | else | 239 | fi |
| 139 | bbwarn "Kernel not found at ${VXN_BLOB_SRC}/${KERNEL_IMAGETYPE_VXN}" | 240 | |
| 140 | fi | 241 | if [ -f "${B}/initramfs.cpio.gz" ]; then |
| 141 | 242 | install -m 0644 "${B}/initramfs.cpio.gz" ${D}${datadir}/vxn/${BLOB_ARCH}/ | |
| 142 | if [ -f "${VXN_BLOB_SRC}/initramfs.cpio.gz" ]; then | 243 | bbnote "Installed initramfs" |
| 143 | install -m 0644 "${VXN_BLOB_SRC}/initramfs.cpio.gz" ${D}${datadir}/vxn/${BLOB_ARCH}/ | 244 | else |
| 144 | bbnote "Installed initramfs" | 245 | bbwarn "Initramfs blob not found in build dir" |
| 145 | else | 246 | fi |
| 146 | bbwarn "Initramfs not found at ${VXN_BLOB_SRC}/initramfs.cpio.gz" | 247 | |
| 147 | fi | 248 | if [ -f "${B}/rootfs.img" ]; then |
| 148 | 249 | install -m 0644 "${B}/rootfs.img" ${D}${datadir}/vxn/${BLOB_ARCH}/ | |
| 149 | if [ -f "${VXN_BLOB_SRC}/rootfs.img" ]; then | 250 | bbnote "Installed rootfs.img" |
| 150 | install -m 0644 "${VXN_BLOB_SRC}/rootfs.img" ${D}${datadir}/vxn/${BLOB_ARCH}/ | ||
| 151 | bbnote "Installed rootfs.img" | ||
| 152 | else | ||
| 153 | bbwarn "Rootfs not found at ${VXN_BLOB_SRC}/rootfs.img" | ||
| 154 | fi | ||
| 155 | else | 251 | else |
| 156 | bbwarn "VXN blob directory not found at ${VXN_BLOB_SRC}. Build with: bitbake vxn-initramfs-create" | 252 | bbwarn "Rootfs blob not found in build dir" |
| 157 | fi | 253 | fi |
| 158 | } | 254 | } |
| 159 | 255 | ||
| 160 | FILES:${PN} = "\ | 256 | FILES:${PN} = "\ |
| 161 | ${bindir}/vxn \ | 257 | ${bindir}/vxn \ |
| 258 | ${bindir}/vxn-oci-runtime \ | ||
| 259 | ${bindir}/vxn-sendtty \ | ||
| 260 | ${bindir}/containerd-shim-vxn-v2 \ | ||
| 261 | ${bindir}/vctr \ | ||
| 262 | ${libexecdir}/vxn/ \ | ||
| 263 | ${sysconfdir}/containerd/config.toml \ | ||
| 162 | ${libdir}/vxn/ \ | 264 | ${libdir}/vxn/ \ |
| 163 | ${datadir}/vxn/ \ | 265 | ${datadir}/vxn/ \ |
| 164 | " | 266 | " |
