Compare commits

...

11 Commits
v4.26 ... v4.30

Author SHA1 Message Date
Kroese
04bd8a1639 feat: Check host connection 2023-11-15 21:52:03 +01:00
Kroese
a024294e19 fix: Folder structure (#379)
* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* Moved to src

* fix: Check entrypoint

* Moved to src

* Moved to src

* fix: Relative paths

* fix: Relative paths

* fix: Shellcheck

* fix: Relative paths

* Test shellcheck

* Test shellcheck
2023-11-15 20:54:51 +01:00
Kroese
0cd1ddf0a1 feat: Cache DSM info (#378)
* feat: Cache location

* feat: Cache location

* feat: Add support URL

* feat: Cache location

* fix: Remove files

* feat: Reset filesystem

* fix: Exit when PID is missing

* fix: Counter file

* fix: Check flags

* docs: Readme

* feat: Cleanup files

* fix: Check flags

* fix: Check flags

* fix: Initialization

* fix: Initalization

* fix: Initialization

* fix: Cleanup temp

* fix: Initialize system

* feat: Config system

* feat: Configure system

* fix: Variables

* fix: Variables

* fix: Error handling

* style: Comments

* fix: Returnvalue

* fix: Returnvalue

* fix: Returnvalue

* fix: Returnvalue

* fix: Returnvalue

* docs: Multi-disk support

* feat: Use cached location

* fix: Swap order
2023-11-15 19:58:51 +01:00
Kroese
98245a1efe Fix for issue #373
Fix for issue #373
2023-11-12 02:10:18 +01:00
Kroese
f425c869c6 Fix for issue https://github.com/vdsm/virtual-dsm/issues/373 2023-11-12 02:05:34 +01:00
Kroese
f5b8c2a2ef feat: Configure sizes for multiple disks
feat: Configure sizes for multiple disks
2023-11-11 17:22:08 +01:00
Kroese
f44584261e fix: Increase delay 2023-11-11 17:06:56 +01:00
Kroese
4134d9e3d3 fix: Display shutdown counter 2023-11-11 17:05:58 +01:00
Kroese
db47f561d3 feat: Configure sizes for multiple disks 2023-11-11 16:51:58 +01:00
Kroese
895bc04a57 feat: Configure sizes for multiple disks 2023-11-11 16:50:08 +01:00
Kroese
d9e882fce4 fix: Remove flags 2023-11-11 15:56:34 +01:00
18 changed files with 386 additions and 273 deletions

View File

@@ -11,4 +11,4 @@ jobs:
- name: Run ShellCheck - name: Run ShellCheck
uses: ludeeus/action-shellcheck@master uses: ludeeus/action-shellcheck@master
env: env:
SHELLCHECK_OPTS: -x -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 -e SC2317 -e SC2028 -e SC2153 -e SC2004 SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 -e SC2317 -e SC2028 -e SC2153 -e SC2004

View File

@@ -33,9 +33,8 @@ RUN apt-get update && apt-get -y upgrade && \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY run/*.sh /run/ COPY src/*.sh /run/
COPY --from=builder /qemu-host.bin /run/host.bin COPY --from=builder /qemu-host.bin /run/host.bin
RUN chmod +x /run/*.sh && chmod +x /run/*.bin RUN chmod +x /run/*.sh && chmod +x /run/*.bin
VOLUME /storage VOLUME /storage
@@ -66,4 +65,4 @@ LABEL org.opencontainers.image.description="Virtual DSM in a docker container"
HEALTHCHECK --interval=60s --retries=2 CMD /run/check.sh HEALTHCHECK --interval=60s --retries=2 CMD /run/check.sh
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/run.sh"] ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]

View File

@@ -17,7 +17,6 @@ Virtual DSM in a docker container.
- Multi-platform - Multi-platform
- KVM acceleration - KVM acceleration
- GPU passthrough - GPU passthrough
- Graceful shutdowns
- Upgrades supported - Upgrades supported
## Usage ## Usage
@@ -85,6 +84,32 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
``` ```
Keep in mind that this will not affect any of your existing disks, it only applies to newly created disks. Keep in mind that this will not affect any of your existing disks, it only applies to newly created disks.
* ### How do I add multiple disks?
To mount extra volumes, modify your compose file like this:
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- /home/example2:/storage2
- /home/example3:/storage3
```
Additionally, it's also possible to passthrough raw disk devices like this:
```yaml
environment:
DEVICE2: "/dev/vdc1"
DEVICE3: "/dev/vdc2"
devices:
- /dev/vdc1
- /dev/vdc2
```
Please beware that any pre-existing partitions and data on those devices will be wiped.
* ### How do I increase the amount of CPU or RAM? * ### How do I increase the amount of CPU or RAM?

View File

@@ -1,50 +0,0 @@
#!/usr/bin/env bash
set -u
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
[ -f "/run/qemu.counter" ] && echo "QEMU is shutting down.." && exit 1
# Retrieve IP from guest VM for Docker healthcheck
RESPONSE=$(curl -s -m 16 -S http://127.0.0.1:2210/read?command=10 2>&1)
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then
echo "Failed to connect to guest: $RESPONSE" && exit 1
fi
# Retrieve the HTTP port number
if [[ ! "${RESPONSE}" =~ "\"http_port\"" ]] ; then
echo "Failed to parse response from guest: $RESPONSE" && exit 1
fi
rest=${RESPONSE#*http_port}
rest=${rest#*:}
rest=${rest%%,*}
PORT=${rest%%\"*}
[ -z "${PORT}" ] && echo "Guest has not set a portnumber yet.." && exit 1
# Retrieve the IP address
if [[ ! "${RESPONSE}" =~ "eth0" ]] ; then
echo "Failed to parse response from guest: $RESPONSE" && exit 1
fi
rest=${RESPONSE#*eth0}
rest=${rest#*ip}
rest=${rest#*:}
rest=${rest#*\"}
IP=${rest%%\"*}
[ -z "${IP}" ] && echo "Guest has not received an IP yet.." && exit 1
if ! curl -m 3 -ILfSs "http://${IP}:${PORT}/" > /dev/null; then
echo "Failed to reach ${IP}:${PORT}"
exit 1
fi
if [[ "$IP" == "20.20"* ]]; then
echo "Healthcheck OK"
else
echo "Healthcheck OK ( $IP )"
fi
exit 0

View File

@@ -1,74 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Configure QEMU for graceful shutdown
QEMU_MONPORT=7100
QEMU_POWERDOWN_TIMEOUT=50
_QEMU_PID=/run/qemu.pid
_QEMU_SHUTDOWN_COUNTER=/run/qemu.counter
rm -f "${_QEMU_PID}"
rm -f "${_QEMU_SHUTDOWN_COUNTER}"
_trap(){
func="$1" ; shift
for sig ; do
trap "$func $sig" "$sig"
done
}
_graceful_shutdown() {
set +e
[ ! -f "${_QEMU_PID}" ] && return
[ -f "${_QEMU_SHUTDOWN_COUNTER}" ] && return
echo && info "Received $1 signal, shutting down..."
echo 0 > "${_QEMU_SHUTDOWN_COUNTER}"
# Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_MONPORT}" > /dev/null
# Send shutdown command to guest agent via serial port
RESPONSE=$(curl -s -m 5 -S http://127.0.0.1:2210/read?command=6 2>&1)
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then
echo && error "Could not send shutdown command to the guest ($RESPONSE)"
kill -15 "$(cat "${_QEMU_PID}")"
pkill -f qemu-system-x86_64 || true
fi
while [ "$(cat ${_QEMU_SHUTDOWN_COUNTER})" -lt "${QEMU_POWERDOWN_TIMEOUT}" ]; do
# Increase the counter
echo $(($(cat ${_QEMU_SHUTDOWN_COUNTER})+1)) > ${_QEMU_SHUTDOWN_COUNTER}
# Try to connect to qemu
if echo 'info version'| nc -q 1 -w 1 localhost "${QEMU_MONPORT}" >/dev/null 2>&1 ; then
sleep 1
#CNT="$(cat ${_QEMU_SHUTDOWN_COUNTER})/${QEMU_POWERDOWN_TIMEOUT}"
#[[ "${DEBUG}" == [Yy1]* ]] && info "Shutting down, waiting... (${CNT})"
fi
done
echo && echo " Quitting..."
echo 'quit' | nc -q 1 -w 1 localhost "${QEMU_MONPORT}" >/dev/null 2>&1 || true
closeNetwork
return
}
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
MON_OPTS="-monitor telnet:localhost:${QEMU_MONPORT},server,nowait,nodelay"

View File

@@ -1,97 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: ${URL:=''} # URL of the PAT file
: ${GPU:='N'} # Enable GPU passthrough
: ${DEBUG:='N'} # Enable debugging mode
: ${ALLOCATE:='Y'} # Preallocate diskspace
: ${ARGUMENTS:=''} # Extra QEMU parameters
: ${CPU_CORES:='1'} # Amount of CPU cores
: ${DISK_SIZE:='16G'} # Initial data disk size
: ${RAM_SIZE:='512M'} # Maximum RAM amount
echo " Starting Virtual DSM for Docker v${VERSION}..."
info () { echo -e "\E[1;34m \E[1;36m$1\E[0m" ; }
error () { echo -e >&2 "\E[1;31m ERROR: $1\E[0m" ; }
trap 'error "Status $? while: ${BASH_COMMAND} (line $LINENO/$BASH_LINENO)"' ERR
[ ! -f "/run/run.sh" ] && error "Script must run inside Docker container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
STORAGE="/storage"
KERNEL=$(uname -r | cut -b 1)
MINOR=$(uname -r | cut -d '.' -f2)
ARCH=$(dpkg --print-architecture)
VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1)
[ ! -d "$STORAGE" ] && error "Storage folder (${STORAGE}) not found!" && exit 13
if [ -f "$STORAGE"/dsm.ver ]; then
BASE=$(cat "${STORAGE}/dsm.ver")
else
# Fallback for old installs
BASE="DSM_VirtualDSM_42962"
fi
[ -n "$URL" ] && BASE=$(basename "$URL" .pat)
if [[ ! -f "$STORAGE/$BASE.boot.img" ]] || [[ ! -f "$STORAGE/$BASE.system.img" ]]; then
. /run/install.sh
fi
. /run/disk.sh # Initialize disks
. /run/network.sh # Initialize network
. /run/serial.sh # Initialize serialport
. /run/power.sh # Configure shutdown
KVM_ERR=""
KVM_OPTS=""
if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
fi
else
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
fi
if [ -n "${KVM_ERR}" ]; then
if [ "$ARCH" == "amd64" ]; then
error "KVM acceleration not detected ${KVM_ERR}, see the FAQ about this."
[[ "${DEBUG}" != [Yy1]* ]] && exit 88
fi
else
KVM_OPTS=",accel=kvm -enable-kvm -cpu host"
fi
DEF_OPTS="-nographic -nodefaults -boot strict=on -display none"
RAM_OPTS=$(echo "-m ${RAM_SIZE}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
CPU_OPTS="-smp ${CPU_CORES},sockets=1,dies=1,cores=${CPU_CORES},threads=1"
MAC_OPTS="-machine type=q35,usb=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
EXTRA_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
EXTRA_OPTS="$EXTRA_OPTS -object rng-random,id=objrng0,filename=/dev/urandom"
EXTRA_OPTS="$EXTRA_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
[[ "${GPU}" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]] && . /run/gpu.sh
ARGS="${DEF_OPTS} ${CPU_OPTS} ${RAM_OPTS} ${MAC_OPTS} ${MON_OPTS} ${SERIAL_OPTS} ${NET_OPTS} ${DISK_OPTS} ${EXTRA_OPTS} ${ARGUMENTS}"
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
trap - ERR
set -m
(
[[ "${DEBUG}" == [Yy1]* ]] && info "$VERS" && set -x
qemu-system-x86_64 ${ARGS:+ $ARGS} & echo $! > "${_QEMU_PID}"
{ set +x; } 2>/dev/null
)
set +m
#if (( KERNEL > 5 )) || ( (( KERNEL == 5 )) && (( MINOR > 2 )) ); then
# pidwait -F "${_QEMU_PID}" & wait $!
#else
tail --pid "$(cat "${_QEMU_PID}")" --follow /dev/null & wait $!

61
src/check.sh Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -u
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
[ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1
file="/run/dsm.url"
if [ ! -f "$file" ]; then
# Retrieve IP from guest VM for Docker healthcheck
RESPONSE=$(curl -s -m 16 -S http://127.0.0.1:2210/read?command=10 2>&1)
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then
echo "Failed to connect to guest: $RESPONSE" && exit 1
fi
# Retrieve the HTTP port number
if [[ ! "${RESPONSE}" =~ "\"http_port\"" ]] ; then
echo "Failed to parse response from guest: $RESPONSE" && exit 1
fi
rest=${RESPONSE#*http_port}
rest=${rest#*:}
rest=${rest%%,*}
PORT=${rest%%\"*}
[ -z "${PORT}" ] && echo "Guest has not set a portnumber yet.." && exit 1
# Retrieve the IP address
if [[ ! "${RESPONSE}" =~ "eth0" ]] ; then
echo "Failed to parse response from guest: $RESPONSE" && exit 1
fi
rest=${RESPONSE#*eth0}
rest=${rest#*ip}
rest=${rest#*:}
rest=${rest#*\"}
IP=${rest%%\"*}
[ -z "${IP}" ] && echo "Guest has not received an IP yet.." && exit 1
echo "${IP}:${PORT}" > $file
fi
LOCATION=$(cat "$file")
if ! curl -m 20 -ILfSs "http://${LOCATION}/" > /dev/null; then
rm -f $file
echo "Failed to reach http://${LOCATION}"
exit 1
fi
if [[ "$LOCATION" == "20.20"* ]]; then
echo "Healthcheck OK"
else
echo "Healthcheck OK ( ${LOCATION%:*} )"
fi
exit 0

40
src/config.sh Normal file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
set -Eeuo pipefail
KVM_ERR=""
KVM_OPTS=""
if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
fi
else
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
fi
if [ -n "${KVM_ERR}" ]; then
if [ "$ARCH" == "amd64" ]; then
error "KVM acceleration not detected ${KVM_ERR}, see the FAQ about this."
[[ "${DEBUG}" != [Yy1]* ]] && exit 88
fi
else
KVM_OPTS=",accel=kvm -enable-kvm -cpu host"
fi
DEF_OPTS="-nographic -nodefaults -boot strict=on -display none"
RAM_OPTS=$(echo "-m ${RAM_SIZE}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
CPU_OPTS="-smp ${CPU_CORES},sockets=1,dies=1,cores=${CPU_CORES},threads=1"
MAC_OPTS="-machine type=q35,usb=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
EXTRA_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
EXTRA_OPTS="$EXTRA_OPTS -object rng-random,id=objrng0,filename=/dev/urandom"
EXTRA_OPTS="$EXTRA_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
if [[ "${GPU}" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then
DEF_OPTS="-nodefaults -boot strict=on -display egl-headless,rendernode=/dev/dri/renderD128"
DEF_OPTS="${DEF_OPTS} -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1"
fi
ARGS="${DEF_OPTS} ${CPU_OPTS} ${RAM_OPTS} ${MAC_OPTS} ${MON_OPTS} ${SERIAL_OPTS} ${NET_OPTS} ${DISK_OPTS} ${EXTRA_OPTS} ${ARGUMENTS}"
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
return 0

View File

@@ -21,10 +21,11 @@ if [[ ! -f "${DATA}" ]] && [[ -f "$STORAGE/data$DISK_SIZE.img" ]]; then
DATA="$STORAGE/data$DISK_SIZE.img" DATA="$STORAGE/data$DISK_SIZE.img"
fi fi
MIN_SIZE=6442450944
DISK_SIZE=$(echo "${DISK_SIZE}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') DISK_SIZE=$(echo "${DISK_SIZE}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
DATA_SIZE=$(numfmt --from=iec "${DISK_SIZE}") DATA_SIZE=$(numfmt --from=iec "${DISK_SIZE}")
if (( DATA_SIZE < 6442450944 )); then if (( DATA_SIZE < MIN_SIZE )); then
error "Please increase DISK_SIZE to at least 6 GB." && exit 83 error "Please increase DISK_SIZE to at least 6 GB." && exit 83
fi fi
@@ -124,9 +125,6 @@ if [ ! -f "${DATA}" ]; then
error "Virtual disk does not exist ($DATA)" && exit 88 error "Virtual disk does not exist ($DATA)" && exit 88
fi fi
# Format as BTRFS filesystem
# mkfs.btrfs -q -L data -d single -m dup "${DATA}" > /dev/null
fi fi
# Check the filesize # Check the filesize
@@ -147,34 +145,60 @@ DISK_OPTS="\
-drive file=${DATA},if=none,id=drive-userdata,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -drive file=${DATA},if=none,id=drive-userdata,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \
-device scsi-hd,bus=hw-userdata.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata,id=userdata0,rotation_rate=${DISK_ROTATION},bootindex=3" -device scsi-hd,bus=hw-userdata.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata,id=userdata0,rotation_rate=${DISK_ROTATION},bootindex=3"
: ${DISK2_SIZE:=''}
EXTRA_SIZE=DISK2_SIZE
EXTRA_DISK="/storage2/data.img" EXTRA_DISK="/storage2/data.img"
if [ -f "${EXTRA_DISK}" ]; then if [ -d "$(dirname "${EXTRA_DISK}")" ]; then
if [ ! -f "${EXTRA_DISK}" ]; then
[ -z "$EXTRA_SIZE" ] && EXTRA_SIZE="16G"
if ! truncate -s "${EXTRA_SIZE}" "${EXTRA_DISK}"; then
error "Could not create the file for the second disk." && exit 53
fi
fi
if [ -n "$EXTRA_SIZE" ]; then
CUR_SIZE=$(stat -c%s "${EXTRA_DISK}")
DATA_SIZE=$(numfmt --from=iec "${EXTRA_SIZE}")
if [ "$DATA_SIZE" -gt "$CUR_SIZE" ]; then
truncate -s "${EXTRA_SIZE}" "${EXTRA_DISK}"
fi
fi
DISK_OPTS="${DISK_OPTS} \ DISK_OPTS="${DISK_OPTS} \
-device virtio-scsi-pci,id=hw-userdata2,bus=pcie.0,addr=0xd \ -device virtio-scsi-pci,id=hw-userdata2,bus=pcie.0,addr=0xd \
-drive file=${EXTRA_DISK},if=none,id=drive-userdata2,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -drive file=${EXTRA_DISK},if=none,id=drive-userdata2,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \
-device scsi-hd,bus=hw-userdata2.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata2,id=userdata2,rotation_rate=${DISK_ROTATION},bootindex=4" -device scsi-hd,bus=hw-userdata2.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata2,id=userdata2,rotation_rate=${DISK_ROTATION},bootindex=4"
else
[ -d "$(dirname "${EXTRA_DISK}")" ] && error "Disk image ${EXTRA_DISK} does not exist! Please supply an empty file of at least 6 GB." && exit 53
fi fi
: ${DISK3_SIZE:=''}
EXTRA_SIZE=DISK3_SIZE
EXTRA_DISK="/storage3/data.img" EXTRA_DISK="/storage3/data.img"
if [ -f "${EXTRA_DISK}" ]; then if [ -d "$(dirname "${EXTRA_DISK}")" ]; then
if [ ! -f "${EXTRA_DISK}" ]; then
[ -z "$EXTRA_SIZE" ] && EXTRA_SIZE="16G"
if ! truncate -s "${EXTRA_SIZE}" "${EXTRA_DISK}"; then
error "Could not create the file for the third disk." && exit 54
fi
fi
if [ -n "$EXTRA_SIZE" ]; then
CUR_SIZE=$(stat -c%s "${EXTRA_DISK}")
DATA_SIZE=$(numfmt --from=iec "${EXTRA_SIZE}")
if [ "$DATA_SIZE" -gt "$CUR_SIZE" ]; then
truncate -s "${EXTRA_SIZE}" "${EXTRA_DISK}"
fi
fi
DISK_OPTS="${DISK_OPTS} \ DISK_OPTS="${DISK_OPTS} \
-device virtio-scsi-pci,id=hw-userdata3,bus=pcie.0,addr=0xe \ -device virtio-scsi-pci,id=hw-userdata3,bus=pcie.0,addr=0xe \
-drive file=${EXTRA_DISK},if=none,id=drive-userdata3,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -drive file=${EXTRA_DISK},if=none,id=drive-userdata3,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \
-device scsi-hd,bus=hw-userdata3.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata3,id=userdata3,rotation_rate=${DISK_ROTATION},bootindex=5" -device scsi-hd,bus=hw-userdata3.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata3,id=userdata3,rotation_rate=${DISK_ROTATION},bootindex=5"
else
[ -d "$(dirname "${EXTRA_DISK}")" ] && error "Disk image ${EXTRA_DISK} does not exist! Please supply an empty file of at least 6 GB." && exit 54
fi fi
: ${DEVICE:=''} # Docker variable to passthrough a block device, like /dev/vdc1. : ${DEVICE:=''} # Docker variable to passthrough a block device, like /dev/vdc1.
@@ -213,3 +237,5 @@ if [ -n "${DEVICE3}" ]; then
-device scsi-hd,bus=hw-userdata6.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata6,id=userdata6,rotation_rate=${DISK_ROTATION},bootindex=8" -device scsi-hd,bus=hw-userdata6.0,channel=0,scsi-id=0,lun=0,drive=drive-userdata6,id=userdata6,rotation_rate=${DISK_ROTATION},bootindex=8"
fi fi
return 0

28
src/entry.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -Eeuo pipefail
echo " Starting Virtual DSM for Docker v${VERSION}..."
echo " For support visit https://github.com/vdsm/virtual-dsm/"
cd /run
. reset.sh # Initialize system
. install.sh # Run installation
. disk.sh # Initialize disks
. network.sh # Initialize network
. gpu.sh # Initialize graphics
. serial.sh # Initialize serialport
. power.sh # Configure shutdown
. config.sh # Configure arguments
trap - ERR
set -m
(
[[ "${DEBUG}" == [Yy1]* ]] && info "$VERS" && set -x
qemu-system-x86_64 ${ARGS:+ $ARGS} & echo $! > "${QEMU_PID}"
{ set +x; } 2>/dev/null
)
set +m
tail --pid "$(cat "${QEMU_PID}")" --follow /dev/null & wait $!

View File

@@ -1,6 +1,10 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
if [[ "${GPU}" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
return 0
fi
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri [ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
if [ ! -c /dev/dri/card0 ]; then if [ ! -c /dev/dri/card0 ]; then
@@ -14,9 +18,6 @@ fi
chmod 666 /dev/dri/card0 chmod 666 /dev/dri/card0
chmod 666 /dev/dri/renderD128 chmod 666 /dev/dri/renderD128
DEF_OPTS="-nodefaults -boot strict=on -display egl-headless,rendernode=/dev/dri/renderD128"
DEF_OPTS="${DEF_OPTS} -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1"
if ! apt-mark showinstall | grep -q "xserver-xorg-video-intel"; then if ! apt-mark showinstall | grep -q "xserver-xorg-video-intel"; then
info "Installing Intel GPU drivers..." info "Installing Intel GPU drivers..."
@@ -40,3 +41,5 @@ if ! apt-mark showinstall | grep -q "qemu-system-modules-opengl"; then
apt-get -qq --no-install-recommends -y install qemu-system-modules-opengl > /dev/null apt-get -qq --no-install-recommends -y install qemu-system-modules-opengl > /dev/null
fi fi
return 0

View File

@@ -1,6 +1,23 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
STORAGE="/storage"
[ ! -d "$STORAGE" ] && error "Storage folder (${STORAGE}) not found!" && exit 13
if [ -f "$STORAGE"/dsm.ver ]; then
BASE=$(cat "${STORAGE}/dsm.ver")
else
# Fallback for old installs
BASE="DSM_VirtualDSM_42962"
fi
[ -n "$URL" ] && BASE=$(basename "$URL" .pat)
if [[ -f "$STORAGE/$BASE.boot.img" ]] && [[ -f "$STORAGE/$BASE.system.img" ]]; then
# Previous installation found
return 0
fi
# Display wait message # Display wait message
/run/server.sh 5000 install & /run/server.sh 5000 install &
@@ -31,15 +48,25 @@ rm -f "$STORAGE"/"$BASE".agent
rm -f "$STORAGE"/"$BASE".boot.img rm -f "$STORAGE"/"$BASE".boot.img
rm -f "$STORAGE"/"$BASE".system.img rm -f "$STORAGE"/"$BASE".system.img
TMP="/tmp/dsm" MIN_SPACE=6442450944
FS=$(stat -f -c %T "$STORAGE") FS=$(stat -f -c %T "$STORAGE")
[[ "$FS" == "ext"* ]] && TMP="$STORAGE/tmp"
if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \
"$FS" != "ntfs"* && "$FS" != "fuse"* && "$FS" != "msdos"* ]]; then
TMP="$STORAGE/tmp"
else
TMP="/tmp/dsm"
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
(( MIN_SPACE > SPACE )) && TMP="$STORAGE/tmp"
fi
rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp"
rm -rf "$TMP" && mkdir -p "$TMP" rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace # Check free diskspace
MIN_SPACE=5842450944
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1) SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1)
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation." && exit 95 (( MIN_SPACE > SPACE )) && error "Not enough free space for installation, need at least 6 GB." && exit 95
[[ "${DEBUG}" == [Yy1]* ]] && set -x [[ "${DEBUG}" == [Yy1]* ]] && set -x
@@ -242,9 +269,11 @@ rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE"/dsm.ver echo "$BASE" > "$STORAGE"/dsm.ver
# Check free diskspace if [[ "$TMP" != "$STORAGE/tmp" ]]; then
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) # Check free diskspace
(( MIN_SPACE > SPACE )) && error "Not enough free space in storage folder." && exit 94 SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
(( MIN_SPACE > SPACE )) && error "Not enough free space in storage folder, need at least 6 GB." && exit 94
fi
mv -f "$PAT" "$STORAGE"/"$BASE".pat mv -f "$PAT" "$STORAGE"/"$BASE".pat
mv -f "$BOOT" "$STORAGE"/"$BASE".boot.img mv -f "$BOOT" "$STORAGE"/"$BASE".boot.img

74
src/power.sh Normal file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Configure QEMU for graceful shutdown
QEMU_PORT=7100
QEMU_TIMEOUT=50
QEMU_PID=/run/qemu.pid
QEMU_COUNT=/run/qemu.count
rm -f "${QEMU_PID}"
rm -f "${QEMU_COUNT}"
_trap(){
func="$1" ; shift
for sig ; do
trap "$func $sig" "$sig"
done
}
_graceful_shutdown() {
set +e
[ ! -f "${QEMU_PID}" ] && exit 130
[ -f "${QEMU_COUNT}" ] && return
echo && info "Received $1 signal, shutting down..."
echo 0 > "${QEMU_COUNT}"
# Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
# Send shutdown command to guest agent via serial port
RESPONSE=$(curl -s -m 5 -S http://127.0.0.1:2210/read?command=6 2>&1)
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then
echo && error "Could not send shutdown command to the guest ($RESPONSE)"
kill -15 "$(cat "${QEMU_PID}")"
pkill -f qemu-system-x86_64 || true
fi
while [ "$(cat ${QEMU_COUNT})" -lt "${QEMU_TIMEOUT}" ]; do
# Increase the counter
echo $(($(cat ${QEMU_COUNT})+1)) > ${QEMU_COUNT}
# Try to connect to qemu
if echo 'info version'| nc -q 1 -w 1 localhost "${QEMU_PORT}" >/dev/null 2>&1 ; then
sleep 1
CNT="$(cat ${QEMU_COUNT})/${QEMU_TIMEOUT}"
[[ "${DEBUG}" == [Yy1]* ]] && info "Shutting down, waiting... (${CNT})"
fi
done
echo && echo " Quitting..."
echo 'quit' | nc -q 1 -w 1 localhost "${QEMU_PORT}" >/dev/null 2>&1 || true
closeNetwork
return
}
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
MON_OPTS="-monitor telnet:localhost:${QEMU_PORT},server,nowait,nodelay"

View File

@@ -4,12 +4,13 @@ set -Eeuo pipefail
info () { echo -e >&2 "\E[1;34m\E[1;36m $1\E[0m" ; } info () { echo -e >&2 "\E[1;34m\E[1;36m $1\E[0m" ; }
error () { echo -e >&2 "\E[1;31m ERROR: $1\E[0m" ; } error () { echo -e >&2 "\E[1;31m ERROR: $1\E[0m" ; }
retry=true file="/run/dsm.url"
while [ "$retry" = true ] while [ ! -f "$file" ]
do do
sleep 2 sleep 3
[ -f "$file" ] && continue
# Retrieve IP from guest VM # Retrieve IP from guest VM
@@ -46,14 +47,16 @@ do
[ -z "${IP}" ] && continue [ -z "${IP}" ] && continue
retry=false echo "${IP}:${PORT}" > $file
done done
if [[ "$IP" == "20.20"* ]]; then LOCATION=$(cat "$file")
MSG="port ${PORT}"
if [[ "$LOCATION" == "20.20"* ]]; then
MSG="port ${LOCATION##*:}"
else else
MSG="http://${IP}:${PORT}" MSG="http://${LOCATION}"
fi fi
echo "" >&2 echo "" >&2

35
src/reset.sh Normal file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -Eeuo pipefail
info () { echo -e "\E[1;34m \E[1;36m$1\E[0m" ; }
error () { echo -e >&2 "\E[1;31m ERROR: $1\E[0m" ; }
trap 'error "Status $? while: ${BASH_COMMAND} (line $LINENO/$BASH_LINENO)"' ERR
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
# Docker environment variables
: ${URL:=''} # URL of the PAT file
: ${GPU:='N'} # Enable GPU passthrough
: ${DEBUG:='N'} # Enable debugging mode
: ${ALLOCATE:='Y'} # Preallocate diskspace
: ${ARGUMENTS:=''} # Extra QEMU parameters
: ${CPU_CORES:='1'} # Amount of CPU cores
: ${DISK_SIZE:='16G'} # Initial data disk size
: ${RAM_SIZE:='512M'} # Maximum RAM amount
# Helper variables
KERNEL=$(uname -r | cut -b 1)
MINOR=$(uname -r | cut -d '.' -f2)
ARCH=$(dpkg --print-architecture)
VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1)
# Cleanup files
rm -f /run/dsm.url
rm -f /run/qemu.pid
rm -f /run/qemu.count
return 0

View File

@@ -5,13 +5,10 @@ set -Eeuo pipefail
: ${HOST_CPU:=''} : ${HOST_CPU:=''}
: ${HOST_MAC:=''} : ${HOST_MAC:=''}
: ${HOST_BUILD:=''}
: ${HOST_DEBUG:=''} : ${HOST_DEBUG:=''}
: ${HOST_SERIAL:=''} : ${HOST_SERIAL:=''}
: ${GUEST_SERIAL:=''}
: ${HOST_MODEL:=''} : ${HOST_MODEL:=''}
: ${HOST_VERSION:=''} : ${GUEST_SERIAL:=''}
: ${HOST_TIMESTAMP:=''}
if [ -z "$HOST_CPU" ]; then if [ -z "$HOST_CPU" ]; then
HOST_CPU=$(lscpu | grep 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g') HOST_CPU=$(lscpu | grep 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
@@ -32,28 +29,44 @@ HOST_ARGS+=("-cpu=${CPU_CORES}")
HOST_ARGS+=("-cpu_arch=${HOST_CPU}") HOST_ARGS+=("-cpu_arch=${HOST_CPU}")
[ -n "$HOST_MAC" ] && HOST_ARGS+=("-mac=${HOST_MAC}") [ -n "$HOST_MAC" ] && HOST_ARGS+=("-mac=${HOST_MAC}")
[ -n "$HOST_BUILD" ] && HOST_ARGS+=("-build=${HOST_BUILD}")
[ -n "$HOST_MODEL" ] && HOST_ARGS+=("-model=${HOST_MODEL}") [ -n "$HOST_MODEL" ] && HOST_ARGS+=("-model=${HOST_MODEL}")
[ -n "$HOST_SERIAL" ] && HOST_ARGS+=("-hostsn=${HOST_SERIAL}") [ -n "$HOST_SERIAL" ] && HOST_ARGS+=("-hostsn=${HOST_SERIAL}")
[ -n "$GUEST_SERIAL" ] && HOST_ARGS+=("-guestsn=${GUEST_SERIAL}") [ -n "$GUEST_SERIAL" ] && HOST_ARGS+=("-guestsn=${GUEST_SERIAL}")
[ -n "$HOST_VERSION" ] && HOST_ARGS+=("-version=${HOST_VERSION}")
[ -n "$HOST_TIMESTAMP" ] && HOST_ARGS+=("-ts=${HOST_TIMESTAMP}")
if [[ "${HOST_DEBUG}" == [Yy1]* ]]; then if [[ "${HOST_DEBUG}" == [Yy1]* ]]; then
set -x set -x
./run/host.bin "${HOST_ARGS[@]}" & ./host.bin "${HOST_ARGS[@]}" &
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
echo echo
else else
./run/host.bin "${HOST_ARGS[@]}" >/dev/null & ./host.bin "${HOST_ARGS[@]}" >/dev/null &
fi fi
cnt=0
sleep 0.2
while ! nc -z -w1 127.0.0.1 2210 > /dev/null 2>&1; do
sleep 0.1
cnt=$((cnt + 1))
(( cnt > 20 )) && error "Failed to connect to qemu-host.." && exit 58
done
cnt=0
while ! nc -z -w1 127.0.0.1 12345 > /dev/null 2>&1; do
sleep 0.1
cnt=$((cnt + 1))
(( cnt > 20 )) && error "Failed to connect to qemu-host.." && exit 59
done
# Configure serial ports # Configure serial ports
SERIAL_OPTS="\ SERIAL_OPTS="\
-serial mon:stdio \ -serial mon:stdio \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \ -device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev pty,id=charserial0 \ -chardev pty,id=charserial0 \
-device isa-serial,chardev=charserial0,id=serial0 \ -device isa-serial,chardev=charserial0,id=serial0 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \ -chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel" -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
return 0

View File

@@ -46,17 +46,15 @@ if [[ "$2" != "/run/ip.sh" ]]; then
else else
BODY="The location of DSM is <a href='http://\${IP}:\${PORT}'>http://\${IP}:\${PORT}</a><script>" BODY="The location of DSM is <a href='http://\${LOCATION}'>http://\${LOCATION}</a><script>"
BODY="${BODY}setTimeout(function(){ window.location.assign('http://\${IP}:\${PORT}'); }, 3000);</script>" BODY="${BODY}setTimeout(function(){ window.location.assign('http://\${LOCATION}'); }, 3000);</script>"
WAIT="Please wait while discovering IP...<script>setTimeout(() => { document.location.reload(); }, 4999);</script>" WAIT="Please wait while discovering IP...<script>setTimeout(() => { document.location.reload(); }, 4999);</script>"
HTML=$(html "xxx") HTML=$(html "xxx")
{ echo "#!/bin/bash" { echo "#!/bin/bash"
echo "INFO=\$(curl -s -m 2 -S http://127.0.0.1:2210/read?command=10 2>/dev/null)" echo "[ -f \"/run/dsm.url\" ] && LOCATION=\$(cat \"/run/dsm.url\")"
echo "rest=\${INFO#*http_port}; rest=\${rest#*:}; rest=\${rest%%,*}; PORT=\${rest%%\\\"*}" echo "HTML=\"$HTML\"; [ -z \"\${LOCATION}\" ] && BODY=\"$WAIT\" || BODY=\"$BODY\"; HTML=\${HTML/xxx/\$BODY}"
echo "rest=\${INFO#*eth0}; rest=\${rest#*ip}; rest=\${rest#*:}; rest=\${rest#*\\\"}; IP=\${rest%%\\\"*}"
echo "HTML=\"$HTML\"; [ -z \"\${IP}\" ] && BODY=\"$WAIT\" || BODY=\"$BODY\"; HTML=\${HTML/xxx/\$BODY}"
echo "printf '%b' \"HTTP/1.1 200 OK\\nContent-Length: \${#HTML}\\nConnection: close\\n\\n\$HTML\"" echo "printf '%b' \"HTTP/1.1 200 OK\\nContent-Length: \${#HTML}\\nConnection: close\\n\\n\$HTML\""
} > "$TMP_FILE" } > "$TMP_FILE"