Hello, My goal was to build a set of live CD with french keyboard/language support
I found it would be nice to reuse pettersson setup, found here : https://alioth.debian.org/anonscm/git/debian-cd/pettersson-live.git I saw in the README file that no script was provided to build the VM that builds the CD, thought sufficient information were given, to make my own, I contribute here, what I did. Find attached the script I wrote to build the vm, and a patch against the git repository, to make it work and build a (partly) localized set of live CD It make use of virtinst to build the VM * Quick start to use it on your own server: - As root : apt-get install virtinst libvirt-clients libvirtd-bin adduser debian-cd adduser debian-cd kvm adduser debian-cd libvirt mkdir -p /mnt/nfs-image/.live/new/log chown -R debian-cd.debian-cd /mnt/nfs-image/.live - As debian-cd user : git clone https://alioth.debian.org/anonscm/git/debian-cd/pettersson-live.git live copy attached kvm-build in live/bin directory apply build_vm_fr.patch cd in live directory and run bin/kvm-build You can watch console running the "build vm" build using virt-manager and login as debian-cd If everything goes well you'll get a default stretch vm to build live cd images you can build a minimal set running bin/small-build Feel free to reuse as GPLV2+ (as far as I was said on IRC, it's the current license for this repo) Comments / Questions welcome :-) Cheers Christian
diff --git a/.gitignore b/.gitignore index 3f9177e..0240e12 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,9 @@ input +authorized_keys +key/*_live_from_vm +key/*_live_from_vm.pub +key/*_live_to_vm +key/*_live_to_vm.pub +localconfig +preseed.cfg + diff --git a/available/CONF.sh b/available/CONF.sh index b066157..2b7dd39 100644 --- a/available/CONF.sh +++ b/available/CONF.sh @@ -3,3 +3,4 @@ VERSION="8.2.1" CODENAME="jessie" +LANG="french" diff --git a/available/auto-config b/available/auto-config index 01bc035..37126e1 100755 --- a/available/auto-config +++ b/available/auto-config @@ -2,8 +2,15 @@ #set -x -M="http://cdbuilder.debian.org/debian" +#M="http://cdbuilder.debian.org/debian" +M="http://ftp.fr.debian.org/debian" S="http://security.debian.org/debian-security" +LAYOUT="fr_FR.UTF-8" +KBD="fr" +# See http://wiki.csnu.org/index.php/Debian_live +#L10N="--bootappend-live \"locales=fr_FR.UTF-8 keyboard-layouts=fr\" --bootappend-install \"locales=fr_FR.UTF-8\"" +#--bootappend-live "boot=live components locales=en_US.UTF-8 keyboard-layouts=fr hostname=rescue \ +# username=user noprompt noeject autologin" case "$DEBIAN_BUILD_ARCH" in amd64) @@ -87,7 +94,10 @@ else --mirror-chroot $M \ --mirror-chroot-security $S \ --mirror-debian-installer $M \ + --bootappend-live "boot=live components locales=${LAYOUT} keyboard-layouts=${KBD}" \ + --bootappend-install "locales=${LAYOUT}" \ -a $DEBIAN_BUILD_ARCH \ + -d $CODENAME \ "${@}" fi diff --git a/available/run-30live-build b/available/run-30live-build index 6cf0f1a..1edfaad 100755 --- a/available/run-30live-build +++ b/available/run-30live-build @@ -127,6 +127,11 @@ for TYPE in $TYPES; do # And make sure we're using the right graphics for Jessie... find . -name 0001-plymouth-theme.hook.chroot | xargs sed -i 's/joy/lines/g' + # Language + if [ ! -z "$LANG" ] + then + find . -name localization.list.chroot | xargs sed -i "s/^task-english/task-${LANG} task-english/g" + fi if [ -f ${LOCALCONF}/splash.png ] ; then for DIR in $(find . -type f -name splash.svg | grep isolinux/ | xargs -n1 dirname); do rm -vf $DIR/splash.svg diff --git a/available/run-50halt b/available/run-50halt index b15dbc5..7b7f82e 100755 --- a/available/run-50halt +++ b/available/run-50halt @@ -12,7 +12,8 @@ sleep 10 # Delete myself so we can't get stuck in a reboot loop! rm -f $0 -halt +#halt +poweroff log "DONE" exit 0 diff --git a/bin/kvm-login b/bin/kvm-login new file mode 100755 index 0000000..caca98b --- /dev/null +++ b/bin/kvm-login @@ -0,0 +1,16 @@ +#!/bin/sh +DIST=${DIST:-"stretch"} +VMNAME="${DIST}-debian-live-maker" +CURSTATE=x`virsh -c qemu:///system domstate ${VMNAME} 2>/dev/null|sed 's/ //'` +SSHPORT=${SSHPORT:-10022} +RSYNCPORT=${RSYNCPORT:-10873} +if [ "${CURSTATE}" != "running" ] +then + virsh -c qemu:///system start ${VMNAME} + virsh -c qemu:///system qemu-monitor-command --hmp ${VMNAME} "hostfwd_add ::${SSHPORT}-:22" + virsh -c qemu:///system qemu-monitor-command --hmp ${VMNAME} "hostfwd_add ::${RSYNCPORT}-:873" +else + virsh -c qemu:///system qemu-monitor-command --hmp ${VMNAME} "hostfwd_add ::${SSHPORT}-:22" + virsh -c qemu:///system qemu-monitor-command --hmp ${VMNAME} "hostfwd_add ::${RSYNCPORT}-:873" +fi +ssh -p${SSHPORT} -o 'StrictHostKeyChecking no' root@localhost diff --git a/bin/kvm-set-storage-pool b/bin/kvm-set-storage-pool new file mode 100644 index 0000000..f1469a1 --- /dev/null +++ b/bin/kvm-set-storage-pool @@ -0,0 +1,3 @@ +#! /bin/sh +virsh pool-define-as --name tmppool --type dir --target /home/debian-cd/images/ +virsh pool-start tmppool diff --git a/bin/kvm-start b/bin/kvm-start index 7480aaf..23f490b 100755 --- a/bin/kvm-start +++ b/bin/kvm-start @@ -4,25 +4,43 @@ # lots of network access in parallel ulimit -n 4096 -DIR=/org/cdbuilder.debian.org/src/deb-cd/debian-live +LOCALCONFIG=${LOCALCONFIG:-"${HOME}/live/localconfig"} +[ -f "${LOCALCONFIG}" ] && . ${LOCALCONFIG} || true +IMAGE_DIR=${IMAGE_DIR:-"/org/cdbuilder.debian.org/src/deb-cd/debian-live"} -CORE_IMAGE=stretch-debian-live-maker.img -WORKSPACE_IMAGE=workspace.img +CPU=${CPU:-"8"} +FORMAT=${FORMAT:-"raw"} +MEM=${MEM:-"16G"} +DIST=${DIST:-"stretch"} + +CORE_IMAGE=${DIST}-debian-live-maker.img +WORKSPACE_IMAGE=${DIST}-workspace.img if [ "$1"x != ""x ] ; then CORE_IMAGE=$1 fi -cd $DIR +startvirsh(){ + #virsh -c qemu:///system destroy debian-live-maker + virsh -c qemu:///system setvcpus ${DIST}-debian-live-maker --config --maximum ${CPU} 2>/dev/null + virsh -c qemu:///system setvcpus ${DIST}-debian-live-maker --config --count ${CPU} 2>/dev/null + virsh -c qemu:///system setmaxmem ${DIST}-debian-live-maker ${MEM} --config 2>/dev/null + virsh -c qemu:///system setmem ${DIST}-debian-live-maker ${MEM} --config 2>/dev/null + virsh -c qemu:///system start ${DIST}-debian-live-maker 2>/dev/null + virsh -c qemu:///system qemu-monitor-command --hmp ${DIST}-debian-live-maker 'hostfwd_add ::10022-:22' 2>/dev/null + virsh -c qemu:///system qemu-monitor-command --hmp ${DIST}-debian-live-maker 'hostfwd_add ::10873-:873' 2>/dev/null +} +startkvm(){ +cd $IMAGE_DIR /usr/bin/kvm \ - -name debian-live-maker \ - -m 16G \ - -smp 8 \ + -name ${DIST}-debian-live-maker \ + -m ${MEM} \ + -smp ${CPU} \ -vnc :1 \ -pidfile /home/debian-cd/debian-live-vm.pid \ -boot order=c \ - -drive file=$CORE_IMAGE,format=raw,if=virtio \ + -drive file=$CORE_IMAGE,format=${FORMAT},if=virtio \ -daemonize \ -netdev user,id=usernet,hostfwd=tcp::10022-10.0.2.15:22,hostfwd=tcp::10873-10.0.2.15:873 \ -device virtio-net-pci,netdev=usernet \ @@ -31,3 +49,8 @@ cd $DIR # -nographic \ # -curses \ # -cdrom /mnt/nfs-cdimage/release/6.0.5/amd64/iso-cd/debian-6.0.5-amd64-netinst.iso \ + +} + +#startkvm +startvirsh diff --git a/bin/small-build b/bin/small-build new file mode 100755 index 0000000..6d189f9 --- /dev/null +++ b/bin/small-build @@ -0,0 +1,3 @@ +#! /bin/sh +BUILDS="cinnamon-desktop gnome-desktop kde-desktop lxde-desktop mate-desktop standard xfce-desktop" +BUILDS="mate-desktop standard" ARCHES="amd64" TYPES="non-free" bin/run-live diff --git a/inside_vm/init b/inside_vm/init index ad230c4..4e6f8dc 100755 --- a/inside_vm/init +++ b/inside_vm/init @@ -15,7 +15,7 @@ log () { rm -f /w/in/* -RUNLOG=/w/out/log/petterson-live-init.log +RUNLOG=/w/out/log/pettersson-live-init.log > $RUNLOG # Need to make sure we have network, can't start until then @@ -46,7 +46,7 @@ while true; do if [ -x $file ] ; then if (! grep -q $file $RUNLOG) ; then # Do something - (date; echo "Run $file") >> /w/out/log/petterson-live-init.log + (date; echo "Run $file") >> /w/out/log/pettersson-live-init.log $file fi rm -f $file diff --git a/key/README b/key/README new file mode 100644 index 0000000..c023cc1 --- /dev/null +++ b/key/README @@ -0,0 +1,6 @@ +# Here are stored key needed by the generated server to communicate +# <servername> is `hostname` +# <servername>_live_from_vm +# <servername>_live_from_vm.pub +# <servername>_live_to_vm +# <servername>_live_to_vm.pub diff --git a/localconfig.sample b/localconfig.sample new file mode 100644 index 0000000..e802eb9 --- /dev/null +++ b/localconfig.sample @@ -0,0 +1,7 @@ +#! /bin/sh +LAYOUT="fr_FR.UTF-8" +KBD="fr" +IMAGE_DIR="$HOME/images" +CPU="8" +FORMAT="qcow2" +MEM="4G"
#! /bin/sh # Raise the maximum number of open files we're allowed, so we can do # lots of network access in parallel ulimit -n 4096 # Look for custom local config if exists named localconfig in current directory by default LOCALCONFIG=${LOCALCONFIG:-"${HOME}/live/localconfig"} [ -f "${LOCALCONFIG}" ] && . ${LOCALCONFIG} || true ##### Default if undefined values ##### # Server params ARCH=${ARCH:-"amd64"} LAYOUT=${LAYOUT:-"en_US.UTF-8"} KBD=${KBD:-"en"} DIST=${DIST:-"stretch"} TZ=${TZ:-"Europe/Paris"} IMAGE_DIR=${IMAGE_DIR:-"/org/cdbuilder.debian.org/src/deb-cd/debian-live"} ROOTPWD=${ROOTPWD:-'*'} # Mirrors DEBMIRROR=${DEBMIRROR:-"ftp.fr.debian.org"} DEBSECMIRROR=${DEBSECMIRROR:-"security.debian.org"} DI_DEBMIRROR=${DI_DEBMIRROR:-"ftp.fr.debian.org"} ######################################## VMNAME="${DIST}-debian-live-maker" VMDOMAIN="local" if [ ! -d "$IMAGE_DIR" -o ! -w $IMAGE_DIR ] then echo "Can't write in $IMAGE_DIR, exiting" exit 1 fi CORE_IMAGE=${DIST}-debian-live-maker.img CORE_SIZE=${CORE_SIZE:-32} WORKSPACE_IMAGE=${DIST}-workspace.img WORKSPACE_SIZE=${WORKSPACE_SIZE:-300} if [ "$1"x != ""x ] ; then CORE_IMAGE=$1 fi # If we want to use static ip in the vm (not used here) # the following values are the default provided by nat I_IPADDRESS="10.0.2.15" I_NETMASK="255.255.255.0" I_GATEWAY="10.0.2.2" I_NS1="10.0.2.3" I_NS2="" PRESEED="preseed.cfg" # Authorised keys installed in the vm, if none are defined # I try t use ${HOME}/.ssh/id_rsa.pub AUTHORIZED_KEYS=authorized_keys if [ ! -f ${AUTHORIZED_KEYS} ] then if [ -f ${HOME}/.ssh/id_rsa.pub ] then cp ${HOME}/.ssh/id_rsa.pub ${AUTHORIZED_KEYS} else echo "I need a public key to generate the VM or you will not be able to log in" exit 1 fi fi # Default values for the VM # No need to have a very powerfull vm at building the VM RAM=${RAM:-2048} CPU=${CPU:-2} FORMAT=${FORMAT:-raw} OSVARIANT=${OSVARIANT:-debianwheezy} # Default user in the VM VMUSER="debian-cd" # Default virtual manager, it should be possible to have it on an other server # VIRSHOST=qemu+ssh://$USER@$SERVER/system VIRSHOST=qemu:///system # Some packages to install in the VM PKGLIST="openssh-server rsync live-build live-images htop git" # This script will create a preseed file to build the VM # It will create a vm with an extra volume mounted on /w # The wm will include authorized keys, so we can log in. # No password will be set by default # A fully automated install will be run # and the VM will shutdown at the end createpreseed(){ echo "Creating ${PRESEED} file" PRESEED=$1 cat > ${PRESEED} <<-FIN d-i debian-installer/locale string ${LAYOUT} d-i keyboard-configuration/xkb-keymap select ${KBD} d-i netcfg/choose_interface select auto d-i netcfg/dhcpv6_timeout string 1 #d-i netcfg/disable_autoconfig boolean true #d-i netcfg/dhcp_options select Configure network manually #d-i netcfg/get_ipaddress string ${I_IPADDRESS} #d-i netcfg/get_netmask string ${I_NETMASK} #d-i netcfg/get_gateway string ${I_GATEWAY} #d-i netcfg/get_nameservers string ${I_NS1} ${I_NS2} #d-i netcfg/confirm_static boolean true d-i netcfg/get_hostname string ${VMNAME} d-i netcfg/get_domain string ${VMDOMAIN} d-i netcfg/hostname string ${VMNAME}.${VMDOMAIN} d-i mirror/country string manual d-i mirror/http/hostname string ${DEBMIRROR} d-i mirror/http/directory string /debian d-i mirror/http/proxy string d-i mirror/suite string ${DIST} d-i mirror/udeb/suite string ${DIST} d-i passwd/root-login boolean false # Root password, either in clear text #d-i passwd/root-password password r00tme #d-i passwd/root-password-again password r00tme # or encrypted using an MD5 hash. d-i passwd/root-password-crypted password ${ROOTPWD} # To create a normal user account. d-i passwd/user-fullname string Debian CD User d-i passwd/username string ${VMUSER} # Normal user's password, either in clear text #d-i passwd/user-password password insecure #d-i passwd/user-password-again password insecure # or encrypted using an MD5 hash. d-i passwd/user-password-crypted password ${ROOTPWD} d-i clock-setup/utc boolean true d-i time/zone string ${TZ} d-i clock-setup/ntp boolean false # # This makes partman automatically partition without confirmation d-i partman-auto/init_automatically_partition select biggest_free d-i partman-auto/disk string /dev/vda /dev/vdb #TRY LVM#d-i partman-auto/method string regular d-i partman-auto/method string lvm # If not, you can put an entire recipe into the preconfiguration file in one # (logical) line. This example creates a small /boot partition, suitable # swap, and uses the rest of the space for the root partition: d-i partman-auto/expert_recipe string \ boot-root :: \ 500 10000 1000000000 ext4 \ \$primary{ } \ \$bootable{ } \ \$defaultignore{ } \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ . \ 1024 100 4096 linux-swap \ \$lvmok{ } \ lv_name{ swap } \ method{ swap } format{ } \ . \ 500 10000 1000000000 ext4 \ \$lvmok{ } \ lv_name{ w } \ method{ format } format{ } \ device{ /dev/vdb } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /w } \ . \ d-i partman-lvm/device_remove_lvm boolean true d-i partman-md/device_remove_md boolean true d-i partman-lvm/confirm boolean true d-i partman-lvm/confirm_nooverwrite boolean true #d-i partman-auto/choose_recipe select atomic d-i partman-partitioning/confirm_write_new_label boolean true d-i partman/choose_partition select finish d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true d-i partman-md/confirm boolean true d-i partman-partitioning/confirm_write_new_label boolean true d-i partman/choose_partition select finish d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true # d-i apt-setup/use_mirror boolean false d-i apt-setup/services-select multiselect security, updates d-i apt-setup/security_host string ${DEBSECMIRROR} tasksel tasksel/first multiselect standard d-i pkgsel/include string ${PKGLIST} d-i pkgsel/upgrade select none popularity-contest popularity-contest/participate boolean false d-i grub-installer/only_debian boolean true d-i grub-installer/with_other_os boolean true d-i grub-installer/bootdev string default # Comment next one for debug when needed ;-) d-i finish-install/reboot_in_progress note # This will power off the machine instead of just halting it. #d-i debian-installer/exit/poweroff boolean true d-i preseed/late_command string mkdir /target/home/${VMUSER}/.ssh ; \\ FIN cat ${AUTHORIZED_KEYS} | while read line do echo "echo \"$line\" >> /target/home/${VMUSER}/.ssh/authorized_keys ; \\" >> ${PRESEED} done cat >> ${PRESEED} <<-FIN in-target chown -R ${VMUSER}.${VMUSER} /home/${VMUSER}/.ssh/authorized_keys ; \\ mkdir /target/root/.ssh ; \\ cat /target/home/${VMUSER}/.ssh/authorized_keys > /target/root/.ssh/authorized_keys ; \\ echo "${VMUSER} ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/debian_user ; \\ in-target mkdir /w/in /w/out /w/out/log ; \\ in-target chown -R ${VMUSER}.${VMUSER} /w ; \\ echo "Done" FIN } # This will restart the vm and connect using the authorized keys # By default we consider the vm run with NAT # Port 22 is forwared on 10022 # Port 873 (rsync) is forwarded on 10873 # A rsync daemon will be set and will listen if there is something to build # Keys are set so the vm can communicate with the local hostname # inside_vm/init will be run at server startup finalizevm(){ echo "Finalize ${VMNAME} vm" #ssh-keygen -f "/home/debian-cd/.ssh/known_hosts" -R [localhost]:10022 LOCALHOST=`hostname` FQDN=`hostname -f` virsh -c qemu:///system start ${VMNAME} virsh -c qemu:///system qemu-monitor-command --hmp ${VMNAME} 'hostfwd_add ::10022-:22' virsh -c qemu:///system qemu-monitor-command --hmp ${VMNAME} 'hostfwd_add ::10873-:873' scp -P10022 -o 'StrictHostKeyChecking no' inside_vm/rsyncd.conf root@localhost:/etc/rsyncd.conf ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "perl -pi -e 's/RSYNC_ENABLE=false/RSYNC_ENABLE=true/' /etc/default/rsync" ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "mkdir -p /w/${LOCALHOST}/vm/bin/" #scp -P10022 -o 'StrictHostKeyChecking no' inside_vm/init root@localhost:/w/${LOCALHOST}/vm/bin/init cat inside_vm/init | \ sed "s/pettersson/${LOCALHOST}/g" | \ ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "cat >/w/${LOCALHOST}/vm/bin/init" ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "chmod +x /w/${LOCALHOST}/vm/bin/init" ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost service rsync restart sleep 1 rsync --port=10873 localhost:: ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "cat > /etc/cron.d/reboot" <<-FIN # /etc/cron.d/reboot SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # m h dom mon dow user command */1 * * * * root /etc/cron.hourly/live-build-cgi > /dev/null 2>&1 @reboot root script -c /w/${LOCALHOST}/vm/bin/init /w/out/log/init @reboot root service rsync start FIN ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "cat > /root/.ssh/config" <<-FIN Host pull-from-${LOCALHOST} HostName ${FQDN} User debian-cd IdentityFile /root/.ssh/${LOCALHOST}_live_to_vm Host push-to-${LOCALHOST} HostName ${FQDN} User debian-cd IdentityFile /root/.ssh/${LOCALHOST}_live_from_vm FIN [ -d key ] || mkdir key [ -f key/${LOCALHOST}_live_to_vm ] || ssh-keygen -b 2048 -t rsa -f key/${LOCALHOST}_live_to_vm -q -P '' [ -f key/${LOCALHOST}_live_from_vm ] || ssh-keygen -b 2048 -t rsa -f key/${LOCALHOST}_live_from_vm -q -P '' scp -P10022 -o 'StrictHostKeyChecking no' \ key/${LOCALHOST}_live_to_vm \ key/${LOCALHOST}_live_from_vm \ root@localhost:.ssh/ if ! grep -q "debian-cd@${LOCALHOST}" ${HOME}/.ssh/authorized_keys then cat key/${LOCALHOST}_live_from_vm.pub key/${LOCALHOST}_live_to_vm.pub >> ${HOME}/.ssh/authorized_keys fi # Validate return ticket ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "ssh -o 'StrictHostKeyChecking no' pull-from-${LOCALHOST} exit" ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost "ssh -o 'StrictHostKeyChecking no' push-to-${LOCALHOST} exit" #ssh -p10022 -o 'StrictHostKeyChecking no' root@localhost virsh -c qemu:///system shutdown ${VMNAME} } # Allow to wait running or shutoff VM state waitstate(){ WAITEDSTATE=x$1 echo "Waiting for $WAITEDSTATE state" OK=false while ! $OK do CURSTATE=x`virsh -c qemu:///system domstate ${VMNAME} 2>/dev/null|sed 's/ //'` if [ "${CURSTATE}" != "${WAITEDSTATE}" ] then echo -n "." else echo -n "+" OK=true fi sleep 5 done } # Generate a VM using virt-install and the preseed file # to be able to run this you may need to add the current debian-cd user # to kvm and libvirt groups # adduser debian-cd kvm # adduser debian-cd libvirt generatevmdeb(){ echo "Generate vm, this will take a lot of time" NET="--network bridge='br0'" NET="--network user,model='virtio'" createpreseed ${PRESEED} virt-install \ --connect ${VIRSHOST} \ --virt-type kvm \ --name ${VMNAME} \ --ram ${RAM} \ --vcpus ${CPU} \ --disk path="${IMAGE_DIR}/${CORE_IMAGE}",size="${CORE_SIZE}",format="${FORMAT}" \ --disk path="${IMAGE_DIR}/${WORKSPACE_IMAGE}",size="${WORKSPACE_SIZE}",format="${FORMAT}" \ ${NET} \ --os-variant ${OSVARIANT} \ --boot hd \ --graphics vnc \ --location=http://${DI_DEBMIRROR}/debian/dists/${DIST}/main/installer\-${ARCH}/ \ --extra-args="auto debian-installer/locale=${LAYOUT} keyboard-configuration/xkb-keymap=${KBD}" \ --initrd-inject=${PRESEED} \ --noautoconsole echo "Waiting running state" waitstate running } [ -f /usr/bin/virt-install ] || sudo apt-get install virtinst [ -f /usr/bin/virsh ] || sudo apt-get install libvirt-clients generatevmdeb waitstate shutoff finalizevm