Compare commits

...

19 Commits
v4.24 ... 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
Kroese
478b6af755 fix: Skip healthcheck during shutdown
fix: Skip healthcheck during shutdown
2023-11-09 16:17:30 +01:00
Kroese
fecd4052fc fix: Skip healtcheck during shutdow 2023-11-09 16:16:51 +01:00
Kroese
0b8306d827 fix: Skip healthcheck during shutdown
fix: Skip healthcheck during shutdown
2023-11-09 16:15:08 +01:00
Kroese
c9d0688424 fix: Skip healthcheck during shutdown 2023-11-09 15:55:35 +01:00
Kroese
413a089e02 build: Shellcheck
build: Shellcheck
2023-11-09 01:12:17 +01:00
Kroese
01e23f22fb build: Shellcheck 2023-11-09 01:12:01 +01:00
Kroese
308a764bb0 fix: Prepare agent for removal
fix: Prepare for removal
2023-11-09 01:09:55 +01:00
Kroese
5ad5f8a8ef fix: Prepare for removal 2023-11-09 01:08:46 +01:00
19 changed files with 391 additions and 298 deletions

View File

@@ -11,4 +11,4 @@ jobs:
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:
SHELLCHECK_OPTS: -x -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 -e SC2317 -e SC2028 -e SC2153
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 \
&& 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
RUN chmod +x /run/*.sh && chmod +x /run/*.bin
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
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/run.sh"]
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -u
VERSION="8"
VERSION="9"
HEADER="VirtualDSM Agent"
# Functions
@@ -45,8 +45,12 @@ function downloadUpdate {
[[ "$remote_size" == "" || "$remote_size" == "0" ]] && return
remote_size=$(($remote_size+0))
((remote_size<100)) && return
SCRIPT=$(readlink -f "${BASH_SOURCE[0]}")
local_size=$(stat -c%s "$SCRIPT")
local_size=$(($local_size+0))
[[ remote_size -eq local_size ]] && return
@@ -126,31 +130,6 @@ else
fi
delay=500
elapsed=$((($(date +%s%N) - ts)/1000000))
if [[ delay -gt elapsed ]]; then
difference=$((delay-elapsed))
float=$(echo | awk -v diff="${difference}" '{print diff * 0.001}')
sleep "$float"
fi
# Display message in docker log output
IP=$(ip address show dev eth0 | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
if [[ "$IP" == "20.20"* ]]; then
MSG="port 5000"
else
MSG="http://${IP}:5000"
fi
echo ""
info "--------------------------------------------------------"
info " You can now login to DSM at ${MSG}"
info "--------------------------------------------------------"
echo ""
# Wait for NMI interrupt as a shutdown signal
while true; do

View File

@@ -17,7 +17,6 @@ Virtual DSM in a docker container.
- Multi-platform
- KVM acceleration
- GPU passthrough
- Graceful shutdowns
- Upgrades supported
## 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.
* ### 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?

View File

@@ -1,49 +0,0 @@
#!/usr/bin/env bash
set -u
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
# 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"
fi
MIN_SIZE=6442450944
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}")
if (( DATA_SIZE < 6442450944 )); then
if (( DATA_SIZE < MIN_SIZE )); then
error "Please increase DISK_SIZE to at least 6 GB." && exit 83
fi
@@ -124,9 +125,6 @@ if [ ! -f "${DATA}" ]; then
error "Virtual disk does not exist ($DATA)" && exit 88
fi
# Format as BTRFS filesystem
# mkfs.btrfs -q -L data -d single -m dup "${DATA}" > /dev/null
fi
# 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 \
-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"
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} \
-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 \
-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
: ${DISK3_SIZE:=''}
EXTRA_SIZE=DISK3_SIZE
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} \
-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 \
-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
: ${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"
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
set -Eeuo pipefail
if [[ "${GPU}" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
return 0
fi
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
if [ ! -c /dev/dri/card0 ]; then
@@ -14,9 +18,6 @@ fi
chmod 666 /dev/dri/card0
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
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
fi
return 0

View File

@@ -1,6 +1,23 @@
#!/usr/bin/env bash
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
/run/server.sh 5000 install &
@@ -31,15 +48,25 @@ rm -f "$STORAGE"/"$BASE".agent
rm -f "$STORAGE"/"$BASE".boot.img
rm -f "$STORAGE"/"$BASE".system.img
TMP="/tmp/dsm"
MIN_SPACE=6442450944
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"
# Check free diskspace
MIN_SPACE=5842450944
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
@@ -242,9 +269,11 @@ rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE"/dsm.ver
# Check free diskspace
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
(( MIN_SPACE > SPACE )) && error "Not enough free space in storage folder." && exit 94
if [[ "$TMP" != "$STORAGE/tmp" ]]; then
# Check free diskspace
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 "$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" ; }
error () { echo -e >&2 "\E[1;31m ERROR: $1\E[0m" ; }
retry=true
file="/run/dsm.url"
while [ "$retry" = true ]
while [ ! -f "$file" ]
do
sleep 2
sleep 3
[ -f "$file" ] && continue
# Retrieve IP from guest VM
@@ -46,14 +47,16 @@ do
[ -z "${IP}" ] && continue
retry=false
echo "${IP}:${PORT}" > $file
done
if [[ "$IP" == "20.20"* ]]; then
MSG="port ${PORT}"
LOCATION=$(cat "$file")
if [[ "$LOCATION" == "20.20"* ]]; then
MSG="port ${LOCATION##*:}"
else
MSG="http://${IP}:${PORT}"
MSG="http://${LOCATION}"
fi
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_MAC:=''}
: ${HOST_BUILD:=''}
: ${HOST_DEBUG:=''}
: ${HOST_SERIAL:=''}
: ${GUEST_SERIAL:=''}
: ${HOST_MODEL:=''}
: ${HOST_VERSION:=''}
: ${HOST_TIMESTAMP:=''}
: ${GUEST_SERIAL:=''}
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')
@@ -32,28 +29,44 @@ HOST_ARGS+=("-cpu=${CPU_CORES}")
HOST_ARGS+=("-cpu_arch=${HOST_CPU}")
[ -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_SERIAL" ] && HOST_ARGS+=("-hostsn=${HOST_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
set -x
./run/host.bin "${HOST_ARGS[@]}" &
./host.bin "${HOST_ARGS[@]}" &
{ set +x; } 2>/dev/null
echo
else
./run/host.bin "${HOST_ARGS[@]}" >/dev/null &
./host.bin "${HOST_ARGS[@]}" >/dev/null &
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
SERIAL_OPTS="\
-serial mon:stdio \
-serial mon:stdio \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev pty,id=charserial0 \
-device isa-serial,chardev=charserial0,id=serial0 \
-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"
return 0

View File

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