Skip to content

Instantly share code, notes, and snippets.

@mark-kubacki
Last active September 23, 2021 10:34
Show Gist options
  • Select an option

  • Save mark-kubacki/18d34b52a61906cc24f94ec39c8d34ff to your computer and use it in GitHub Desktop.

Select an option

Save mark-kubacki/18d34b52a61906cc24f94ec39c8d34ff to your computer and use it in GitHub Desktop.

Revisions

  1. mark-kubacki revised this gist Sep 23, 2021. 1 changed file with 11 additions and 4 deletions.
    15 changes: 11 additions & 4 deletions kvm-bootstrap.sh
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,15 @@
    #!/bin/bash
    # This file encapsulates the rather elaborate networking setup withing Docker.
    # This file encapsulates the rather elaborate networking setup within Docker.
    # Furthermore, absent any Windows VM file, starts the Windows installation from a provided ISO file.

    set -eo pipefail
    if [[ ! -z ${debug+x} ]]; then
    set -x
    fi

    # Again, this is run within the Docker container.
    # Usually runtimes will replace /dev with a standardized set of devices.
    # That’s why we need to extend them:
    if [[ ! -c "/dev/kvm" ]]; then
    rm /dev/kvm || true
    set +e
    @@ -34,14 +37,17 @@ if [[ ! -z "${BRIDGE_IF+x}" ]]; then
    set -e
    fi
    fi
    # End of network plumbing.

    # shortcuts such as:
    # Start of setting up, or setting up and starting the actual VM.
    # This script accepts some arguments (the first one must be "windows"; else this runs any supplied KVM),
    # which are referred to by shortcuts such as:
    # windows <spice address> <spice port> <spice password> <MAC> <nic-device> <name> <memory in MB> <boot ISO>
    # example:
    # windows ${COREOS_PUBLIC_IPV4} 5900 geheim 52:54:00:xx:xx:xx macvtap0 "windows-1" $((16 * 8 * 1024)) Windows-10-threshold-2-take-1.iso
    # windows ${PUBLIC_IPV4} 5900 geheim 52:54:00:xx:xx:xx macvtap0 "windows-1" $((16 * 8 * 1024)) Windows-10-threshold-2-take-1.iso

    if (( $# >= 8 )) && [[ "$1" == "windows" ]]; then

    # No prior Windows installation? Create a fresh image:
    if [[ ! -e /var/vm/disks/"$7".img ]]; then
    mkdir -p /var/vm/disks
    chmod 0700 /var/vm/disks
    @@ -74,6 +80,7 @@ if (( $# >= 8 )) && [[ "$1" == "windows" ]]; then
    >&2 printf "Probably okay if this is no Windows, else download them from here:\n"
    >&2 printf " https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso\n"
    fi
    # 9th argument is expected to be an ISO file to install a fresh Windows from.
    if (( $# >= 9 )); then
    if [[ -s "/var/cache/media/$9" ]]; then
    drives+=("-drive" "file=/var/cache/media/$9,index=2,media=cdrom,readonly")
  2. mark-kubacki revised this gist Sep 23, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion windows-macvtap.service
    Original file line number Diff line number Diff line change
    @@ -27,7 +27,7 @@ ExecStart=/bin/docker run -t --rm --privileged \
    -v /var/vm:/var/vm \
    -v /run/kvm:/run/kvm \
    wmark/docker-kvm \
    windows ${COREOS_PUBLIC_IPV4} 5900 "${VM_PASSWORD}" ${VM_MAC} ${VM_TAP} "${VM_NAME}" 8192 Windows10.iso
    windows ${PUBLIC_IPV4} 5900 "${VM_PASSWORD}" ${VM_MAC} ${VM_TAP} "${VM_NAME}" 8192 Windows10.iso

    ExecReload=/bin/sh -c "echo system_reset | nc -U /run/kvm/${VM_NAME}.monitor"

  3. mark-kubacki revised this gist Sep 23, 2021. 3 changed files with 7 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions Dockerfile
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,6 @@
    FROM blitznote/debootstrap-amd64:16.10
    MAINTAINER W. Mark Kubacki <[email protected]>
    # Image meant to host Windows in QEMU, hence we install QEMU
    # and everything that is needed to get the networking for it working.
    FROM ubuntu:latest

    RUN apt-get -q update \
    && apt-get -y install \
    2 changes: 2 additions & 0 deletions kvm-bootstrap.sh
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,6 @@
    #!/bin/bash
    # This file encapsulates the rather elaborate networking setup withing Docker.
    # Furthermore, absent any Windows VM file, starts the Windows installation from a provided ISO file.

    set -eo pipefail
    if [[ ! -z ${debug+x} ]]; then
    2 changes: 2 additions & 0 deletions windows-macvtap.service
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    # ExecStart is the important part. It shows how to run the Docker container this all is for.

    [Unit]
    Description=KVM with Windows using macvtap
    Wants=network-online.target docker.service
  4. mark-kubacki created this gist Sep 23, 2021.
    18 changes: 18 additions & 0 deletions Dockerfile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    FROM blitznote/debootstrap-amd64:16.10
    MAINTAINER W. Mark Kubacki <[email protected]>

    RUN apt-get -q update \
    && apt-get -y install \
    kvm qemu-kvm bridge-utils psmisc \
    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

    RUN update-alternatives --install /usr/bin/qemu qemu /usr/bin/qemu-system-x86_64-spice 10

    EXPOSE 3389 5900
    # 3389 for Windows RDP
    # 5900 for QEMU's SPICE
    VOLUME /var/cache/media /var/vm

    COPY kvm-bootstrap.sh /sbin/kvm-bootstrap.sh

    ENTRYPOINT ["/sbin/kvm-bootstrap.sh"]
    99 changes: 99 additions & 0 deletions kvm-bootstrap.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,99 @@
    #!/bin/bash

    set -eo pipefail
    if [[ ! -z ${debug+x} ]]; then
    set -x
    fi

    if [[ ! -c "/dev/kvm" ]]; then
    rm /dev/kvm || true
    set +e
    read -r NODNUM _ < <(grep '\<kvm\>' /proc/misc)
    mknod /dev/kvm c 10 "${NODNUM}"
    set -e
    else
    if ! dd if=/dev/kvm count=0 2>/dev/null; then
    >&2 printf "Cannot open /dev/kvm - please run this in a privileged context.\n"
    # see: /usr/include/sysexits.h: EX_OSFILE
    exit 72
    fi
    fi

    if [[ ! -z "${BRIDGE_IF+x}" ]]; then
    printf "allow ${BRIDGE_IF}" >/etc/qemu/bridge.conf

    # Make sure we have the tun device node
    if [[ ! -c "/dev/net/tun" ]]; then
    rm /dev/net/tun || true
    mkdir -p /dev/net
    set +e
    read -r NODNUM _ < <(grep '\<tun\>' /proc/misc)
    mknod /dev/net/tun c 10 "${NODNUM}"
    set -e
    fi
    fi

    # shortcuts such as:
    # windows <spice address> <spice port> <spice password> <MAC> <nic-device> <name> <memory in MB> <boot ISO>
    # example:
    # windows ${COREOS_PUBLIC_IPV4} 5900 geheim 52:54:00:xx:xx:xx macvtap0 "windows-1" $((16 * 8 * 1024)) Windows-10-threshold-2-take-1.iso

    if (( $# >= 8 )) && [[ "$1" == "windows" ]]; then

    if [[ ! -e /var/vm/disks/"$7".img ]]; then
    mkdir -p /var/vm/disks
    chmod 0700 /var/vm/disks
    qemu-img create -f qcow2 /var/vm/disks/"$7".img 80G
    fi

    read MAJOR MINOR < <(cat /sys/devices/virtual/net/$6/tap*/dev | tr ':' ' ')
    mknod /dev/tap-vm c ${MAJOR} ${MINOR}

    # -device virtio-balloon,id=balloon0,bus=pci.0,addr=0x7 \ Windows 10 won't start with this

    if [[ -z ${ncores+x} ]]; then
    : ${ncores:="4"}
    if (( $(nproc) > 16 )); then
    # MCC or HCC cpu(s)
    if (( $(nproc) > 20)); then
    let ncores="$[ $(nproc --ignore 4)/2 ]"
    else
    let ncores="$(nproc --ignore 2)"
    fi
    fi
    fi

    drives=()
    drives+=("-drive" "file=/var/vm/disks/$7.img,if=virtio,index=0,media=disk")
    if [[ -s /var/cache/media/virtio-win.iso ]]; then
    drives+=("-drive" "file=/var/cache/media/virtio-win.iso,index=3,media=cdrom,readonly")
    else
    >&2 printf "Virtio drivers not found in: %s\n" "/var/cache/media/virtio-win.iso"
    >&2 printf "Probably okay if this is no Windows, else download them from here:\n"
    >&2 printf " https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso\n"
    fi
    if (( $# >= 9 )); then
    if [[ -s "/var/cache/media/$9" ]]; then
    drives+=("-drive" "file=/var/cache/media/$9,index=2,media=cdrom,readonly")
    drives+=("-boot" "once=d")
    else
    >&2 printf "Ignored, because it is no file: %s\n" "$9"
    fi
    fi
    spice=()
    spice+=("-vga" "qxl")
    spice+=("-spice" "addr=$2,port=$3,password=$4")
    spice+=("-chardev" "spicevmc,id=vdagent,name=vdagent")
    spice+=("-device" "virtserialport,chardev=vdagent,name=com.redhat.spice.0")

    exec /usr/bin/qemu -enable-kvm -nographic -rtc base=utc \
    -monitor unix:/run/kvm/"$7".monitor,server,nowait \
    -cpu host -m $8 -smp ${ncores},sockets=1 -k de -usbdevice tablet \
    -device virtio-serial \
    ${spice[*]} \
    -net nic,model=virtio,macaddr=$5 -net tap,fd=3 3<>/dev/tap-vm \
    ${drives[*]} \
    -name "$7"
    else
    exec /usr/bin/qemu -enable-kvm "$@"
    fi
    36 changes: 36 additions & 0 deletions windows-macvtap.service
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    [Unit]
    Description=KVM with Windows using macvtap
    Wants=network-online.target docker.service
    Requires=docker.service
    After=network-online.target

    [Service]
    Restart=on-abort
    RestartSec=30s
    SuccessExitStatus=2

    EnvironmentFile=/etc/environment
    Environment=PATH=/opt/sbin:/opt/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    Environment=VM_NAME=windows-1
    Environment=VM_MAC=52:54:00:12:34:56
    Environment=VM_TAP=macvtap0
    Environment=VM_PASSWORD=geheim

    ExecStartPre=/bin/sh -c "[ -d /run/kvm ] || mkdir /run/kvm && chmod 0700 /run/kvm"
    ExecStartPre=/bin/sh -c "/bin/ip link add link ext0 name ${VM_TAP} type macvtap && /bin/ip link set ${VM_TAP} address ${VM_MAC} up"
    ExecStart=/bin/docker run -t --rm --privileged \
    --net host \
    --cgroup-parent machine.slice \
    -v /var/cache/media:/var/cache/media \
    -v /var/vm:/var/vm \
    -v /run/kvm:/run/kvm \
    wmark/docker-kvm \
    windows ${COREOS_PUBLIC_IPV4} 5900 "${VM_PASSWORD}" ${VM_MAC} ${VM_TAP} "${VM_NAME}" 8192 Windows10.iso

    ExecReload=/bin/sh -c "echo system_reset | nc -U /run/kvm/${VM_NAME}.monitor"

    ExecStop=/bin/sh -c "echo system_powerdown | nc -U /run/kvm/${VM_NAME}.monitor || rm /run/kvm/${VM_NAME}.monitor"
    ExecStopPost=/bin/sh -c "/bin/ip link set dev ${VM_TAP} down && /bin/ip link del ${VM_TAP}"

    [Install]
    WantedBy=multi-user.target