From ad7e63d78c22b2895a0ccfafc976bf0d0ebaf58a Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sun, 28 May 2023 21:35:13 +0200 Subject: [PATCH] Boot from CD-ROM works. --- org.jdrupes.vmoperator.manager/build.gradle | 2 +- .../build.gradle | 4 +- .../config-sample.yaml | 14 ++- .../vmoperator/runner/qemu/Configuration.java | 18 +++- .../vmoperator/runner/qemu/Runner.java | 6 +- .../templates/Standard-VM-latest.ftl.yaml | 99 +++++++++++++++++-- 6 files changed, 122 insertions(+), 21 deletions(-) diff --git a/org.jdrupes.vmoperator.manager/build.gradle b/org.jdrupes.vmoperator.manager/build.gradle index 8efcc35..f28c8fe 100644 --- a/org.jdrupes.vmoperator.manager/build.gradle +++ b/org.jdrupes.vmoperator.manager/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'org.jgrapes:org.jgrapes.core:[1.19.0,2)' implementation 'org.jgrapes:org.jgrapes.io:[2.5.0,3)' implementation 'org.jgrapes:org.jgrapes.http:[3.1.0,4)' - implementation 'org.jgrapes:org.jgrapes.util:[1.26.0,2)' + implementation 'org.jgrapes:org.jgrapes.util:[1.28.0,2)' } application { diff --git a/org.jdrupes.vmoperator.runner.qemu/build.gradle b/org.jdrupes.vmoperator.runner.qemu/build.gradle index 9b7be88..f794975 100644 --- a/org.jdrupes.vmoperator.runner.qemu/build.gradle +++ b/org.jdrupes.vmoperator.runner.qemu/build.gradle @@ -12,10 +12,10 @@ dependencies { implementation 'org.jgrapes:org.jgrapes.core:[1.19.0,2)' implementation 'org.jgrapes:org.jgrapes.io:[2.5.0,3)' implementation 'org.jgrapes:org.jgrapes.http:[3.1.0,4)' - implementation 'org.jgrapes:org.jgrapes.util:[1.26.0,2)' + implementation 'org.jgrapes:org.jgrapes.util:[1.28.0,2)' implementation 'org.freemarker:freemarker:[2.3.32,2.4)' - implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:[2.15.0,3]' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:[2.15.1,3]' implementation project(':org.jdrupes.vmoperator.util') diff --git a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml index 54d8ee8..b55a881 100644 --- a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml +++ b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml @@ -43,8 +43,8 @@ # "bootMode": "uefi" # RAM settings - # "maximumRam": "512M" - # "currentRam": "512M" + # "maximumRam": "1G" + # "currentRam": "1G" # CPU settings # "cpuModel": "host" @@ -59,4 +59,12 @@ # "threadsPerCore": 0 # "accelertor": "kvm" - \ No newline at end of file + # RTC settings. + # "rtcBase": "utc" + # "rtcClock": "rt" + + "drives": + - "type": "ide-cd" + "bootindex": (undefined) + "file": (undefined) + \ No newline at end of file diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java index 1d9002c..7d22565 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java @@ -53,7 +53,7 @@ class Configuration implements Dto { /** * Subsection "vm". */ - @SuppressWarnings("PMD.ShortClassName") + @SuppressWarnings({ "PMD.ShortClassName", "PMD.TooManyFields" }) public static class Vm implements Dto { public String name; public String uuid; @@ -69,6 +69,18 @@ class Configuration implements Dto { public int coresPerDie; public int threadsPerCore; public String accelerator = "kvm"; + public String rtcBase = "utc"; + public String rtcClock = "rt"; + public Drive[] drives; + } + + /** + * Subsection "drive". + */ + public static class Drive implements Dto { + public String type; + public Integer bootindex; + public String file; } /** @@ -76,9 +88,6 @@ class Configuration implements Dto { * * @return true, if successful */ - @SuppressWarnings({ "PMD.AvoidDeeplyNestedIfStmts", - "PMD.CognitiveComplexity", "PMD.CyclomaticComplexity", - "PMD.NPathComplexity" }) public boolean check() { if (vm == null || vm.name == null) { logger.severe(() -> "Configuration is missing mandatory entries."); @@ -127,6 +136,7 @@ class Configuration implements Dto { return true; } + @SuppressWarnings("PMD.AvoidDeeplyNestedIfStmts") private boolean checkDataDir() { // Data directory if (dataDir == null) { diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java index 72590d4..405a1f7 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java @@ -19,6 +19,7 @@ package org.jdrupes.vmoperator.runner.qemu; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -143,6 +144,9 @@ public class Runner extends Component { */ public Runner() throws IOException { super(new Context()); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, + false); + // Get defaults defaults = mapper.readValue( Runner.class.getResourceAsStream("defaults.yaml"), JsonNode.class); @@ -153,7 +157,7 @@ public class Runner extends Component { fmConfig.setDirectoryForTemplateLoading(new File("/")); fmConfig.setDefaultEncoding("utf-8"); fmConfig.setObjectWrapper(new ExtendedObjectWrapper( - fmConfig.getIncompatibleImprovements(), mapper)); + fmConfig.getIncompatibleImprovements())); fmConfig.setTemplateExceptionHandler( TemplateExceptionHandler.RETHROW_HANDLER); fmConfig.setLogTemplateExceptions(false); diff --git a/org.jdrupes.vmoperator.runner.qemu/templates/Standard-VM-latest.ftl.yaml b/org.jdrupes.vmoperator.runner.qemu/templates/Standard-VM-latest.ftl.yaml index 9e6fae5..848cc3c 100644 --- a/org.jdrupes.vmoperator.runner.qemu/templates/Standard-VM-latest.ftl.yaml +++ b/org.jdrupes.vmoperator.runner.qemu/templates/Standard-VM-latest.ftl.yaml @@ -23,23 +23,38 @@ # Useful links: # - https://joonas.fi/2021/02/uefi-pc-boot-process-and-uefi-with-qemu/ "arguments": + # Qemu configuration - "-no-user-config" + # * https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt + - [ "-global", "kvm-pit.lost_tick_policy=delay" ] + - [ "-sandbox", "on,obsolete=deny,elevateprivileges=deny,\ + spawn=allow,resourcecontrol=deny" ] + - [ "-msg", "timestamp=on" ] + # * Qemu monitor connection + - [ "-chardev", "socket,id=charmonitor,\ + path=${ runtimeDir }/monitor.sock,server=on,wait=off" ] + - [ "-mon", "chardev=charmonitor,id=monitor,mode=control" ] + + # VM configuration - [ "-name", "guest=${ vm.name },debug-threads=on" ] - [ "-uuid", "${ vm.uuid }"] - # Configure "modern" machine (pc-q35-7.0). USB is off, because we - # configure (better) xhci later. No VMWare IO port (obviously). - # For smm=on see https://scumjr.github.io/2016/01/04/playing-with-smm-and-qemu/. - # Configure ROM/EEPROM for UEFI. + # * Configure "modern" machine (pc-q35-7.0). USB is off, because we + # configure (better) xhci later. No VMWare IO port (obviously). + # For smm=on see https://scumjr.github.io/2016/01/04/playing-with-smm-and-qemu/. + # Configure ROM/EEPROM for UEFI. - [ "-machine", "pc-q35-7.0,usb=off,vmport=off,dump-guest-core=off\ <#if vm.bootMode == "secure">,smm=on\ <#if vm.bootMode != "bios">,pflash0=fw-rom-device\ ,pflash1=fw-eeprom-device,memory-backend=pc.ram,hpet=off" ] + # * https://bugzilla.redhat.com/show_bug.cgi?id=1170533, may be unnecessary + - [ "-global", "ICH9-LPC.disable_s3=1" ] + - [ "-global", "ICH9-LPC.disable_s4=1" ] # {{- if .Values.vm.secureBoot }} # -global driver=cfi.pflash01,property=secure,value=on # -object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/local/qemu/master-key.aes"}' # {{- end }} <#if vm.bootMode != "bios"> - # Provide ROM/EEPROM devices (instead of built-in BIOS) + # * Provide ROM/EEPROM devices (instead of built-in BIOS) - [ "-blockdev", "node-name=fw-rom-file,driver=file,\ filename=${ firmwareRom },auto-read-only=true,discard=unmap" ] - [ "-blockdev", "node-name=fw-rom-device,driver=raw,\ @@ -49,9 +64,10 @@ - [ "-blockdev", "node-name=fw-eeprom-device,driver=raw,\ read-only=false,file=fw-eeprom-file" ] - # Provide RAM + # * Provide RAM - [ "-object", "memory-backend-ram,id=pc.ram,\ - size=${ vm.maximumRam!"512M" }" ] + size=${ vm.maximumRam!"1G" }" ] + - [ "-m", "${ vm.maximumRam!"1G" }" ] <#if vm.useTpm> # Attach TPM - [ "-chardev", "socket,id=chrtpm,path=${ runtimeDir }/swtpm-sock" ] @@ -69,10 +85,73 @@ <#if vm.accelerator != "none"> - [ "-accel", "${ vm.accelerator }"] - - - [ "-chardev", "socket,id=charmonitor,path=${ runtimeDir }/monitor.sock,server=on,wait=off" ] - - [ "-mon", "chardev=charmonitor,id=monitor,mode=control" ] + # (More devices:) + # * RTC + - [ "-rtc", "base=${ vm.rtcBase },clock=${ vm.rtcClock },driftfix=slew" ] + # On-board serial, made available as pty on host (not used) + - [ "-chardev", "pty,id=ptyserial0" ] + - [ "-device", "isa-serial,chardev=ptyserial0,id=serial0,index=0" ] + # * PCI Serial device(s) (more in SPICE configuration below) + # Best explanation found: + # https://fedoraproject.org/wiki/Features/VirtioSerial + - [ "-device", "virtio-serial-pci,id=virtio-serial0" ] + # - Guest agent serial connection + - [ "-chardev","socket,id=guest-agent-socket,\ + path=${ runtimeDir }/org.qemu.guest_agent.0,server=on,wait=off" ] + - [ "-device", "virtserialport,bus=virtio-serial0.0,nr=1,\ + chardev=guest-agent-socket,id=channel0,name=org.qemu.guest_agent.0" ] + # * USB Hub and devices (more in SPICE configuration below) + # https://qemu-project.gitlab.io/qemu/system/devices/usb.html + # https://github.com/qemu/qemu/blob/master/hw/usb/hcd-xhci.c + - [ "-device", "qemu-xhci,p2=15,p3=15,id=usb" ] + - [ "-device", "usb-tablet" ] + # * Random number generator + - [ "-object", "rng-random,id=objrng0,filename=/dev/random" ] + - [ "-device", "virtio-rng-pci,rng=objrng0,id=rng0" ] + # * Graphics and Audio Card + - [ "-device", "virtio-vga,id=video0,max_outputs=1" ] + - [ "-device", "ich9-intel-hda,id=sound0" ] + # Drives + # * CD-Drives + <#assign cdCounter = 0/> + <#list vm.drives![] as drive> + <#if (drive.type!"hdd") == "ide-cd"> + - [ "-drive", "id=drive-cdrom${ cdCounter },if=none,media=cdrom\ + <#if drive.file??>,file=${ drive.file }" ] + # (IDE is old, but faster than usb-storage. virtio-blk-pci does not + # work without file [empty drive]) + - [ "-device", "ide-cd,id=cd${ cdCounter },drive=drive-cdrom${ cdCounter }\ + <#if drive.bootindex??>,bootindex=${ drive.bootindex }" ] + <#assign cdCounter += 1/> + + + + # - how to access the resource on the host (a file) +# - [ "-blockdev", "node-name=blockdev-cdrom-file,driver=file,\ +# filename=/home/mnl/Downloads/archlinux-2023.05.03-x86_64.iso" ] + # - how to use the file (as sequence of literal blocks) +# - [ "-blockdev", "node-name=blockdev-cdrom-backend,driver=raw,\ +# read-only=true,file=blockdev-cdrom-file" ] + # - the driver (what the guest sees) +# - [ "-device", "virtio-blk-pci,id=virtio-disk-cdrom,\ +# drive=blockdev-cdrom-backend,bootindex=1" ] + + + # SPICE (display, channels ...) # - [ "-spice", "port=5900,disable-ticketing=on" ] +# -chardev spicevmc,id=charchannel1,name=vdagent +# -device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel1,id=channel1,name=com.redhat.spice.0 +# -audiodev '{"id":"audio1","driver":"spice"}' +# - [ "-device", "hda-duplex,\ +# id=sound0-codec0,bus=sound0.0,cad=0,audiodev=audio1" ] +# -spice port={{ .Values.vm.spicePort }},addr=0.0.0.0,disable-ticketing=on,seamless-migration=on +# -chardev spicevmc,id=charredir0,name=usbredir +# -device usb-redir,chardev=charredir0,id=redir0,bus=usb.0,port=2 +# -chardev spicevmc,id=charredir1,name=usbredir +# -device usb-redir,chardev=charredir1,id=redir1,bus=usb.0,port=3 +# -device virtio-balloon-pci,id=balloon0 + + "monitorMessages": "connect": '{ "execute": "qmp_capabilities" }' \ No newline at end of file