diff -Nru oz-0.15.0/debian/changelog oz-0.16.0/debian/changelog --- oz-0.15.0/debian/changelog 2016-07-18 10:05:20.000000000 +0000 +++ oz-0.16.0/debian/changelog 2017-08-18 18:46:46.000000000 +0000 @@ -1,3 +1,10 @@ +oz (0.16.0-1) unstable; urgency=low + + * New upstream version. + * Update Standards-Version from 3.9.8 to 4.0.0. + + -- Simon Josefsson Fri, 18 Aug 2017 20:46:46 +0200 + oz (0.15.0-1) unstable; urgency=low * New upstream version. diff -Nru oz-0.15.0/debian/control oz-0.16.0/debian/control --- oz-0.15.0/debian/control 2016-07-18 10:04:12.000000000 +0000 +++ oz-0.16.0/debian/control 2017-08-18 18:46:46.000000000 +0000 @@ -3,7 +3,7 @@ Section: python Priority: optional Build-Depends: debhelper (>= 9), python-all (>= 2.6.6-3) -Standards-Version: 3.9.8 +Standards-Version: 4.0.0 Homepage: http://github.com/clalancette/oz/wiki Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/oz.git Vcs-Git: https://anonscm.debian.org/git/collab-maint/oz.git diff -Nru oz-0.15.0/.gitignore oz-0.16.0/.gitignore --- oz-0.15.0/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/.gitignore 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,8 @@ +*.gz +*.pyc +*.sw[po] +*~ +*.egg +*.egg-info +tests/.venv +tests/results.xml diff -Nru oz-0.15.0/Makefile oz-0.16.0/Makefile --- oz-0.15.0/Makefile 2014-12-30 02:22:58.000000000 +0000 +++ oz-0.16.0/Makefile 2017-08-08 18:46:43.000000000 +0000 @@ -1,19 +1,21 @@ VERSION = $(shell egrep "^VERSION" setup.py | awk '{print $$3}') VENV_DIR = tests/.venv -sdist: +sdist: oz.spec.in python setup.py sdist +oz.spec: sdist + signed-tarball: sdist gpg --detach-sign --armor -o dist/oz-$(VERSION).tar.gz.sign dist/oz-$(VERSION).tar.gz -signed-rpm: sdist +signed-rpm: oz.spec rpmbuild -ba oz.spec --sign --define "_sourcedir `pwd`/dist" -rpm: sdist +rpm: oz.spec rpmbuild -ba oz.spec --define "_sourcedir `pwd`/dist" -srpm: sdist +srpm: oz.spec rpmbuild -bs oz.spec --define "_sourcedir `pwd`/dist" deb: @@ -42,8 +44,17 @@ @[ -f $(VENV_DIR)/bin/activate ] && source $(VENV_DIR)/bin/activate ; python setup.py test @(type deactivate 2>/dev/null | grep -q 'function') && deactivate || true +tests: unittests + +test-coverage: + python-coverage run --source oz /usr/bin/py.test --verbose tests + python-coverage html + xdg-open htmlcov/index.html + pylint: pylint --rcfile=pylint.conf oz oz-install oz-customize oz-cleanup-cache oz-generate-icicle clean: - rm -rf MANIFEST build dist usr *~ oz.spec *.pyc oz/*~ oz/*.pyc examples/*~ oz/auto/*~ man/*~ docs/*~ man/*.html $(VENV_DIR) tests/tdl/*~ tests/factory/*~ tests/results.xml + rm -rf MANIFEST build dist usr *~ oz.spec *.pyc oz/*~ oz/*.pyc examples/*~ oz/auto/*~ man/*~ docs/*~ man/*.html $(VENV_DIR) tests/tdl/*~ tests/factory/*~ tests/results.xml htmlcov + +.PHONY: sdist oz.spec signed-tarball signed-rpm rpm srpm deb release man2html virtualenv unittests tests test-coverage pylint clean diff -Nru oz-0.15.0/MANIFEST.in oz-0.16.0/MANIFEST.in --- oz-0.15.0/MANIFEST.in 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/MANIFEST.in 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,3 @@ +include README COPYING examples/*.tdl man/*.1 oz.cfg docs/*.rng oz/*.rng Makefile +include man/examples/*.example man/examples/header man/examples/footer +include oz/auto/* diff -Nru oz-0.15.0/oz/auto/Debian5.auto oz-0.16.0/oz/auto/Debian5.auto --- oz-0.15.0/oz/auto/Debian5.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Debian5.auto 2017-08-08 18:46:43.000000000 +0000 @@ -34,4 +34,39 @@ d-i apt-setup/volatile_host string d-i apt-setup/security_host string +d-i preseed/late_command string \ + in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# X-Start-Before: sshd' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# deletes the SSH host keys at the end of installation. This solves' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# the above problem, but introduces the problem of having no way to' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# SSH into the machine without manual intervention. This service checks' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# to see if host keys are already installed, and if not, recreates them.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Note that after the very first boot, this service could be removed.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'case "$1" in' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' start)' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' [ -r /etc/ssh/ssh_host_rsa_key ] || /usr/sbin/dpkg-reconfigure openssh-server' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' ;;' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'esac' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'exit 0' >> /target/etc/init.d/oz-generate-ssh ; \ + in-target chmod 755 /etc/init.d/oz-generate-ssh ; \ + in-target ln -s /etc/init.d/oz-generate-ssh /etc/rc2.d/S40oz-generate-ssh ; \ + in-target update-rc.d oz-generate-ssh enable ; \ + sed -i -e 's/^deb cdrom/# deb cdrom/' /target/etc/apt/sources.list + d-i finish-install/reboot_in_progress note diff -Nru oz-0.15.0/oz/auto/Debian6.auto oz-0.16.0/oz/auto/Debian6.auto --- oz-0.15.0/oz/auto/Debian6.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Debian6.auto 2017-08-08 18:46:43.000000000 +0000 @@ -36,6 +36,39 @@ d-i apt-setup/volatile_host string d-i apt-setup/security_host string -d-i preseed/late_command string sed -i "/^deb cdrom:/s/^/#/" /target/etc/apt/sources.list; in-target apt-get update +d-i preseed/late_command string \ + in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# X-Start-Before: sshd' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# deletes the SSH host keys at the end of installation. This solves' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# the above problem, but introduces the problem of having no way to' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# SSH into the machine without manual intervention. This service checks' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# to see if host keys are already installed, and if not, recreates them.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Note that after the very first boot, this service could be removed.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'case "$1" in' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' start)' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' [ -r /etc/ssh/ssh_host_rsa_key ] || /usr/sbin/dpkg-reconfigure openssh-server' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' ;;' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'esac' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'exit 0' >> /target/etc/init.d/oz-generate-ssh ; \ + in-target chmod 755 /etc/init.d/oz-generate-ssh ; \ + in-target ln -s /etc/init.d/oz-generate-ssh /etc/rc2.d/S40oz-generate-ssh ; \ + in-target update-rc.d oz-generate-ssh enable ; \ + sed -i -e 's/^deb cdrom/# deb cdrom/' /target/etc/apt/sources.list d-i finish-install/reboot_in_progress note diff -Nru oz-0.15.0/oz/auto/Debian7.auto oz-0.16.0/oz/auto/Debian7.auto --- oz-0.15.0/oz/auto/Debian7.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Debian7.auto 2017-08-08 18:46:43.000000000 +0000 @@ -36,4 +36,39 @@ d-i apt-setup/volatile_host string d-i apt-setup/security_host string +d-i preseed/late_command string \ + in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# X-Start-Before: sshd' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# deletes the SSH host keys at the end of installation. This solves' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# the above problem, but introduces the problem of having no way to' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# SSH into the machine without manual intervention. This service checks' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# to see if host keys are already installed, and if not, recreates them.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Note that after the very first boot, this service could be removed.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'case "$1" in' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' start)' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' [ -r /etc/ssh/ssh_host_rsa_key ] || /usr/sbin/dpkg-reconfigure openssh-server' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' ;;' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'esac' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'exit 0' >> /target/etc/init.d/oz-generate-ssh ; \ + in-target chmod 755 /etc/init.d/oz-generate-ssh ; \ + in-target ln -s /etc/init.d/oz-generate-ssh /etc/rc2.d/S01oz-generate-ssh ; \ + in-target update-rc.d oz-generate-ssh enable ; \ + sed -i -e 's/^deb cdrom/# deb cdrom/' /target/etc/apt/sources.list + d-i finish-install/reboot_in_progress note diff -Nru oz-0.15.0/oz/auto/Debian8.auto oz-0.16.0/oz/auto/Debian8.auto --- oz-0.15.0/oz/auto/Debian8.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Debian8.auto 2017-08-08 18:46:43.000000000 +0000 @@ -32,4 +32,37 @@ d-i apt-setup/security_host string d-i apt-setup/services-select multiselect +d-i preseed/late_command string \ + in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# deletes the SSH host keys at the end of installation. This solves' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# the above problem, but introduces the problem of having no way to' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# SSH into the machine without manual intervention. This service checks' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# to see if host keys are already installed, and if not, recreates them.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Note that after the very first boot, this service could be removed.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'case "$1" in' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' start)' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' [ -r /etc/ssh/ssh_host_rsa_key ] || /usr/sbin/dpkg-reconfigure openssh-server' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' ;;' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'esac' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'exit 0' >> /target/etc/init.d/oz-generate-ssh ; \ + in-target chmod 755 /etc/init.d/oz-generate-ssh ; \ + in-target ln -s /etc/init.d/oz-generate-ssh /etc/rc2.d/S40oz-generate-ssh ; \ + sed -i -e 's/^deb cdrom/# deb cdrom/' /target/etc/apt/sources.list + d-i finish-install/reboot_in_progress note diff -Nru oz-0.15.0/oz/auto/Fedora24.auto oz-0.16.0/oz/auto/Fedora24.auto --- oz-0.15.0/oz/auto/Fedora24.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Fedora24.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,23 @@ +install +text +keyboard us +lang en_US.UTF-8 +skipx +network --device ens3 --bootproto dhcp +rootpw %ROOTPW% +firewall --disabled +authconfig --enableshadow --enablemd5 +selinux --enforcing +timezone --utc America/New_York +bootloader --location=mbr --append="console=tty0 console=ttyS0,115200" + +zerombr +clearpart --all --initlabel +autopart --type=lvm + +reboot + +%packages +@core + +%end diff -Nru oz-0.15.0/oz/auto/Fedora25.auto oz-0.16.0/oz/auto/Fedora25.auto --- oz-0.15.0/oz/auto/Fedora25.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Fedora25.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,23 @@ +install +text +keyboard us +lang en_US.UTF-8 +skipx +network --device ens3 --bootproto dhcp +rootpw %ROOTPW% +firewall --disabled +authconfig --enableshadow --enablemd5 +selinux --enforcing +timezone --utc America/New_York +bootloader --location=mbr --append="console=tty0 console=ttyS0,115200" + +zerombr +clearpart --all --initlabel +autopart --type=lvm + +reboot + +%packages +@core + +%end diff -Nru oz-0.15.0/oz/auto/Fedora26.auto oz-0.16.0/oz/auto/Fedora26.auto --- oz-0.15.0/oz/auto/Fedora26.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Fedora26.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,23 @@ +install +text +keyboard us +lang en_US.UTF-8 +skipx +network --device ens3 --bootproto dhcp +rootpw %ROOTPW% +firewall --disabled +authconfig --enableshadow --enablemd5 +selinux --enforcing +timezone --utc America/New_York +bootloader --location=mbr --append="console=tty0 console=ttyS0,115200" + +zerombr +clearpart --all --initlabel +autopart --type=lvm + +reboot + +%packages +@core + +%end diff -Nru oz-0.15.0/oz/auto/FreeBSD11.0.auto oz-0.16.0/oz/auto/FreeBSD11.0.auto --- oz-0.15.0/oz/auto/FreeBSD11.0.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/FreeBSD11.0.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,28 @@ +# Install rules. +PARTITIONS="vtbd0 { auto freebsd-ufs / }" +BSDINSTALL_DISTSITE="http://mirror.leaseweb.net/freebsd/releases/amd64/11.0-RELEASE/" +DISTRIBUTIONS="kernel.txz base.txz" + +# Post script. +#!/bin/sh +hostname=$(openssl rand -hex 4) +echo "ifconfig_vtnet0=DHCP" >> /etc/rc.conf +echo "sshd_enable=YES" >> /etc/rc.conf +echo "hostname=$hostname" >> /etc/rc.conf +echo "#ROOTPW#" | pw mod user root -h 0 + +# Clean up any old dhclient stuff from the preparation stage. +rm -f /var/db/dhclient.leases.* + +# The machine check fails pretty hard on a hypervisor running on top of AMD +# processors, so just disable it. +echo "hw.mca.enabled=0" >> /boot/loader.conf.local + +# Log kernel (boot) messages to console, so that they can be captured. +echo 'console="vidconsole,comconsole"' >> /boot/loader.conf.local +sed -i.bak -E "s/^(ttyu0.+)dialup(.+)off(.+)secure/\1 vt100 on secure/" /etc/ttys +rm -f /etc/ttys.bak + +# The installer wants to reboot to the newly installed system, though at this +# point we'd rather send the image to the cloud :) +shutdown -p now diff -Nru oz-0.15.0/oz/auto/Mageia2.auto oz-0.16.0/oz/auto/Mageia2.auto --- oz-0.15.0/oz/auto/Mageia2.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Mageia2.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,139 @@ +#!/usr/bin/perl -cw +# +# You should check the syntax of this file before using it in an auto-install. +# You can do this with 'perl -cw auto_inst.cfg.pl' or by executing this file +# (note the '#!/usr/bin/perl -cw' on the first line). +$o = { + 'autoExitInstall' => '1', + 'interactiveSteps' => [], + 'no_suggests' => 1, + 'mkbootdisk' => 0, + 'isUpgrade' => 0, + 'excludedocs' => 1, + 'minimal' => 1, + 'miscellaneous' => { + 'numlock' => 1, + }, + + 'netc' => { + 'ZEROCONF_HOSTNAME' => undef, + 'NETWORKING' => 'yes', + 'NET_DEVICE' => 'eth0', + 'DHCP' => 'yes' + }, + 'locale' => { + 'lang' => 'en_US', + 'country' => 'US', + 'utf8' => 1, + 'langs' => { + 'en_US' => 1 + } + }, + 'mkbootdisk' => 0, + 'partitions' => [ + { + 'size' => 12273597, + 'mntpoint' => '/', + 'type' => 1155, + 'fs_type' => 'ext4' + }, + { + 'size' => 1012032, + 'mntpoint' => 'swap', + 'type' => 130, + 'fs_type' => 'swap' + }, + { + 'size' => 1024, + 'ratio' => 100, + 'mntpoint' => '/home', + 'type' => 1155, + 'fs_type' => 'ext4' + } + ], + 'printer' => { + 'MANUALCUPSCONFIG' => undef, + 'BROWSEPOLLPORT' => undef, + 'BROWSEPOLLADDR' => undef, + 'SPOOLER' => undef, + 'configured' => {}, + 'DEFAULT' => undef + }, + 'intf' => { + 'eth0' => { + 'BROADCAST' => '', + 'ONBOOT' => 'yes', + 'BOOTPROTO' => 'dhcp', + 'DEVICE' => 'eth0', + 'NETMASK' => '255.255.255.0', + 'NETWORK' => '' + } + }, + 'authentication' => { + 'shadow' => 1, + 'md5' => 1, + 'local' => undef + }, + 'partitioning' => { + 'auto_allocate' => '1', + 'clearall' => '1', + 'eraseBadPartitions' => 0 + }, + 'users' => [{ +'icon' => 'default', +'realname' => 'user', +'uid' => undef, +'groups' => [], +'name' => 'user', +'shell' => '/bin/bash', +'password' => %ROOTPW%, +'gid' => undef +}], + 'security' => 2, + 'X' => { disabled => 1 }, + 'skipped_packages' => [ 'xdm', 'shorewall', + 'shorewall-core', 'mageia-gfxboot-theme'], + 'default_packages' => [ + 'basesystem-minimal', + 'urpmi', + 'sudo', + 'openssh-client', + 'openssh-server', 'vim-minimal' + ], + 'superuser' => { + 'home' => '/root', + 'shell' => '/bin/bash', + 'realname' => 'root', + 'password' => %ROOTPW%, + 'uid' => '0', + 'gid' => '0' + }, + 'manualFstab' => [], + 'libsafe' => 0, + 'useSupermount' => '0', + 'timezone' => { + 'UTC' => 1, + 'ntp' => undef, + 'timezone' => 'America/New_York' + }, + 'netcnx' => { + 'lan' => {}, + 'type' => 'lan' + }, + 'keyboard' => { + 'unsafe' => 1, + 'GRP_TOGGLE' => '', + 'KEYBOARD' => 'us', + 'KBCHARSET' => 'C' + }, + 'security_user' => '', + 'mouse' => { + 'MOUSETYPE' => 'ps/2', + 'device' => 'psaux', + 'name' => 'Standard', + 'nbuttons' => 2, + 'type' => 'PS/2', + 'XMOUSETYPE' => 'PS/2', + 'EMULATEWHEEL' => undef + } + }; diff -Nru oz-0.15.0/oz/auto/Mageia3.auto oz-0.16.0/oz/auto/Mageia3.auto --- oz-0.15.0/oz/auto/Mageia3.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Mageia3.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,139 @@ +#!/usr/bin/perl -cw +# +# You should check the syntax of this file before using it in an auto-install. +# You can do this with 'perl -cw auto_inst.cfg.pl' or by executing this file +# (note the '#!/usr/bin/perl -cw' on the first line). +$o = { + 'autoExitInstall' => '1', + 'interactiveSteps' => [], + 'no_suggests' => 1, + 'mkbootdisk' => 0, + 'isUpgrade' => 0, + 'excludedocs' => 1, + 'minimal' => 1, + 'miscellaneous' => { + 'numlock' => 1, + }, + + 'netc' => { + 'ZEROCONF_HOSTNAME' => undef, + 'NETWORKING' => 'yes', + 'NET_DEVICE' => 'eth0', + 'DHCP' => 'yes' + }, + 'locale' => { + 'lang' => 'en_US', + 'country' => 'US', + 'utf8' => 1, + 'langs' => { + 'en_US' => 1 + } + }, + 'mkbootdisk' => 0, + 'partitions' => [ + { + 'size' => 12273597, + 'mntpoint' => '/', + 'type' => 1155, + 'fs_type' => 'ext4' + }, + { + 'size' => 1012032, + 'mntpoint' => 'swap', + 'type' => 130, + 'fs_type' => 'swap' + }, + { + 'size' => 1024, + 'ratio' => 100, + 'mntpoint' => '/home', + 'type' => 1155, + 'fs_type' => 'ext4' + } + ], + 'printer' => { + 'MANUALCUPSCONFIG' => undef, + 'BROWSEPOLLPORT' => undef, + 'BROWSEPOLLADDR' => undef, + 'SPOOLER' => undef, + 'configured' => {}, + 'DEFAULT' => undef + }, + 'intf' => { + 'eth0' => { + 'BROADCAST' => '', + 'ONBOOT' => 'yes', + 'BOOTPROTO' => 'dhcp', + 'DEVICE' => 'eth0', + 'NETMASK' => '255.255.255.0', + 'NETWORK' => '' + } + }, + 'authentication' => { + 'shadow' => 1, + 'md5' => 1, + 'local' => undef + }, + 'partitioning' => { + 'auto_allocate' => '1', + 'clearall' => '1', + 'eraseBadPartitions' => 0 + }, + 'users' => [{ +'icon' => 'default', +'realname' => 'user', +'uid' => undef, +'groups' => [], +'name' => 'user', +'shell' => '/bin/bash', +'password' => %ROOTPW%, +'gid' => undef +}], + 'security' => 2, + 'X' => { disabled => 1 }, + 'skipped_packages' => [ 'xdm', 'shorewall', + 'shorewall-core', 'mageia-gfxboot-theme'], + 'default_packages' => [ + 'basesystem-minimal', + 'urpmi', + 'sudo', + 'openssh-client', + 'openssh-server', 'vim-minimal' + ], + 'superuser' => { + 'home' => '/root', + 'shell' => '/bin/bash', + 'realname' => 'root', + 'password' => %ROOTPW%, + 'uid' => '0', + 'gid' => '0' + }, + 'manualFstab' => [], + 'libsafe' => 0, + 'useSupermount' => '0', + 'timezone' => { + 'UTC' => 1, + 'ntp' => undef, + 'timezone' => 'America/New_York' + }, + 'netcnx' => { + 'lan' => {}, + 'type' => 'lan' + }, + 'keyboard' => { + 'unsafe' => 1, + 'GRP_TOGGLE' => '', + 'KEYBOARD' => 'us', + 'KBCHARSET' => 'C' + }, + 'security_user' => '', + 'mouse' => { + 'MOUSETYPE' => 'ps/2', + 'device' => 'psaux', + 'name' => 'Standard', + 'nbuttons' => 2, + 'type' => 'PS/2', + 'XMOUSETYPE' => 'PS/2', + 'EMULATEWHEEL' => undef + } + }; diff -Nru oz-0.15.0/oz/auto/Mageia4.1.auto oz-0.16.0/oz/auto/Mageia4.1.auto --- oz-0.15.0/oz/auto/Mageia4.1.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Mageia4.1.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,139 @@ +#!/usr/bin/perl -cw +# +# You should check the syntax of this file before using it in an auto-install. +# You can do this with 'perl -cw auto_inst.cfg.pl' or by executing this file +# (note the '#!/usr/bin/perl -cw' on the first line). +$o = { + 'autoExitInstall' => '1', + 'interactiveSteps' => [], + 'no_suggests' => 1, + 'mkbootdisk' => 0, + 'isUpgrade' => 0, + 'excludedocs' => 1, + 'minimal' => 1, + 'miscellaneous' => { + 'numlock' => 1, + }, + + 'netc' => { + 'ZEROCONF_HOSTNAME' => undef, + 'NETWORKING' => 'yes', + 'NET_DEVICE' => 'eth0', + 'DHCP' => 'yes' + }, + 'locale' => { + 'lang' => 'en_US', + 'country' => 'US', + 'utf8' => 1, + 'langs' => { + 'en_US' => 1 + } + }, + 'mkbootdisk' => 0, + 'partitions' => [ + { + 'size' => 12273597, + 'mntpoint' => '/', + 'type' => 1155, + 'fs_type' => 'ext4' + }, + { + 'size' => 1012032, + 'mntpoint' => 'swap', + 'type' => 130, + 'fs_type' => 'swap' + }, + { + 'size' => 1024, + 'ratio' => 100, + 'mntpoint' => '/home', + 'type' => 1155, + 'fs_type' => 'ext4' + } + ], + 'printer' => { + 'MANUALCUPSCONFIG' => undef, + 'BROWSEPOLLPORT' => undef, + 'BROWSEPOLLADDR' => undef, + 'SPOOLER' => undef, + 'configured' => {}, + 'DEFAULT' => undef + }, + 'intf' => { + 'eth0' => { + 'BROADCAST' => '', + 'ONBOOT' => 'yes', + 'BOOTPROTO' => 'dhcp', + 'DEVICE' => 'eth0', + 'NETMASK' => '255.255.255.0', + 'NETWORK' => '' + } + }, + 'authentication' => { + 'shadow' => 1, + 'md5' => 1, + 'local' => undef + }, + 'partitioning' => { + 'auto_allocate' => '1', + 'clearall' => '1', + 'eraseBadPartitions' => 0 + }, + 'users' => [{ +'icon' => 'default', +'realname' => 'user', +'uid' => undef, +'groups' => [], +'name' => 'user', +'shell' => '/bin/bash', +'password' => %ROOTPW%, +'gid' => undef +}], + 'security' => 2, + 'X' => { disabled => 1 }, + 'skipped_packages' => [ 'xdm', 'shorewall', + 'shorewall-core', 'mageia-gfxboot-theme'], + 'default_packages' => [ + 'basesystem-minimal', + 'urpmi', + 'sudo', + 'openssh-client', + 'openssh-server', 'vim-minimal' + ], + 'superuser' => { + 'home' => '/root', + 'shell' => '/bin/bash', + 'realname' => 'root', + 'password' => %ROOTPW%, + 'uid' => '0', + 'gid' => '0' + }, + 'manualFstab' => [], + 'libsafe' => 0, + 'useSupermount' => '0', + 'timezone' => { + 'UTC' => 1, + 'ntp' => undef, + 'timezone' => 'America/New_York' + }, + 'netcnx' => { + 'lan' => {}, + 'type' => 'lan' + }, + 'keyboard' => { + 'unsafe' => 1, + 'GRP_TOGGLE' => '', + 'KEYBOARD' => 'us', + 'KBCHARSET' => 'C' + }, + 'security_user' => '', + 'mouse' => { + 'MOUSETYPE' => 'ps/2', + 'device' => 'psaux', + 'name' => 'Standard', + 'nbuttons' => 2, + 'type' => 'PS/2', + 'XMOUSETYPE' => 'PS/2', + 'EMULATEWHEEL' => undef + } + }; diff -Nru oz-0.15.0/oz/auto/Mageia5.auto oz-0.16.0/oz/auto/Mageia5.auto --- oz-0.15.0/oz/auto/Mageia5.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Mageia5.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,139 @@ +#!/usr/bin/perl -cw +# +# You should check the syntax of this file before using it in an auto-install. +# You can do this with 'perl -cw auto_inst.cfg.pl' or by executing this file +# (note the '#!/usr/bin/perl -cw' on the first line). +$o = { + 'autoExitInstall' => '1', + 'interactiveSteps' => [], + 'no_suggests' => 1, + 'mkbootdisk' => 0, + 'isUpgrade' => 0, + 'excludedocs' => 1, + 'minimal' => 1, + 'miscellaneous' => { + 'numlock' => 1, + }, + + 'netc' => { + 'ZEROCONF_HOSTNAME' => undef, + 'NETWORKING' => 'yes', + 'NET_DEVICE' => 'eth0', + 'DHCP' => 'yes' + }, + 'locale' => { + 'lang' => 'en_US', + 'country' => 'US', + 'utf8' => 1, + 'langs' => { + 'en_US' => 1 + } + }, + 'mkbootdisk' => 0, + 'partitions' => [ + { + 'size' => 12273597, + 'mntpoint' => '/', + 'type' => 1155, + 'fs_type' => 'ext4' + }, + { + 'size' => 1012032, + 'mntpoint' => 'swap', + 'type' => 130, + 'fs_type' => 'swap' + }, + { + 'size' => 1024, + 'ratio' => 100, + 'mntpoint' => '/home', + 'type' => 1155, + 'fs_type' => 'ext4' + } + ], + 'printer' => { + 'MANUALCUPSCONFIG' => undef, + 'BROWSEPOLLPORT' => undef, + 'BROWSEPOLLADDR' => undef, + 'SPOOLER' => undef, + 'configured' => {}, + 'DEFAULT' => undef + }, + 'intf' => { + 'eth0' => { + 'BROADCAST' => '', + 'ONBOOT' => 'yes', + 'BOOTPROTO' => 'dhcp', + 'DEVICE' => 'eth0', + 'NETMASK' => '255.255.255.0', + 'NETWORK' => '' + } + }, + 'authentication' => { + 'shadow' => 1, + 'md5' => 1, + 'local' => undef + }, + 'partitioning' => { + 'auto_allocate' => '1', + 'clearall' => '1', + 'eraseBadPartitions' => 0 + }, + 'users' => [{ +'icon' => 'default', +'realname' => 'user', +'uid' => undef, +'groups' => [], +'name' => 'user', +'shell' => '/bin/bash', +'password' => %ROOTPW%, +'gid' => undef +}], + 'security' => 2, + 'X' => { disabled => 1 }, + 'skipped_packages' => [ 'xdm', 'shorewall', + 'shorewall-core', 'mageia-gfxboot-theme'], + 'default_packages' => [ + 'basesystem-minimal', + 'urpmi', + 'sudo', + 'openssh-client', + 'openssh-server', 'vim-minimal' + ], + 'superuser' => { + 'home' => '/root', + 'shell' => '/bin/bash', + 'realname' => 'root', + 'password' => %ROOTPW%, + 'uid' => '0', + 'gid' => '0' + }, + 'manualFstab' => [], + 'libsafe' => 0, + 'useSupermount' => '0', + 'timezone' => { + 'UTC' => 1, + 'ntp' => undef, + 'timezone' => 'America/New_York' + }, + 'netcnx' => { + 'lan' => {}, + 'type' => 'lan' + }, + 'keyboard' => { + 'unsafe' => 1, + 'GRP_TOGGLE' => '', + 'KEYBOARD' => 'us', + 'KBCHARSET' => 'C' + }, + 'security_user' => '', + 'mouse' => { + 'MOUSETYPE' => 'ps/2', + 'device' => 'psaux', + 'name' => 'Standard', + 'nbuttons' => 2, + 'type' => 'PS/2', + 'XMOUSETYPE' => 'PS/2', + 'EMULATEWHEEL' => undef + } + }; diff -Nru oz-0.15.0/oz/auto/Mandrake9.0.auto oz-0.16.0/oz/auto/Mandrake9.0.auto --- oz-0.15.0/oz/auto/Mandrake9.0.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Mandrake9.0.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,155 @@ +#!/usr/bin/perl -cw +# +# You should check the syntax of this file before using it in an auto-install. +# You can do this with 'perl -cw auto_inst.cfg.pl' or by executing this file +# (note the '#!/usr/bin/perl -cw' on the first line). +$o = { + 'autoExitInstall' => '1', + 'netc' => { + 'ZEROCONF_HOSTNAME' => undef, + 'NETWORKING' => 'yes', + 'NET_DEVICE' => 'eth0', + 'DHCP' => 'yes' + }, + 'locale' => { + 'lang' => 'en_US', + 'country' => 'US', + 'utf8' => '', + 'langs' => { + 'en_US' => 1 + } + }, + 'mkbootdisk' => 0, + 'partitions' => [ + { + 'size' => 12273597, + 'mntpoint' => '/', + 'type' => 1155 + }, + { + 'size' => 1012032, + 'mntpoint' => 'swap', + 'type' => 130 + }, + { + 'size' => 28643832, + 'mntpoint' => '/home', + 'type' => 1155 + } + ], + 'printer' => { + 'MANUALCUPSCONFIG' => undef, + 'BROWSEPOLLPORT' => undef, + 'BROWSEPOLLADDR' => undef, + 'SPOOLER' => undef, + 'configured' => {}, + 'DEFAULT' => undef + }, + 'intf' => { + 'eth0' => { + 'BROADCAST' => '', + 'ONBOOT' => 'yes', + 'BOOTPROTO' => 'dhcp', + 'DEVICE' => 'eth0', + 'NETMASK' => '255.255.255.0', + 'NETWORK' => '' + } + }, + 'authentication' => { + 'shadow' => 1, + 'md5' => 1, + 'local' => undef + }, + 'partitioning' => { + 'auto_allocate' => '1', + 'clearall' => '1', + 'eraseBadPartitions' => 0 + }, + 'users' => [], + 'security' => 2, + 'default_packages' => [ + 'locales', + 'locales-en', + 'msec', + 'devfsd', + 'kernel-enterprise-2.4.22.10mdk', + 'eject', + 'gnupg', + 'urpmi', + 'hotplug', + 'procmail', + 'mtools', + 'groff', + 'at', + 'coreutils-doc', + 'dhcp-client', + 'ftp-client-krb5', + 'grub', + 'harddrake', + 'hdparm', + 'hexedit', + 'info', + 'ldetect', + 'man', + 'mkxauth', + 'numlock', + 'open', + 'openssh-server', + 'rfbdrake', + 'samba-server', + 'slocate', + 'strace', + 'sudo', + 'tmdns', + 'tmpwatch', + 'words', + 'zcip', + 'patch', + 'recode', + 'emacs-X11', + 'man-pages', + 'nfs-utils', + 'rgrep', + 'screen', + 'symlinks', + 'webmin', + 'xterm' + ], + 'superuser' => { + 'home' => '/root', + 'shell' => '/bin/bash', + 'realname' => 'root', + 'password' => %ROOTPW%, + 'uid' => '0', + 'gid' => '0' + }, + 'manualFstab' => [], + 'libsafe' => 0, + 'useSupermount' => '1', + 'timezone' => { + 'UTC' => 1, + 'ntp' => undef, + 'timezone' => 'America/New_York' + }, + 'netcnx' => { + 'lan' => {}, + 'type' => 'lan' + }, + 'keyboard' => { + 'unsafe' => 1, + 'GRP_TOGGLE' => '', + 'KEYBOARD' => 'us', + 'KBCHARSET' => 'C' + }, + 'X' => {}, + 'security_user' => '', + 'mouse' => { + 'MOUSETYPE' => 'ps/2', + 'device' => 'psaux', + 'name' => 'Standard', + 'nbuttons' => 2, + 'type' => 'PS/2', + 'XMOUSETYPE' => 'PS/2', + 'EMULATEWHEEL' => undef + } + }; diff -Nru oz-0.15.0/oz/auto/Ubuntu10.04.auto oz-0.16.0/oz/auto/Ubuntu10.04.auto --- oz-0.15.0/oz/auto/Ubuntu10.04.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu10.04.auto 2017-08-08 18:46:43.000000000 +0000 @@ -24,8 +24,7 @@ tasksel tasksel/first multiselect standard d-i pkgsel/include/install-recommends boolean true -d-i pkgsel/include string ltsp-server-standalone openssh-server python-software-properties -d-i preseed/late_command string chroot /target /usr/sbin/ltsp-update-sshkeys +d-i pkgsel/include string openssh-server d-i grub-installer/only_debian boolean true d-i grub-installer/with_other_os boolean true diff -Nru oz-0.15.0/oz/auto/Ubuntu14.04.auto oz-0.16.0/oz/auto/Ubuntu14.04.auto --- oz-0.15.0/oz/auto/Ubuntu14.04.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu14.04.auto 2017-08-08 18:46:43.000000000 +0000 @@ -43,8 +43,18 @@ # setup a service that will generate the keys on boot if necessary. d-i preseed/late_command string \ in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ - echo '#! /bin/sh' > /target/etc/init.d/oz-generate-ssh ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ diff -Nru oz-0.15.0/oz/auto/Ubuntu14.10.auto oz-0.16.0/oz/auto/Ubuntu14.10.auto --- oz-0.15.0/oz/auto/Ubuntu14.10.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu14.10.auto 2017-08-08 18:46:43.000000000 +0000 @@ -43,8 +43,18 @@ # setup a service that will generate the keys on boot if necessary. d-i preseed/late_command string \ in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ - echo '#! /bin/sh' > /target/etc/init.d/oz-generate-ssh ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ diff -Nru oz-0.15.0/oz/auto/Ubuntu15.04.auto oz-0.16.0/oz/auto/Ubuntu15.04.auto --- oz-0.15.0/oz/auto/Ubuntu15.04.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu15.04.auto 2017-08-08 18:46:43.000000000 +0000 @@ -43,8 +43,18 @@ # setup a service that will generate the keys on boot if necessary. d-i preseed/late_command string \ in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ - echo '#! /bin/sh' > /target/etc/init.d/oz-generate-ssh ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ diff -Nru oz-0.15.0/oz/auto/Ubuntu15.10.auto oz-0.16.0/oz/auto/Ubuntu15.10.auto --- oz-0.15.0/oz/auto/Ubuntu15.10.auto 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu15.10.auto 2017-08-08 18:46:43.000000000 +0000 @@ -43,8 +43,18 @@ # setup a service that will generate the keys on boot if necessary. d-i preseed/late_command string \ in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ - echo '#! /bin/sh' > /target/etc/init.d/oz-generate-ssh ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ diff -Nru oz-0.15.0/oz/auto/Ubuntu16.04.auto oz-0.16.0/oz/auto/Ubuntu16.04.auto --- oz-0.15.0/oz/auto/Ubuntu16.04.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu16.04.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,76 @@ +d-i debian-installer/locale string en_US +d-i console-setup/ask_detect boolean false +d-i console-setup/layoutcode string us + +d-i netcfg/choose_interface select auto +d-i netcfg/get_hostname string unassigned-hostname +d-i netcfg/get_domain string unassigned-domain +d-i netcfg/wireless_wep string + +d-i clock-setup/utc boolean true +d-i time/zone string US/Eastern + +d-i partman-auto/method string regular +d-i partman-auto/choose_recipe select home +d-i partman/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 passwd/root-login boolean true +d-i passwd/make-user boolean false +d-i passwd/root-password password %ROOTPW% +d-i passwd/root-password-again password %ROOTPW% + +tasksel tasksel/first multiselect standard +d-i pkgsel/include/install-recommends boolean true +d-i pkgsel/include string openssh-server + +d-i grub-installer/only_debian boolean true +d-i grub-installer/with_other_os boolean true + +d-i apt-setup/security_host string +base-config apt-setup/security-updates boolean false + +ubiquity ubiquity/summary note +ubiquity ubiquity/reboot boolean true + +d-i finish-install/reboot_in_progress note + +# In Debian/Ubuntu, ssh keys are generated at package install time. Because +# the disk image may be cached, we need to remove the ssh keys, but this means +# that ssh'ing into the server won't work later. So we remove the keys, but +# setup a service that will generate the keys on boot if necessary. +d-i preseed/late_command string \ + in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# deletes the SSH host keys at the end of installation. This solves' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# the above problem, but introduces the problem of having no way to' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# SSH into the machine without manual intervention. This service checks' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# to see if host keys are already installed, and if not, recreates them.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Note that after the very first boot, this service could be removed.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'case "$1" in' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' start)' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' [ -r /etc/ssh/ssh_host_rsa_key ] || /usr/sbin/dpkg-reconfigure openssh-server' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' ;;' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'esac' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'exit 0' >> /target/etc/init.d/oz-generate-ssh ; \ + in-target chmod 755 /etc/init.d/oz-generate-ssh ; \ + in-target ln -s /etc/init.d/oz-generate-ssh /etc/rc2.d/S40oz-generate-ssh ; \ + echo "GRUB_TERMINAL=console" >> /target/etc/default/grub ; \ + in-target /usr/sbin/update-grub diff -Nru oz-0.15.0/oz/auto/Ubuntu16.10.auto oz-0.16.0/oz/auto/Ubuntu16.10.auto --- oz-0.15.0/oz/auto/Ubuntu16.10.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu16.10.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,76 @@ +d-i debian-installer/locale string en_US +d-i console-setup/ask_detect boolean false +d-i console-setup/layoutcode string us + +d-i netcfg/choose_interface select auto +d-i netcfg/get_hostname string unassigned-hostname +d-i netcfg/get_domain string unassigned-domain +d-i netcfg/wireless_wep string + +d-i clock-setup/utc boolean true +d-i time/zone string US/Eastern + +d-i partman-auto/method string regular +d-i partman-auto/choose_recipe select home +d-i partman/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 passwd/root-login boolean true +d-i passwd/make-user boolean false +d-i passwd/root-password password %ROOTPW% +d-i passwd/root-password-again password %ROOTPW% + +tasksel tasksel/first multiselect standard +d-i pkgsel/include/install-recommends boolean true +d-i pkgsel/include string openssh-server + +d-i grub-installer/only_debian boolean true +d-i grub-installer/with_other_os boolean true + +d-i apt-setup/security_host string +base-config apt-setup/security-updates boolean false + +ubiquity ubiquity/summary note +ubiquity ubiquity/reboot boolean true + +d-i finish-install/reboot_in_progress note + +# In Debian/Ubuntu, ssh keys are generated at package install time. Because +# the disk image may be cached, we need to remove the ssh keys, but this means +# that ssh'ing into the server won't work later. So we remove the keys, but +# setup a service that will generate the keys on boot if necessary. +d-i preseed/late_command string \ + in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# deletes the SSH host keys at the end of installation. This solves' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# the above problem, but introduces the problem of having no way to' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# SSH into the machine without manual intervention. This service checks' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# to see if host keys are already installed, and if not, recreates them.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Note that after the very first boot, this service could be removed.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'case "$1" in' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' start)' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' [ -r /etc/ssh/ssh_host_rsa_key ] || /usr/sbin/dpkg-reconfigure openssh-server' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' ;;' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'esac' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'exit 0' >> /target/etc/init.d/oz-generate-ssh ; \ + in-target chmod 755 /etc/init.d/oz-generate-ssh ; \ + in-target ln -s /etc/init.d/oz-generate-ssh /etc/rc2.d/S40oz-generate-ssh ; \ + echo "GRUB_TERMINAL=console" >> /target/etc/default/grub ; \ + in-target /usr/sbin/update-grub diff -Nru oz-0.15.0/oz/auto/Ubuntu17.04.auto oz-0.16.0/oz/auto/Ubuntu17.04.auto --- oz-0.15.0/oz/auto/Ubuntu17.04.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Ubuntu17.04.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,76 @@ +d-i debian-installer/locale string en_US +d-i console-setup/ask_detect boolean false +d-i console-setup/layoutcode string us + +d-i netcfg/choose_interface select auto +d-i netcfg/get_hostname string unassigned-hostname +d-i netcfg/get_domain string unassigned-domain +d-i netcfg/wireless_wep string + +d-i clock-setup/utc boolean true +d-i time/zone string US/Eastern + +d-i partman-auto/method string regular +d-i partman-auto/choose_recipe select home +d-i partman/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 passwd/root-login boolean true +d-i passwd/make-user boolean false +d-i passwd/root-password password %ROOTPW% +d-i passwd/root-password-again password %ROOTPW% + +tasksel tasksel/first multiselect standard +d-i pkgsel/include/install-recommends boolean true +d-i pkgsel/include string openssh-server + +d-i grub-installer/only_debian boolean true +d-i grub-installer/with_other_os boolean true + +d-i apt-setup/security_host string +base-config apt-setup/security-updates boolean false + +ubiquity ubiquity/summary note +ubiquity ubiquity/reboot boolean true + +d-i finish-install/reboot_in_progress note + +# In Debian/Ubuntu, ssh keys are generated at package install time. Because +# the disk image may be cached, we need to remove the ssh keys, but this means +# that ssh'ing into the server won't work later. So we remove the keys, but +# setup a service that will generate the keys on boot if necessary. +d-i preseed/late_command string \ + in-target rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key.pub ; \ + echo '#! /bin/sh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### BEGIN INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Provides: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Start: $local_fs $network' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Required-Stop: $local_fs' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Start: 2 3 4 5' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Default-Stop: 0 1 6' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Short-Description: oz-generate-ssh' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Description: regenerate ssh keys on first boot' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '### END INIT INFO' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Debian/Ubuntu generate ssh host keys at package installation time.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# This is problematic for Oz, since the final disk image may be cached' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# and reused, leading to duplicate host keys. To work around this, Oz' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# deletes the SSH host keys at the end of installation. This solves' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# the above problem, but introduces the problem of having no way to' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# SSH into the machine without manual intervention. This service checks' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# to see if host keys are already installed, and if not, recreates them.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '# Note that after the very first boot, this service could be removed.' >> /target/etc/init.d/oz-generate-ssh ; \ + echo '#' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'case "$1" in' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' start)' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' [ -r /etc/ssh/ssh_host_rsa_key ] || /usr/sbin/dpkg-reconfigure openssh-server' >> /target/etc/init.d/oz-generate-ssh ; \ + echo ' ;;' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'esac' >> /target/etc/init.d/oz-generate-ssh ; \ + echo 'exit 0' >> /target/etc/init.d/oz-generate-ssh ; \ + in-target chmod 755 /etc/init.d/oz-generate-ssh ; \ + in-target ln -s /etc/init.d/oz-generate-ssh /etc/rc2.d/S40oz-generate-ssh ; \ + echo "GRUB_TERMINAL=console" >> /target/etc/default/grub ; \ + in-target /usr/sbin/update-grub diff -Nru oz-0.15.0/oz/auto/Windows10.auto oz-0.16.0/oz/auto/Windows10.auto --- oz-0.15.0/oz/auto/Windows10.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Windows10.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + OnError + + + + 1 + 1 + Primary + + + 0 + true + + + true + true + NTFS + + C + 1 + 1 + + + + + + + + + /IMAGE/INDEX + 1 + + + + 0 + 1 + + OnError + + + + true + + + + + en-US + + en-US + en-US + en-US + + + + + + + %ROOTPW% + true</PlainText> + </AdministratorPassword> + </UserAccounts> + <AutoLogon> + <Password> + <Value>%ROOTPW%</Value> + <PlainText>true</PlainText> + </Password> + <Enabled>true</Enabled> + <LogonCount>5</LogonCount> + <Username>Administrator</Username> + </AutoLogon> + <OOBE> + <NetworkLocation>Work</NetworkLocation> + <HideEULAPage>true</HideEULAPage> + <ProtectYourPC>3</ProtectYourPC> + <SkipMachineOOBE>true</SkipMachineOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + </OOBE> + <FirstLogonCommands> + <SynchronousCommand wcm:action="add"> + <Order>1</Order> + <Description>Turn Off Network Selection pop-up</Description> + <CommandLine>cmd /c reg add "HKLM\SYSTEM\CurrentControlSet\Control\Network\NewNetworkWindowOff"</CommandLine> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>2</Order> + <Description>Shutting down Windows</Description> + <CommandLine>cmd /C shutdown /s /t 0</CommandLine> + </SynchronousCommand> + </FirstLogonCommands> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <ProductKey></ProductKey> + </component> + </settings> +</unattend> diff -Nru oz-0.15.0/oz/auto/Windows2016.auto oz-0.16.0/oz/auto/Windows2016.auto --- oz-0.15.0/oz/auto/Windows2016.auto 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/auto/Windows2016.auto 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="utf-8"?> +<unattend xmlns="urn:schemas-microsoft-com:unattend"> + <settings pass="windowsPE"> + <component name="Microsoft-Windows-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <DiskConfiguration> + <WillShowUI>OnError</WillShowUI> + <Disk> + <CreatePartitions> + <CreatePartition> + <Order>1</Order> + <Size>1</Size> + <Type>Primary</Type> + </CreatePartition> + </CreatePartitions> + <DiskID>0</DiskID> + <WillWipeDisk>true</WillWipeDisk> + <ModifyPartitions> + <ModifyPartition> + <Active>true</Active> + <Extend>true</Extend> + <Format>NTFS</Format> + <Label>C drive</Label> + <Letter>C</Letter> + <Order>1</Order> + <PartitionID>1</PartitionID> + </ModifyPartition> + </ModifyPartitions> + </Disk> + </DiskConfiguration> + <ImageInstall> + <OSImage> + <InstallFrom> + <MetaData wcm:action="add"> + <Key>/IMAGE/INDEX</Key> + <Value>1</Value> + </MetaData> + </InstallFrom> + <InstallTo> + <DiskID>0</DiskID> + <PartitionID>1</PartitionID> + </InstallTo> + <WillShowUI>OnError</WillShowUI> + </OSImage> + </ImageInstall> + <UserData> + <AcceptEula>true</AcceptEula> + </UserData> + </component> + <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <SetupUILanguage> + <UILanguage>en-US</UILanguage> + </SetupUILanguage> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UserLocale>en-US</UserLocale> + </component> + </settings> + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <UserAccounts> + <AdministratorPassword> + <Value></Value> + <PlainText>true</PlainText> + </AdministratorPassword> + </UserAccounts> + <AutoLogon> + <Password> + <Value></Value> + <PlainText>true</PlainText> + </Password> + <Enabled>true</Enabled> + <LogonCount>5</LogonCount> + <Username>Administrator</Username> + </AutoLogon> + <RegisteredOwner/> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <ProtectYourPC>3</ProtectYourPC> + </OOBE> + <FirstLogonCommands> + <SynchronousCommand wcm:action="add"> + <Order>1</Order> + <Description>Shutting down Windows</Description> + <CommandLine>cmd /C shutdown /s /t 0</CommandLine> + </SynchronousCommand> + </FirstLogonCommands> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <ProductKey></ProductKey> + </component> + </settings> +</unattend> diff -Nru oz-0.15.0/oz/Debian.py oz-0.16.0/oz/Debian.py --- oz-0.15.0/oz/Debian.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Debian.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2011 Chris Lalancette <clalance@redhat.com> -# Copyright (C) 2012-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2012-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -24,6 +24,7 @@ import re import shutil +import oz.GuestFSManager import oz.Linux import oz.OzException import oz.ozutil @@ -191,7 +192,7 @@ self.log.debug("Teardown step 1") # reset the authorized keys self.log.debug("Resetting authorized_keys") - self._guestfs_path_restore(g_handle, '/root/.ssh') + g_handle.path_restore('/root/.ssh') def _image_ssh_teardown_step_2(self, g_handle): """ @@ -200,12 +201,12 @@ self.log.debug("Teardown step 2") # remove custom sshd_config self.log.debug("Resetting sshd_config") - self._guestfs_path_restore(g_handle, '/etc/ssh/sshd_config') + g_handle.path_restore('/etc/ssh/sshd_config') # reset the service link self.log.debug("Resetting sshd service") if self.ssh_startuplink: - self._guestfs_path_restore(g_handle, self.ssh_startuplink) + g_handle.path_restore(self.ssh_startuplink) def _image_ssh_teardown_step_3(self, g_handle): """ @@ -214,16 +215,16 @@ self.log.debug("Teardown step 3") # remove announce cronjob self.log.debug("Resetting announcement to host") - self._guestfs_remove_if_exists(g_handle, '/etc/cron.d/announce') + g_handle.remove_if_exists('/etc/cron.d/announce') # remove reportip self.log.debug("Removing reportip") - self._guestfs_remove_if_exists(g_handle, '/root/reportip') + g_handle.remove_if_exists('/root/reportip') # reset the service link self.log.debug("Resetting cron service") if self.cron_startuplink: - self._guestfs_path_restore(g_handle, self.cron_startuplink) + g_handle.path_restore(self.cron_startuplink) def _image_ssh_teardown_step_4(self, g_handle): """ @@ -236,7 +237,7 @@ "/etc/ssh/ssh_host_rsa_key", "/etc/ssh/ssh_host_rsa_key.pub", "/etc/ssh/ssh_host_ecdsa_key", "/etc/ssh/ssh_host_ecdsa_key.pub", "/etc/ssh/ssh_host_key", "/etc/ssh/ssh_host_key.pub"]: - self._guestfs_remove_if_exists(g_handle, f) + g_handle.remove_if_exists(f) def _image_ssh_setup_step_1(self, g_handle): """ @@ -244,10 +245,10 @@ """ # part 1; upload the keys self.log.debug("Step 1: Uploading ssh keys") - self._guestfs_path_backup(g_handle, '/root/.ssh') + g_handle.path_backup('/root/.ssh') g_handle.mkdir('/root/.ssh') - self._guestfs_path_backup(g_handle, '/root/.ssh/authorized_keys') + g_handle.path_backup('/root/.ssh/authorized_keys') self._generate_openssh_key(self.sshprivkey) @@ -263,7 +264,7 @@ raise oz.OzException.OzException("ssh not installed on the image, cannot continue") self.ssh_startuplink = self._get_service_runlevel_link(g_handle, 'ssh') - self._guestfs_path_backup(g_handle, self.ssh_startuplink) + g_handle.path_backup(self.ssh_startuplink) g_handle.ln_sf('/etc/init.d/ssh', self.ssh_startuplink) sshd_config_file = os.path.join(self.icicle_tmp, "sshd_config") @@ -272,14 +273,14 @@ f.close() try: - self._guestfs_path_backup(g_handle, '/etc/ssh/sshd_config') + g_handle.path_backup('/etc/ssh/sshd_config') g_handle.upload(sshd_config_file, '/etc/ssh/sshd_config') finally: os.unlink(sshd_config_file) def _image_ssh_setup_step_3(self, g_handle): """ - Fourth step for allowing remote access (make the guest announce itself + Third step for allowing remote access (make the guest announce itself on bootup). """ # part 3; make sure the guest announces itself @@ -314,7 +315,7 @@ self.cron_startuplink = self._get_service_runlevel_link(g_handle, 'cron') - self._guestfs_path_backup(g_handle, self.cron_startuplink) + g_handle.path_backup(self.cron_startuplink) g_handle.ln_sf('/etc/init.d/cron', self.cron_startuplink) def _collect_setup(self, libvirt_xml): @@ -323,7 +324,8 @@ """ self.log.info("Collection Setup") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() # we have to do 3 things to make sure we can ssh into Debian # 1) Upload our ssh key @@ -351,7 +353,7 @@ raise finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() def _collect_teardown(self, libvirt_xml): """ @@ -359,7 +361,8 @@ """ self.log.info("Collection Teardown") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() try: self._image_ssh_teardown_step_1(g_handle) @@ -370,95 +373,9 @@ self._image_ssh_teardown_step_4(g_handle) finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() shutil.rmtree(self.icicle_tmp) - def _internal_customize(self, libvirt_xml, action): - """ - Internal method to customize and optionally generate an ICICLE for the - operating system after initial installation. - """ - # the "action" input is actually a tri-state: - # action = "gen_and_mod" means to generate the icicle and to - # potentially make modifications - # action = "gen_only" means to generate the icicle only, and not - # look at any modifications - # action = "mod_only" means to not generate the icicle, but still - # potentially make modifications - - self.log.info("Customizing image") - - if not self.tdl.packages and not self.tdl.files and not self.tdl.commands: - if action == "mod_only": - self.log.info("No additional packages, files, or commands to install, and icicle generation not requested, skipping customization") - return - elif action == "gen_and_mod": - # It is actually possible to get here with a "gen_and_mod" - # action but a TDL that contains no real customizations. - # In the "safe ICICLE" code below it is important to know - # when we are truly in a "gen_only" state so we modify - # the action here if we detect that ICICLE generation is the - # only task to be done. - # FIXME: See about doing this test earlier or in a more generic - # way - self.log.debug("Asked to gen_and_mod but no mods are present - changing action to gen_only") - action = "gen_only" - - # when doing an oz-install with -g, this isn't necessary as it will - # just replace the port with the same port. However, it is very - # necessary when doing an oz-customize since the serial port might - # not match what is specified in the libvirt XML - modified_xml = self._modify_libvirt_xml_for_serial(libvirt_xml) - - if action == "gen_only" and self.safe_icicle_gen: - # We are only generating ICICLE and the user has asked us to do - # this without modifying the completed image by booting it. - # Create a copy on write snapshot to use for ICICLE - # generation - discard when finished - cow_diskimage = self.diskimage + "-icicle-snap.qcow2" - self._internal_generate_diskimage(force=True, - backing_filename=self.diskimage, - image_filename=cow_diskimage) - modified_xml = self._modify_libvirt_xml_diskimage(modified_xml, cow_diskimage, 'qcow2') - - self._collect_setup(modified_xml) - - icicle = None - try: - libvirt_dom = self.libvirt_conn.createXML(modified_xml, 0) - - try: - guestaddr = None - guestaddr = self._wait_for_guest_boot(libvirt_dom) - self._test_ssh_connection(guestaddr) - - if action == "gen_and_mod": - self.do_customize(guestaddr) - icicle = self.do_icicle(guestaddr) - elif action == "gen_only": - icicle = self.do_icicle(guestaddr) - elif action == "mod_only": - self.do_customize(guestaddr) - else: - raise oz.OzException.OzException("Invalid customize action %s; this is a programming error" % (action)) - finally: - if action == "gen_only" and self.safe_icicle_gen: - # if this is a gen_only and safe_icicle_gen, there is no - # reason to wait around for the guest to shutdown; we'll - # be removing the overlay file anyway. Just destroy it - libvirt_dom.destroy() - else: - self._shutdown_guest(guestaddr, libvirt_dom) - finally: - if action == "gen_only" and self.safe_icicle_gen: - # no need to teardown because we simply discard the file - # containing those changes - os.unlink(cow_diskimage) - else: - self._collect_teardown(modified_xml) - - return icicle - def _discover_repo_locality(self, repo_url, guestaddr, certdict): """ Internal method to discover whether a repository is reachable from the @@ -478,34 +395,9 @@ self.guest_execute_command(guestaddr, "echo '%s' > /etc/apt/sources.list.d/%s" % (repo.url.strip('\'"'), repo.name + "list")) self.guest_execute_command(guestaddr, "apt-get update") - def do_customize(self, guestaddr): - """ - Method to customize by installing additional packages and files. - """ - if not self.tdl.packages and not self.tdl.files and not self.tdl.commands: - # no work to do, just return - return - - self._customize_repos(guestaddr) - - self.log.debug("Installing custom packages") - packstr = '' - for package in self.tdl.packages: - packstr += package.name + ' ' - - if packstr != '': - self.guest_execute_command(guestaddr, - 'apt-get install -y %s' % (packstr), - tunnels=None) - - self._customize_files(guestaddr) - - self.log.debug("Running custom commands") - for cmd in self.tdl.commands: - self.guest_execute_command(guestaddr, cmd.read()) - - self.log.debug("Syncing") - self.guest_execute_command(guestaddr, 'sync') + def _install_packages(self, guestaddr, packstr): + self.guest_execute_command(guestaddr, + 'apt-get install -y %s' % (packstr)) def do_icicle(self, guestaddr): """ @@ -529,14 +421,6 @@ return self._output_icicle_xml(packages, self.tdl.description) - def guest_live_upload(self, guestaddr, file_to_upload, destination, - timeout=10): - """ - Method to copy a file to the live guest. - """ - return oz.ozutil.scp_copy_file(guestaddr, self.sshprivkey, - file_to_upload, destination, timeout) - def _get_kernel_from_txt_cfg(self, fetchurl): """ Internal method to download and parse the txt.cfg file from a URL. If @@ -552,7 +436,7 @@ raise oz.OzException.OzException("Could not find %s" % (txtcfgurl)) txtcfg = os.path.join(self.icicle_tmp, "txt.cfg") - self.log.debug("Going to write txt.cfg to %s" % (txtcfg)) + self.log.debug("Going to write txt.cfg to %s", txtcfg) txtcfgfd = os.open(txtcfg, os.O_RDWR | os.O_CREAT | os.O_TRUNC) os.unlink(txtcfg) fp = os.fdopen(txtcfgfd) @@ -580,7 +464,7 @@ if kernel is None or initrd is None: raise oz.OzException.OzException("Empty kernel or initrd") - self.log.debug("Returning kernel %s and initrd %s" % (kernel, initrd)) + self.log.debug("Returning kernel %s and initrd %s", kernel, initrd) return (kernel, initrd) def _gzip_file(self, inputfile, outputmode): @@ -606,7 +490,7 @@ Internal method to create a modified CPIO initrd """ extrafname = os.path.join(self.icicle_tmp, "extra.cpio") - self.log.debug("Writing cpio to %s" % (extrafname)) + self.log.debug("Writing cpio to %s", extrafname) cpiofiledict = {} cpiofiledict[preseedpath] = 'preseed.cfg' oz.ozutil.write_cpio(cpiofiledict, extrafname) @@ -631,30 +515,40 @@ pass if kernel is None: - self.log.debug("Kernel was None, trying debian-installer/%s/linux" % (self.debarch)) + self.log.debug("Kernel was None, trying debian-installer/%s/linux", self.debarch) # we couldn't find the kernel in the txt.cfg, so try a # hard-coded path kernel = "debian-installer/%s/linux" % (self.debarch) if initrd is None: - self.log.debug("Initrd was None, trying debian-installer/%s/initrd.gz" % (self.debarch)) + self.log.debug("Initrd was None, trying debian-installer/%s/initrd.gz", self.debarch) # we couldn't find the initrd in the txt.cfg, so try a # hard-coded path initrd = "debian-installer/%s/initrd.gz" % (self.debarch) - self._get_original_media('/'.join([self.url.rstrip('/'), - kernel.lstrip('/')]), - self.kernelcache, force_download) + (fd, outdir) = oz.ozutil.open_locked_file(self.kernelcache) try: self._get_original_media('/'.join([self.url.rstrip('/'), - initrd.lstrip('/')]), - self.initrdcache, force_download) - except: - os.unlink(self.kernelfname) - raise + kernel.lstrip('/')]), + fd, outdir, force_download) - # if we made it here, then we can copy the kernel into place - shutil.copyfile(self.kernelcache, self.kernelfname) + # if we made it here, then we can copy the kernel into place + shutil.copyfile(self.kernelcache, self.kernelfname) + finally: + os.close(fd) + + (fd, outdir) = oz.ozutil.open_locked_file(self.initrdcache) + + try: + try: + self._get_original_media('/'.join([self.url.rstrip('/'), + initrd.lstrip('/')]), + fd, outdir, force_download) + except: + os.unlink(self.kernelfname) + raise + finally: + os.close(fd) try: preseedpath = os.path.join(self.icicle_tmp, "preseed.cfg") diff -Nru oz-0.15.0/oz/Fedora.py oz-0.16.0/oz/Fedora.py --- oz-0.15.0/oz/Fedora.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Fedora.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2010,2011 Chris Lalancette <clalance@redhat.com> -# Copyright (C) 2012-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2012-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -27,7 +27,7 @@ class FedoraGuest(oz.RedHat.RedHatLinuxCDYumGuest): """ - Class for Fedora 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, and 23 installation. + Class for Fedora 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, and 26 installation. """ def __init__(self, tdl, config, auto, nicmodel, haverepo, diskbus, brokenisomethod, output_disk=None, macaddress=None, @@ -37,16 +37,24 @@ directkernel = None self.assumed_update = assumed_update + # Prior to Fedora-22, we use yum; on F-22 and later, we use dnf. + # Express that preference here. + use_yum = True + if tdl.update in ["22", "23", "24", "25", "26"]: + use_yum = False oz.RedHat.RedHatLinuxCDYumGuest.__init__(self, tdl, config, auto, output_disk, nicmodel, diskbus, True, True, directkernel, - macaddress) + macaddress, use_yum) if self.assumed_update is not None: self.log.warning("==== WARN: TDL contains Fedora update %s, which is newer than Oz knows about; pretending this is Fedora %s, but this may fail ====", tdl.update, assumed_update) self.haverepo = haverepo self.brokenisomethod = brokenisomethod + if self.tdl.update in ["14", "15", "16", "17", "18", "19", "20", "21", + "22", "23", "24", "25", "26"]: + self.virtio_channel_name = 'org.fedoraproject.anaconda.log.0' def _modify_iso(self): """ @@ -54,7 +62,7 @@ """ self._copy_kickstart(os.path.join(self.iso_contents, "ks.cfg")) - if self.tdl.update in ["17", "18", "19", "20", "21", "22", "23"]: + if self.tdl.update in ["17", "18", "19", "20", "21", "22", "23", "24", "25", "26"]: initrdline = " append initrd=initrd.img ks=cdrom:/dev/cdrom:/ks.cfg" else: initrdline = " append initrd=initrd.img ks=cdrom:/ks.cfg" @@ -113,7 +121,7 @@ # The newer_distros list is actually a list of distros that have similar # installation requirements. Thus, even if the particular distro isn't # supported anymore, it should not be removed from the list. - newer_distros = ["19", "20", "21", "22", "23"] + newer_distros = ["19", "20", "21", "22", "23", "24", "25", "26"] if tdl.update == 'rawhide' or int(tdl.update) > int(newer_distros[-1]): if netdev is None: @@ -131,7 +139,7 @@ return FedoraGuest(tdl, config, auto, netdev, True, diskbus, False, output_disk, macaddress, None) - if tdl.update in ["9", "10", "11", "12", "13", "14", "15", "16", "17", "18"]: + if tdl.update in ["10", "11", "12", "13", "14", "15", "16", "17", "18"]: if netdev is None: netdev = 'virtio' if diskbus is None: @@ -139,6 +147,14 @@ return FedoraGuest(tdl, config, auto, netdev, True, diskbus, True, output_disk, macaddress, None) + if tdl.update in ["9"]: + if netdev is None: + netdev = 'virtio' + if diskbus is None: + diskbus = 'virtio' + return FedoraGuest(tdl, config, auto, netdev, True, diskbus, False, + output_disk, macaddress, None) + if tdl.update in ["7", "8"]: return FedoraGuest(tdl, config, auto, netdev, False, diskbus, False, output_disk, macaddress, None) @@ -147,4 +163,4 @@ """ Return supported versions as a string. """ - return "Fedora: 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23" + return "Fedora: 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26" diff -Nru oz-0.15.0/oz/FreeBSD.py oz-0.16.0/oz/FreeBSD.py --- oz-0.15.0/oz/FreeBSD.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/FreeBSD.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2013 harmw <harm@weites.com> -# Copyright (C) 2013-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2013-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -27,7 +27,7 @@ class FreeBSD(oz.Guest.CDGuest): """ - Class for FreeBSD 10.0 installation. + Class for FreeBSD 10.0, 10.1, 10.2, 10.3 and 11.0 installation. """ def __init__(self, tdl, config, auto, output_disk, netdev, diskbus, macaddress): @@ -88,7 +88,7 @@ """ Factory method for FreeBSD installs. """ - if tdl.update in ["10.0"]: + if tdl.update in ["10.0", "10.1", "10.2", "10.3", "11.0",]: if netdev is None: netdev = 'virtio' if diskbus is None: @@ -100,4 +100,4 @@ """ Return supported versions as a string. """ - return "FreeBSD: 10" + return "FreeBSD: 10, 11" diff -Nru oz-0.15.0/oz/GuestFactory.py oz-0.16.0/oz/GuestFactory.py --- oz-0.15.0/oz/GuestFactory.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/GuestFactory.py 2017-08-08 18:46:43.000000000 +0000 @@ -21,50 +21,50 @@ import oz.OzException -os_dict = { 'Fedora': 'Fedora', - 'FedoraCore': 'FedoraCore', - 'FC': 'FedoraCore', - 'RedHatEnterpriseLinux-2.1': 'RHEL_2_1', - 'RHEL-2.1': 'RHEL_2_1', - 'RedHatEnterpriseLinux-3': 'RHEL_3', - 'RHEL-3': 'RHEL_3', - 'CentOS-3': 'RHEL_3', - 'RedHatEnterpriseLinux-4': 'RHEL_4', - 'RHEL-4': 'RHEL_4', - 'CentOS-4': 'RHEL_4', - 'ScientificLinux-4': 'RHEL_4', - 'SL-4': 'RHEL_4', - 'RedHatEnterpriseLinux-5': 'RHEL_5', - 'RHEL-5': 'RHEL_5', - 'CentOS-5': 'RHEL_5', - 'OL-5': 'RHEL_5', - 'ScientificLinux-5': 'RHEL_5', - 'SL-5': 'RHEL_5', - 'ScientificLinuxCern-5': 'RHEL_5', - 'SLC-5': 'RHEL_5', - 'RedHatEnterpriseLinux-6': 'RHEL_6', - 'RHEL-6': 'RHEL_6', - 'CentOS-6': 'RHEL_6', - 'ScientificLinux-6': 'RHEL_6', - 'SL-6': 'RHEL_6', - 'ScientificLinuxCern-6': 'RHEL_6', - 'SLC-6': 'RHEL_6', - 'OracleEnterpriseLinux-6': 'RHEL_6', - 'OEL-6': 'RHEL_6', - 'OL-6': 'RHEL_6', - 'RHEL-7': 'RHEL_7', - 'CentOS-7': 'RHEL_7', - 'Ubuntu': 'Ubuntu', - 'Windows': 'Windows', - 'RedHatLinux': 'RHL', - 'RHL': 'RHL', - 'OpenSUSE': 'OpenSUSE', - 'Debian': 'Debian', - 'Mandrake': 'Mandrake', - 'Mandriva': 'Mandriva', - 'Mageia': 'Mageia', - 'FreeBSD': 'FreeBSD', -} +os_dict = {'Fedora': 'Fedora', + 'FedoraCore': 'FedoraCore', + 'FC': 'FedoraCore', + 'RedHatEnterpriseLinux-2.1': 'RHEL_2_1', + 'RHEL-2.1': 'RHEL_2_1', + 'RedHatEnterpriseLinux-3': 'RHEL_3', + 'RHEL-3': 'RHEL_3', + 'CentOS-3': 'RHEL_3', + 'RedHatEnterpriseLinux-4': 'RHEL_4', + 'RHEL-4': 'RHEL_4', + 'CentOS-4': 'RHEL_4', + 'ScientificLinux-4': 'RHEL_4', + 'SL-4': 'RHEL_4', + 'RedHatEnterpriseLinux-5': 'RHEL_5', + 'RHEL-5': 'RHEL_5', + 'CentOS-5': 'RHEL_5', + 'OL-5': 'RHEL_5', + 'ScientificLinux-5': 'RHEL_5', + 'SL-5': 'RHEL_5', + 'ScientificLinuxCern-5': 'RHEL_5', + 'SLC-5': 'RHEL_5', + 'RedHatEnterpriseLinux-6': 'RHEL_6', + 'RHEL-6': 'RHEL_6', + 'CentOS-6': 'RHEL_6', + 'ScientificLinux-6': 'RHEL_6', + 'SL-6': 'RHEL_6', + 'ScientificLinuxCern-6': 'RHEL_6', + 'SLC-6': 'RHEL_6', + 'OracleEnterpriseLinux-6': 'RHEL_6', + 'OEL-6': 'RHEL_6', + 'OL-6': 'RHEL_6', + 'RHEL-7': 'RHEL_7', + 'CentOS-7': 'RHEL_7', + 'Ubuntu': 'Ubuntu', + 'Windows': 'Windows', + 'RedHatLinux': 'RHL', + 'RHL': 'RHL', + 'OpenSUSE': 'OpenSUSE', + 'Debian': 'Debian', + 'Mandrake': 'Mandrake', + 'Mandriva': 'Mandriva', + 'Mageia': 'Mageia', + 'FreeBSD': 'FreeBSD', + } def guest_factory(tdl, config, auto, output_disk=None, netdev=None, diskbus=None, macaddress=None): diff -Nru oz-0.15.0/oz/GuestFSManager.py oz-0.16.0/oz/GuestFSManager.py --- oz-0.15.0/oz/GuestFSManager.py 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz/GuestFSManager.py 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,257 @@ +# Copyright (C) 2017 Chris Lalancette <clalancette@gmail.com> + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +""" +Helper class for managing a GuestFS connection +""" + +import logging +import lxml.etree +import guestfs + +import oz.OzException + +class GuestFS(object): + ''' + A wrapper class over guestfs to make some common operations easier. + ''' + def __init__(self, input_disk, input_disk_type): + self.log = logging.getLogger(__name__) + + self.g_handle = guestfs.GuestFS(python_return_dict=True) + + self.log.debug("Adding disk image %s", input_disk) + # NOTE: we use "add_drive_opts" here so we can specify the type + # of the diskimage. Otherwise it might be possible for an attacker + # to fool libguestfs with a specially-crafted diskimage that looks + # like a qcow2 disk (thanks to rjones for the tip) + self.g_handle.add_drive_opts(input_disk, format=input_disk_type) + + self.log.debug("Launching guestfs") + self.g_handle.launch() + + def create_msdos_partition_table(self): + ''' + A method to create a new msdos partition table on a disk. + ''' + devices = self.g_handle.list_devices() + self.g_handle.part_init(devices[0], "msdos") + self.g_handle.part_add(devices[0], 'p', 1, 2) + + def mount_partitions(self): + ''' + A method to mount existing partitions on a disk inside of guestfs. + ''' + self.log.debug("Inspecting guest OS") + roots = self.g_handle.inspect_os() + + if len(roots) == 0: + raise oz.OzException.OzException("No operating systems found on the disk") + + self.log.debug("Getting mountpoints") + already_mounted = {} + for root in roots: + self.log.debug("Root device: %s", root) + + # the problem here is that the list of mountpoints returned by + # inspect_get_mountpoints is in no particular order. So if the + # diskimage contains /usr and /usr/local on different devices, + # but /usr/local happened to come first in the listing, the + # devices would get mapped improperly. The clever solution here is + # to sort the mount paths by length; this will ensure that they + # are mounted in the right order. Thanks to rjones for the hint, + # and the example code that comes from the libguestfs.org python + # example page. + mps = self.g_handle.inspect_get_mountpoints(root) + def _compare(a, b): + """ + Method to sort disks by length. + """ + return len(a) - len(b) + for device in sorted(mps.keys(), _compare): + try: + # Here we check to see if the device was already mounted. + # If it was, we skip over this mountpoint and go to the + # next one. This can happen, for instance, on btrfs volumes + # with snapshots. In that case, we'll always take the + # "original" backing filesystem, and not the snapshots, + # which seems to work in practice. + if already_mounted[device] == mps[device]: + continue + except KeyError: + # If we got a KeyError exception, we know that we haven't + # yet mounted this filesystem, so continue on. + pass + + try: + self.g_handle.mount_options('', mps[device], device) + already_mounted[device] = mps[device] + except: + if device == '/': + # If we cannot mount root, we may as well give up + raise + else: + # some custom guests may have fstab content with + # "nofail" as a mount option. For example, images + # built for EC2 with ephemeral mappings. These + # fail at this point. Allow things to continue. + # Profound failures will trigger later on during + # the process. + self.log.warning("Unable to mount (%s) on (%s) - trying to continue", mps[device], device) + + def remove_if_exists(self, path): + """ + Method to remove a file if it exists in the disk image. + """ + if self.g_handle.exists(path): + self.g_handle.rm_rf(path) + + def move_if_exists(self, orig_path, replace_path): + """ + Method to move a file if it exists in the disk image. + """ + if self.g_handle.exists(orig_path): + self.g_handle.mv(orig_path, replace_path) + + def path_backup(self, orig): + """ + Method to backup a file in the disk image. + """ + self.move_if_exists(orig, orig + ".ozbackup") + + def path_restore(self, orig): + """ + Method to restore a backup file in the disk image. + """ + backup = orig + ".ozbackup" + self.remove_if_exists(orig) + self.move_if_exists(backup, orig) + + def exists(self, filename): + ''' + A passthrough method for the guestfs functionality of "exists". + ''' + return self.g_handle.exists(filename) + + def rm(self, filename): + ''' + A passthrough method for the guestfs functionality of "rm". + ''' + return self.g_handle.rm(filename) + + def glob_expand(self, glob): + ''' + A passthrough method for the guestfs functionality of "glob_expand". + ''' + return self.g_handle.glob_expand(glob) + + def mkdir(self, directory): + ''' + A passthrough method for the guestfs functionality of "mkdir". + ''' + return self.g_handle.mkdir(directory) + + def ln_sf(self, src, dst): + ''' + A passthrough method for the guestfs functionality of "ln_sf". + ''' + return self.g_handle.ln_sf(src, dst) + + def chmod(self, mode, fname): + ''' + A passthrough method for the guestfs functionality of "chmod". + ''' + return self.g_handle.chmod(mode, fname) + + def cat(self, fname): + ''' + A passthrough method for the guestfs functionality of "cat". + ''' + return self.g_handle.cat(fname) + + def upload(self, src, dest): + ''' + A passthrough method for the guestfs functionalityh of "upload". + ''' + return self.g_handle.upload(src, dest) + + def cleanup(self): + ''' + A method to cleanup after finishing with a guestfs handle. + ''' + self.log.info("Cleaning up guestfs handle") + self.log.debug("Syncing") + self.g_handle.sync() + + self.log.debug("Unmounting all") + self.g_handle.umount_all() + self.g_handle.kill_subprocess() + self.g_handle.close() + +def GuestFSLibvirtFactory(libvirt_xml, libvirt_conn): + ''' + A factory function for getting a GuestFS object from a libvirt XML and a connection. + ''' + log = logging.getLogger(__name__) + + input_doc = lxml.etree.fromstring(libvirt_xml) + namenode = input_doc.xpath('/domain/name') + if len(namenode) != 1: + raise oz.OzException.OzException("invalid libvirt XML with no name") + input_name = namenode[0].text + disks = input_doc.xpath('/domain/devices/disk') + if len(disks) != 1: + log.warning("Oz given a libvirt domain with more than 1 disk; using the first one parsed") + source = disks[0].xpath('source') + if len(source) != 1: + raise oz.OzException.OzException("invalid <disk> entry without a source") + input_disk = source[0].get('file') + driver = disks[0].xpath('driver') + if len(driver) == 0: + input_disk_type = 'raw' + elif len(driver) == 1: + input_disk_type = driver[0].get('type') + else: + raise oz.OzException.OzException("invalid <disk> entry without a driver") + + for domid in libvirt_conn.listDomainsID(): + try: + doc = lxml.etree.fromstring(libvirt_conn.lookupByID(domid).XMLDesc(0)) + except: + log.debug("Could not get XML for domain ID (%s) - it may have disappeared (continuing)", + domid) + continue + + namenode = doc.xpath('/domain/name') + if len(namenode) != 1: + # hm, odd, a domain without a name? + raise oz.OzException.OzException("Saw a domain without a name, something weird is going on") + if input_name == namenode[0].text: + raise oz.OzException.OzException("Cannot setup ICICLE generation on a running guest") + disks = doc.xpath('/domain/devices/disk') + if len(disks) < 1: + # odd, a domain without a disk, but don't worry about it + continue + for guestdisk in disks: + for source in guestdisk.xpath("source"): + # FIXME: this will only work for files; we can make it work + # for other things by following something like: + # http://git.annexia.org/?p=libguestfs.git;a=blob;f=src/virt.c;h=2c6be3c6a2392ab8242d1f4cee9c0d1445844385;hb=HEAD#l169 + filename = str(source.get('file')) + if filename == input_disk: + raise oz.OzException.OzException("Cannot setup ICICLE generation on a running disk") + + return GuestFS(input_disk, input_disk_type) diff -Nru oz-0.15.0/oz/Guest.py oz-0.16.0/oz/Guest.py --- oz-0.15.0/oz/Guest.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Guest.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2010,2011 Chris Lalancette <clalance@redhat.com> -# Copyright (C) 2012-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2012-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,9 +20,7 @@ """ import uuid -import libvirt import os -import fcntl import subprocess import shutil import time @@ -31,18 +29,21 @@ except ImportError: import urlparse import stat -import lxml.etree import logging -import guestfs import socket import struct import tempfile -import M2Crypto import base64 import hashlib import errno import re +import guestfs +import libvirt +import lxml.etree +import M2Crypto + +import oz.GuestFSManager import oz.ozutil import oz.OzException @@ -59,7 +60,7 @@ doc = lxml.etree.fromstring(self.libvirt_conn.getCapabilities()) # Libvirt calls the old intel 32-bit architecture i686, while we - # refer to it as i686. Do the mapping here, since we need to look + # refer to it as i386. Do the mapping here, since we need to look # up the libvirt name. libvirtarch = self.tdl.arch if libvirtarch == 'i386': @@ -130,7 +131,7 @@ # for backwards compatibility self.name = self.tdl.name - if not self.tdl.arch in [ "i386", "x86_64", "ppc64", "ppc64le", "aarch64", "armv7l" ]: + if not self.tdl.arch in ["i386", "x86_64", "ppc64", "ppc64le", "aarch64", "armv7l"]: raise oz.OzException.OzException("Unsupported guest arch " + self.tdl.arch) if os.uname()[4] in ["i386", "i586", "i686"] and self.tdl.arch == "x86_64": @@ -173,13 +174,13 @@ 1) # the memory in the configuration file is specified in megabytes, but # libvirt expects kilobytes, so multiply by 1024 - if self.tdl.arch in [ "ppc64", "ppc64le" ]: + if self.tdl.arch in ["ppc64", "ppc64le"]: # ppc64 needs at least 2Gb RAM self.install_memory = int(oz.ozutil.config_get_key(config, 'libvirt', - 'memory', 2048)) * 1024 + 'memory', 2048)) * 1024 else: self.install_memory = int(oz.ozutil.config_get_key(config, 'libvirt', - 'memory', 1024)) * 1024 + 'memory', 1024)) * 1024 self.image_type = oz.ozutil.config_get_key(config, 'libvirt', 'image_type', 'raw') @@ -203,6 +204,16 @@ 'safe_generation', False) + # configuration of 'timeouts' section + self.default_install_timeout = int(oz.ozutil.config_get_key(config, 'timeouts', + 'install', 1200)) + self.inactivity_timeout = int(oz.ozutil.config_get_key(config, 'timeouts', + 'inactivity', 300)) + self.boot_timeout = int(oz.ozutil.config_get_key(config, 'timeouts', + 'boot', 300)) + self.shutdown_timeout = int(oz.ozutil.config_get_key(config, 'timeouts', + 'shutdown', 90)) + # only pull a cached JEOS if it was built with the correct image type jeos_extension = self.image_type if self.image_type == 'raw': @@ -226,12 +237,9 @@ self.icicle_tmp = os.path.join(self.data_dir, "icicletmp", self.tdl.name) - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Bind to port 0 which will use a free socket to listen to. - sock.bind(("", 0)) - self.listen_port = sock.getsockname()[1] - # Close the socket to free it up for libvirt - sock.close() + self.listen_port = oz.ozutil.get_free_port() + + self.console_listen_port = oz.ozutil.get_free_port() self.connect_to_libvirt() @@ -283,6 +291,7 @@ self.log.debug("nicmodel: %s, clockoffset: %s", self.nicmodel, self.clockoffset) self.log.debug("mousetype: %s, disk_bus: %s, disk_dev: %s", self.mousetype, self.disk_bus, self.disk_dev) self.log.debug("icicletmp: %s, listen_port: %d", self.icicle_tmp, self.listen_port) + self.log.debug("console_listen_port: %s", self.console_listen_port) def image_name(self): """ @@ -402,31 +411,24 @@ self.path = path self.bus = bus - def lxml_subelement(self, root, name, text=None, attributes=None): - """ - Method to add a new element to an LXML tree, optionally include text - and a dictionary of attributes. - """ - tmp = lxml.etree.SubElement(root, name) - if text is not None: - tmp.text = text - if attributes is not None: - for k, v in attributes.items(): - tmp.set(k, v) - return tmp - def _generate_serial_xml(self, devices): """ Method to generate the serial portion of the libvirt XML. """ - serial = self.lxml_subelement(devices, "serial", None, {'type':'tcp'}) - self.lxml_subelement(serial, "source", None, - {'mode':'bind', 'host':'127.0.0.1', 'service':str(self.listen_port)}) - self.lxml_subelement(serial, "protocol", None, {'type':'raw'}) - self.lxml_subelement(serial, "target", None, {'port':'1'}) + serial = oz.ozutil.lxml_subelement(devices, "serial", None, {'type':'tcp'}) + oz.ozutil.lxml_subelement(serial, "source", None, + {'mode':'bind', 'host':'127.0.0.1', 'service':str(self.listen_port)}) + oz.ozutil.lxml_subelement(serial, "protocol", None, {'type':'raw'}) + oz.ozutil.lxml_subelement(serial, "target", None, {'port':'1'}) + + def _generate_virtio_channel(self, devices, name): + virtio = oz.ozutil.lxml_subelement(devices, "channel", None, {'type': 'tcp'}) + oz.ozutil.lxml_subelement(virtio, "source", None, + {'mode':'bind', 'host':'127.0.0.1', 'service':str(self.console_listen_port)}) + oz.ozutil.lxml_subelement(virtio, "target", None, {"type": "virtio", "name": name}) def _generate_xml(self, bootdev, installdev, kernel=None, initrd=None, - cmdline=None): + cmdline=None, virtio_channel_name=None): """ Method to generate libvirt XML useful for installation. """ @@ -435,85 +437,95 @@ # top-level domain element domain = lxml.etree.Element("domain", type=self.libvirt_type) # name element - self.lxml_subelement(domain, "name", self.tdl.name) + oz.ozutil.lxml_subelement(domain, "name", self.tdl.name) # memory elements - self.lxml_subelement(domain, "memory", str(self.install_memory)) - self.lxml_subelement(domain, "currentMemory", str(self.install_memory)) + oz.ozutil.lxml_subelement(domain, "memory", str(self.install_memory)) + oz.ozutil.lxml_subelement(domain, "currentMemory", str(self.install_memory)) # uuid - self.lxml_subelement(domain, "uuid", str(self.uuid)) + oz.ozutil.lxml_subelement(domain, "uuid", str(self.uuid)) # clock offset - self.lxml_subelement(domain, "clock", None, {'offset':self.clockoffset}) + oz.ozutil.lxml_subelement(domain, "clock", None, {'offset':self.clockoffset}) # vcpu - self.lxml_subelement(domain, "vcpu", str(self.install_cpus)) + oz.ozutil.lxml_subelement(domain, "vcpu", str(self.install_cpus)) # features - features = self.lxml_subelement(domain, "features") - self.lxml_subelement(features, "acpi") - self.lxml_subelement(features, "apic") - self.lxml_subelement(features, "pae") + features = oz.ozutil.lxml_subelement(domain, "features") + oz.ozutil.lxml_subelement(features, "acpi") + oz.ozutil.lxml_subelement(features, "apic") + oz.ozutil.lxml_subelement(features, "pae") # CPU if self.tdl.arch in ["aarch64", "armv7l"] and self.libvirt_type == "kvm": # Possibly related to BZ 1171501 - need host passthrough for aarch64 and arm with kvm - cpu = self.lxml_subelement(domain, "cpu", None, { 'mode': 'custom', 'match': 'exact' }) - model = self.lxml_subelement(cpu, "model", "host", { 'fallback': 'allow' }) + cpu = oz.ozutil.lxml_subelement(domain, "cpu", None, {'mode': 'custom', 'match': 'exact'}) + model = oz.ozutil.lxml_subelement(cpu, "model", "host", {'fallback': 'allow'}) # os - osNode = self.lxml_subelement(domain, "os") + osNode = oz.ozutil.lxml_subelement(domain, "os") mods = None if self.tdl.arch in ["aarch64", "armv7l"]: - mods = { 'arch': self.tdl.arch, 'machine': 'virt' } - self.lxml_subelement(osNode, "type", "hvm", mods) + mods = {'arch': self.tdl.arch, 'machine': 'virt'} + oz.ozutil.lxml_subelement(osNode, "type", "hvm", mods) if bootdev: - self.lxml_subelement(osNode, "boot", None, {'dev':bootdev}) + oz.ozutil.lxml_subelement(osNode, "boot", None, {'dev':bootdev}) if kernel: - self.lxml_subelement(osNode, "kernel", kernel) + oz.ozutil.lxml_subelement(osNode, "kernel", kernel) if initrd: - self.lxml_subelement(osNode, "initrd", initrd) + oz.ozutil.lxml_subelement(osNode, "initrd", initrd) if cmdline: if self.tdl.arch == "armv7l": cmdline += " console=ttyAMA0" - self.lxml_subelement(osNode, "cmdline", cmdline) + oz.ozutil.lxml_subelement(osNode, "cmdline", cmdline) + if self.tdl.arch == "aarch64": + loader, nvram = oz.ozutil.find_uefi_firmware(self.tdl.arch) + oz.ozutil.lxml_subelement(osNode, "loader", loader, {'readonly': 'yes', 'type': 'pflash'}) + oz.ozutil.lxml_subelement(osNode, "nvram", None, {'template': nvram}) # poweroff, reboot, crash - self.lxml_subelement(domain, "on_poweroff", "destroy") - self.lxml_subelement(domain, "on_reboot", "destroy") - self.lxml_subelement(domain, "on_crash", "destroy") + oz.ozutil.lxml_subelement(domain, "on_poweroff", "destroy") + oz.ozutil.lxml_subelement(domain, "on_reboot", "destroy") + oz.ozutil.lxml_subelement(domain, "on_crash", "destroy") # devices - devices = self.lxml_subelement(domain, "devices") + devices = oz.ozutil.lxml_subelement(domain, "devices") # graphics if not self.tdl.arch in ["aarch64", "armv7l"]: # qemu for arm/aarch64 does not support a graphical console - amazingly - self.lxml_subelement(devices, "graphics", None, {'port':'-1', 'type':'vnc'}) + oz.ozutil.lxml_subelement(devices, "graphics", None, {'port':'-1', 'type':'vnc'}) # network - interface = self.lxml_subelement(devices, "interface", None, {'type':'bridge'}) - self.lxml_subelement(interface, "source", None, {'bridge':self.bridge_name}) - self.lxml_subelement(interface, "mac", None, {'address':self.macaddr}) - self.lxml_subelement(interface, "model", None, {'type':self.nicmodel}) + interface = oz.ozutil.lxml_subelement(devices, "interface", None, {'type':'bridge'}) + oz.ozutil.lxml_subelement(interface, "source", None, {'bridge':self.bridge_name}) + oz.ozutil.lxml_subelement(interface, "mac", None, {'address':self.macaddr}) + oz.ozutil.lxml_subelement(interface, "model", None, {'type':self.nicmodel}) # input mousedict = {'bus':self.mousetype} if self.mousetype == "ps2": mousedict['type'] = 'mouse' elif self.mousetype == "usb": mousedict['type'] = 'tablet' - self.lxml_subelement(devices, "input", None, mousedict) + oz.ozutil.lxml_subelement(devices, "input", None, mousedict) # serial console pseudo TTY - console = self.lxml_subelement(devices, "serial", None, {'type':'pty'}) - self.lxml_subelement(console, "target", None, {'port':'0'}) + console = oz.ozutil.lxml_subelement(devices, "serial", None, {'type':'pty'}) + oz.ozutil.lxml_subelement(console, "target", None, {'port':'0'}) # serial self._generate_serial_xml(devices) + # virtio + if virtio_channel_name is not None: + self._generate_virtio_channel(devices, virtio_channel_name) + self.has_consolelog = True + else: + self.has_consolelog = False # boot disk - bootDisk = self.lxml_subelement(devices, "disk", None, {'device':'disk', 'type':'file'}) - self.lxml_subelement(bootDisk, "target", None, {'dev':self.disk_dev, 'bus':self.disk_bus}) - self.lxml_subelement(bootDisk, "source", None, {'file':self.diskimage}) - self.lxml_subelement(bootDisk, "driver", None, {'name':'qemu', 'type':self.image_type}) + bootDisk = oz.ozutil.lxml_subelement(devices, "disk", None, {'device':'disk', 'type':'file'}) + oz.ozutil.lxml_subelement(bootDisk, "target", None, {'dev':self.disk_dev, 'bus':self.disk_bus}) + oz.ozutil.lxml_subelement(bootDisk, "source", None, {'file':self.diskimage}) + oz.ozutil.lxml_subelement(bootDisk, "driver", None, {'name':'qemu', 'type':self.image_type}) # install disk (if any) if not installdev: installdev_list = [] - elif not type(installdev) is list: + elif not isinstance(installdev, list): installdev_list = [installdev] else: installdev_list = installdev for installdev in installdev_list: - install = self.lxml_subelement(devices, "disk", None, {'type':'file', 'device':installdev.devicetype}) - self.lxml_subelement(install, "source", None, {'file':installdev.path}) - self.lxml_subelement(install, "target", None, {'dev':installdev.bus}) + install = oz.ozutil.lxml_subelement(devices, "disk", None, {'type':'file', 'device':installdev.devicetype}) + oz.ozutil.lxml_subelement(install, "source", None, {'file':installdev.path}) + oz.ozutil.lxml_subelement(install, "target", None, {'dev':installdev.bus}) xml = lxml.etree.tostring(domain, pretty_print=True) self.log.debug("Generated XML:\n%s", xml) @@ -547,26 +559,26 @@ # create the pool XML pool = lxml.etree.Element("pool", type="dir") - self.lxml_subelement(pool, "name", "oztempdir" + str(uuid.uuid4())) - target = self.lxml_subelement(pool, "target") - self.lxml_subelement(target, "path", directory) + oz.ozutil.lxml_subelement(pool, "name", "oztempdir" + str(uuid.uuid4())) + target = oz.ozutil.lxml_subelement(pool, "target") + oz.ozutil.lxml_subelement(target, "path", directory) pool_xml = lxml.etree.tostring(pool, pretty_print=True) # create the volume XML vol = lxml.etree.Element("volume", type="file") - self.lxml_subelement(vol, "name", filename) - self.lxml_subelement(vol, "allocation", "0") - target = self.lxml_subelement(vol, "target") + oz.ozutil.lxml_subelement(vol, "name", filename) + oz.ozutil.lxml_subelement(vol, "allocation", "0") + target = oz.ozutil.lxml_subelement(vol, "target") imgtype = self.image_type if backing_filename: # Only qcow2 supports image creation using a backing file imgtype = "qcow2" - self.lxml_subelement(target, "format", None, {"type":imgtype}) + oz.ozutil.lxml_subelement(target, "format", None, {"type":imgtype}) # FIXME: this makes the permissions insecure, but is needed since # libvirt launches guests as qemu:qemu - permissions = self.lxml_subelement(target, "permissions") - self.lxml_subelement(permissions, "mode", "0666") + permissions = oz.ozutil.lxml_subelement(target, "permissions") + oz.ozutil.lxml_subelement(permissions, "mode", "0666") capacity = size if backing_filename: @@ -582,12 +594,12 @@ else: capacity = os.path.getsize(backing_filename) / 1024 / 1024 / 1024 backing_format = 'raw' - backing = self.lxml_subelement(vol, "backingStore") - self.lxml_subelement(backing, "path", backing_filename) - self.lxml_subelement(backing, "format", None, - {"type":backing_format}) + backing = oz.ozutil.lxml_subelement(vol, "backingStore") + oz.ozutil.lxml_subelement(backing, "path", backing_filename) + oz.ozutil.lxml_subelement(backing, "format", None, + {"type":backing_format}) - self.lxml_subelement(vol, "capacity", str(capacity), {'unit':'G'}) + oz.ozutil.lxml_subelement(vol, "capacity", str(capacity), {'unit':'G'}) vol_xml = lxml.etree.tostring(vol, pretty_print=True) # sigh. Yes, this is racy; if a pool is defined during this loop, we @@ -613,25 +625,30 @@ pool = self.libvirt_conn.storagePoolCreateXML(pool_xml, 0) started = True - # libvirt will not allow us to do certain operations (like a refresh) - # while other operations are happening on a pool (like creating a new - # volume). Since we don't exactly know which other processes might be - # running on the system, we take a system-wide Oz lock to ensure that - # these succeed. In most cases these operations will be fast and thus - # the lock will not be held very long. - lockfile = os.path.join(self.icicle_tmp, "libvirt_pool_lockfile") - (refresh_lock,outdir) = self._open_locked_file(lockfile) - pool.refresh(0) + def _vol_create_cb(args): + """ + The callback used for waiting on volume creation to complete. + """ + pool = args[0] + vol_xml = args[1] + + try: + pool.refresh(0) + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_INTERNAL_ERROR: + # libvirt returns a VIR_ERR_INTERNAL_ERROR when the + # refresh fails. + return False + raise - # this is a bit complicated, because of the cases that can - # happen. The cases are: - # - # 1. The volume did not exist. In this case, storageVolLookupByName() - # throws an exception, which we just ignore. We then go on to - # create the volume - # 2. The volume did exist. In this case, storageVolLookupByName() - # returns a valid volume object, and then we delete the volume - try: + # this is a bit complicated, because of the cases that can + # happen. The cases are: + # + # 1. The volume did not exist. In this case, storageVolLookupByName() + # throws an exception, which we just ignore. We then go on to + # create the volume + # 2. The volume did exist. In this case, storageVolLookupByName() + # returns a valid volume object, and then we delete the volume try: vol = pool.storageVolLookupByName(filename) vol.delete(0) @@ -639,29 +656,28 @@ if e.get_error_code() != libvirt.VIR_ERR_NO_STORAGE_VOL: raise - try: - pool.createXML(vol_xml, 0) - except libvirt.libvirtError as e: - raise + pool.createXML(vol_xml, 0) + + return True + + try: + # libvirt will not allow us to do certain operations (like a refresh) + # while other operations are happening on a pool (like creating a new + # volume). Since we don't exactly know which other processes might be + # running on the system, we retry for a while until our refresh and + # volume creation succeed. In most cases these operations will be fast. + oz.ozutil.timed_loop(90, _vol_create_cb, "Waiting for volume to be created", (pool, vol_xml)) finally: if started: pool.destroy() - # remember to unlock the refresh lock - os.close(refresh_lock) - if create_partition: if backing_filename: self.log.warning("Asked to create partition against a copy-on-write snapshot - ignoring") else: - g_handle = guestfs.GuestFS() - g_handle.add_drive_opts(self.diskimage, format=self.image_type, - readonly=0) - g_handle.launch() - devices = g_handle.list_devices() - g_handle.part_init(devices[0], "msdos") - g_handle.part_add(devices[0], 'p', 1, 2) - g_handle.close() + g_handle = oz.GuestFSManager.GuestFS(self.diskimage, self.image_type) + g_handle.create_msdos_partition_table() + g_handle.cleanup() def generate_diskimage(self, size=10, force=False): """ @@ -675,20 +691,23 @@ """ return self._internal_generate_diskimage(size, force, False) - def _get_disks_and_interfaces(self, libvirt_dom): + def _get_disks_and_interfaces(self, libvirt_xml): """ Method to figure out the disks and interfaces attached to a domain. The method returns two lists: the first is a list of disk devices (like hda, hdb, etc), and the second is a list of network devices (like vnet0, vnet1, etc). """ - doc = lxml.etree.fromstring(libvirt_dom.XMLDesc(0)) + doc = lxml.etree.fromstring(libvirt_xml) disktargets = doc.xpath("/domain/devices/disk/target") + if len(disktargets) < 1: raise oz.OzException.OzException("Could not find disk target") disks = [] for target in disktargets: - disks.append(target.get('dev')) + dev = target.get('dev') + if dev: + disks.append(dev) if not disks: raise oz.OzException.OzException("Could not find disk target device") inttargets = doc.xpath("/domain/devices/interface/target") @@ -696,7 +715,9 @@ raise oz.OzException.OzException("Could not find interface target") interfaces = [] for target in inttargets: - interfaces.append(target.get('dev')) + dev = target.get('dev') + if dev: + interfaces.append(dev) if not interfaces: raise oz.OzException.OzException("Could not find interface target device") @@ -721,48 +742,7 @@ return total_disk_req, total_net_bytes - def _wait_for_clean_shutdown(self, libvirt_dom, saved_exception): - """ - Internal method to wait for a clean shutdown of a libvirt domain that - is suspected to have cleanly quit. If that domain did cleanly quit, - then we will hit a libvirt VIR_ERR_NO_DOMAIN exception on the very - first libvirt call and return with no delay. If no exception, or some - other exception occurs, we wait up to 10 seconds for the domain to go - away. If the domain is still there after 10 seconds then we raise the - original exception that was passed in. - """ - count = 10 - while count > 0: - self.log.debug("Waiting for %s to complete shutdown, %d/10", self.tdl.name, count) - try: - libvirt_dom.info() - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: - break - count -= 1 - time.sleep(1) - - if count == 0: - # Got something other than the expected exception even after 10 - # seconds - re-raise - if saved_exception: - self.log.debug("Libvirt Domain Info Failed:") - self.log.debug(" code is %d", saved_exception.get_error_code()) - self.log.debug(" domain is %d", saved_exception.get_error_domain()) - self.log.debug(" message is %s", saved_exception.get_error_message()) - self.log.debug(" level is %d", saved_exception.get_error_level()) - self.log.debug(" str1 is %s", saved_exception.get_str1()) - self.log.debug(" str2 is %s", saved_exception.get_str2()) - self.log.debug(" str3 is %s", saved_exception.get_str3()) - self.log.debug(" int1 is %d", saved_exception.get_int1()) - self.log.debug(" int2 is %d", saved_exception.get_int2()) - raise saved_exception - else: - # the passed in exception was None, just raise a generic error - raise oz.OzException.OzException("Unknown libvirt error") - - def _wait_for_install_finish(self, libvirt_dom, count, - inactivity_timeout=300): + def _wait_for_install_finish(self, xml, max_time): """ Method to wait for an installation to finish. This will wait around until either the VM has gone away (at which point it is assumed the @@ -770,23 +750,32 @@ point it is assumed the install failed and raise an exception). """ - disks, interfaces = self._get_disks_and_interfaces(libvirt_dom) + libvirt_dom = self.libvirt_conn.createXML(xml, 0) - last_disk_activity = 0 - last_network_activity = 0 - inactivity_countdown = inactivity_timeout - origcount = count - saved_exception = None - while count > 0 and inactivity_countdown > 0: - if count % 10 == 0: - self.log.debug("Waiting for %s to finish installing, %d/%d", self.tdl.name, count, origcount) + disks, interfaces = self._get_disks_and_interfaces(libvirt_dom.XMLDesc(0)) + + self.last_disk_activity = 0 + self.last_network_activity = 0 + self.inactivity_countdown = self.inactivity_timeout + self.saved_exception = None + if self.has_consolelog: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.settimeout(1) + self.sock.connect(('127.0.0.1', self.console_listen_port)) + + def _finish_cb(self): + ''' + A callback for _looper to deal with waiting for a guest to finish installing. + ''' + if self.inactivity_countdown <= 0: + return True try: total_disk_req, total_net_bytes = self._get_disk_and_net_activity(libvirt_dom, disks, interfaces) except libvirt.libvirtError as e: # we save the exception here because we want to raise it later # if this was a "real" exception - saved_exception = e - break + self.saved_exception = e + return True # rd_req and wr_req are the *total* number of disk read requests and # write requests ever made for this domain. Similarly rd_bytes and @@ -803,62 +792,90 @@ # made, however, to try to reduce false positives from things like # ARP requests - if (total_disk_req == last_disk_activity) and (total_net_bytes < (last_network_activity + 4096)): + if (total_disk_req == self.last_disk_activity) and (total_net_bytes < (self.last_network_activity + 4096)): # if we saw no read or write requests since the last iteration, # decrement our activity timer - inactivity_countdown -= 1 + self.inactivity_countdown -= 1 else: # if we did see some activity, then we can reset the timer - inactivity_countdown = inactivity_timeout + self.inactivity_countdown = self.inactivity_timeout - last_disk_activity = total_disk_req - last_network_activity = total_net_bytes - count -= 1 - time.sleep(1) + self.last_disk_activity = total_disk_req + self.last_network_activity = total_net_bytes + + if self.has_consolelog: + try: + # note that we have to build the data up here, since there + # is no guarantee that we will get the whole write in one go + data = self.sock.recv(65536) + if data: + self.log.debug(data) + except socket.timeout: + # the socket times out after 1 second. We can just fall + # through to the below code because it is a noop. + pass + + return False + + finished = oz.ozutil.timed_loop(max_time, _finish_cb, "Waiting for %s to finish installing" % (self.tdl.name), self) # We get here because of a libvirt exception, an absolute timeout, or # an I/O timeout; we sort this out below - if count == 0: + if not finished: # if we timed out, then let's make sure to take a screenshot. screenshot_text = self._capture_screenshot(libvirt_dom) raise oz.OzException.OzException("Timed out waiting for install to finish. %s" % (screenshot_text)) - elif inactivity_countdown == 0: + elif self.inactivity_countdown <= 0: # if we saw no disk or network activity in the countdown window, # we presume the install has hung. Fail here screenshot_text = self._capture_screenshot(libvirt_dom) - raise oz.OzException.OzException("No disk activity in %d seconds, failing. %s" % (inactivity_timeout, screenshot_text)) + raise oz.OzException.OzException("No disk activity in %d seconds, failing. %s" % (self.inactivity_timeout, screenshot_text)) # We get here only if we got a libvirt exception - self._wait_for_clean_shutdown(libvirt_dom, saved_exception) + if not self._wait_for_guest_shutdown(libvirt_dom): + if self.saved_exception is not None: + self.log.debug("Libvirt Domain Info Failed:") + self.log.debug(" code is %d", self.saved_exception.get_error_code()) + self.log.debug(" domain is %d", self.saved_exception.get_error_domain()) + self.log.debug(" message is %s", self.saved_exception.get_error_message()) + self.log.debug(" level is %d", self.saved_exception.get_error_level()) + self.log.debug(" str1 is %s", self.saved_exception.get_str1()) + self.log.debug(" str2 is %s", self.saved_exception.get_str2()) + self.log.debug(" str3 is %s", self.saved_exception.get_str3()) + self.log.debug(" int1 is %d", self.saved_exception.get_int1()) + self.log.debug(" int2 is %d", self.saved_exception.get_int2()) + raise self.saved_exception + else: + # the passed in exception was None, just raise a generic error + raise oz.OzException.OzException("Unknown libvirt error") self.log.info("Install of %s succeeded", self.tdl.name) - def _wait_for_guest_shutdown(self, libvirt_dom, count=90): + def _wait_for_guest_shutdown(self, libvirt_dom): """ Method to wait around for orderly shutdown of a running guest. Returns True if the guest shutdown in the specified time, False otherwise. """ - origcount = count - saved_exception = None - while count > 0: - if count % 10 == 0: - self.log.debug("Waiting for %s to shutdown, %d/%d", self.tdl.name, count, origcount) + + def _shutdown_cb(self): + ''' + The _looper callback to wait for the guest to shutdown. + ''' try: libvirt_dom.info() except libvirt.libvirtError as e: - saved_exception = e - break - count -= 1 - time.sleep(1) + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: + # Here, the guest really, truly went away cleanly. Thus, we know this + # loop has successfully completed, and we can return True. + return True + + # In all other cases, we got some kind of exception we didn't understand. + # Fall through to the False return, and if we don't eventually see the + # VIR_ERR_NO_DOMAIN, we'll return False to the caller. - # Timed Out - if count == 0: return False - # We get here only if we got a libvirt exception - self._wait_for_clean_shutdown(libvirt_dom, saved_exception) - - return True + return oz.ozutil.timed_loop(self.shutdown_timeout, _shutdown_cb, "Waiting for %s to finish shutdown" % (self.tdl.name), self) def _get_csums(self, original_url, outdir, outputfd): """ @@ -882,7 +899,7 @@ csumname = os.path.join(outdir, self.tdl.distro + self.tdl.update + self.tdl.arch + "-CHECKSUM") - (csumfd,outdir) = self._open_locked_file(csumname) + (csumfd, outdir) = oz.ozutil.open_locked_file(csumname) os.ftruncate(csumfd, 0) @@ -921,8 +938,8 @@ info = oz.ozutil.http_get_header(url) - if not 'HTTP-Code' in info or info['HTTP-Code'] >= 400 or not 'Content-Length' in info or info['Content-Length'] < 0: - raise oz.OzException.OzException("Could not reach destination to fetch boot media") + if 'HTTP-Code' not in info or info['HTTP-Code'] >= 400 or 'Content-Length' not in info or info['Content-Length'] < 0: + raise oz.OzException.OzException("Could not reach %s to fetch boot media: %r" % (url, info)) content_length = int(info['Content-Length']) @@ -964,6 +981,15 @@ """ Method to capture a screenshot of the VM. """ + + # First check to ensure that we've got a graphics device that would even let + # us take a screenshot. + domxml = libvirt_dom.XMLDesc(0) + doc = lxml.etree.fromstring(domxml) + graphics = doc.xpath("/domain/devices/graphics") + if len(graphics) == 0: + return "No graphics device found, screenshot skipped" + oz.ozutil.mkdir_p(self.screenshot_dir) # create a new stream st = libvirt_dom.connect().newStream(0) @@ -1002,157 +1028,6 @@ return text - def _guestfs_handle_setup(self, libvirt_xml): - """ - Method to setup a guestfs handle to the guest disks. - """ - input_doc = lxml.etree.fromstring(libvirt_xml) - namenode = input_doc.xpath('/domain/name') - if len(namenode) != 1: - raise oz.OzException.OzException("invalid libvirt XML with no name") - input_name = namenode[0].text - disks = input_doc.xpath('/domain/devices/disk') - if len(disks) != 1: - self.log.warning("Oz given a libvirt domain with more than 1 disk; using the first one parsed") - source = disks[0].xpath('source') - if len(source) != 1: - raise oz.OzException.OzException("invalid <disk> entry without a source") - input_disk = source[0].get('file') - driver = disks[0].xpath('driver') - if len(driver) == 0: - input_disk_type = 'raw' - elif len(driver) == 1: - input_disk_type = driver[0].get('type') - else: - raise oz.OzException.OzException("invalid <disk> entry without a driver") - - for domid in self.libvirt_conn.listDomainsID(): - try: - doc = lxml.etree.fromstring(self.libvirt_conn.lookupByID(domid).XMLDesc(0)) - except: - self.log.debug("Could not get XML for domain ID (%s) - it may have disappeared (continuing)", domid) - continue - - namenode = doc.xpath('/domain/name') - if len(namenode) != 1: - # hm, odd, a domain without a name? - raise oz.OzException.OzException("Saw a domain without a name, something weird is going on") - if input_name == namenode[0].text: - raise oz.OzException.OzException("Cannot setup ICICLE generation on a running guest") - disks = doc.xpath('/domain/devices/disk') - if len(disks) < 1: - # odd, a domain without a disk, but don't worry about it - continue - for guestdisk in disks: - for source in guestdisk.xpath("source"): - # FIXME: this will only work for files; we can make it work - # for other things by following something like: - # http://git.annexia.org/?p=libguestfs.git;a=blob;f=src/virt.c;h=2c6be3c6a2392ab8242d1f4cee9c0d1445844385;hb=HEAD#l169 - filename = str(source.get('file')) - if filename == input_disk: - raise oz.OzException.OzException("Cannot setup ICICLE generation on a running disk") - - - self.log.info("Setting up guestfs handle for %s", self.tdl.name) - g = guestfs.GuestFS() - - self.log.debug("Adding disk image %s", input_disk) - # NOTE: we use "add_drive_opts" here so we can specify the type - # of the diskimage. Otherwise it might be possible for an attacker - # to fool libguestfs with a specially-crafted diskimage that looks - # like a qcow2 disk (thanks to rjones for the tip) - g.add_drive_opts(input_disk, format=input_disk_type) - - self.log.debug("Launching guestfs") - g.launch() - - self.log.debug("Inspecting guest OS") - roots = g.inspect_os() - - if len(roots) == 0: - raise oz.OzException.OzException("No operating systems found on the disk") - - self.log.debug("Getting mountpoints") - for root in roots: - self.log.debug("Root device: %s", root) - - # the problem here is that the list of mountpoints returned by - # inspect_get_mountpoints is in no particular order. So if the - # diskimage contains /usr and /usr/local on different devices, - # but /usr/local happened to come first in the listing, the - # devices would get mapped improperly. The clever solution here is - # to sort the mount paths by length; this will ensure that they - # are mounted in the right order. Thanks to rjones for the hint, - # and the example code that comes from the libguestfs.org python - # example page. - mps = g.inspect_get_mountpoints(root) - def _compare(a, b): - """ - Method to sort disks by length. - """ - if len(a[0]) > len(b[0]): - return 1 - elif len(a[0]) == len(b[0]): - return 0 - else: - return -1 - mps.sort(_compare) - for mp_dev in mps: - try: - g.mount_options('', mp_dev[1], mp_dev[0]) - except: - if mp_dev[0] == '/': - # If we cannot mount root, we may as well give up - raise - else: - # some custom guests may have fstab content with - # "nofail" as a mount option. For example, images - # built for EC2 with ephemeral mappings. These - # fail at this point. Allow things to continue. - # Profound failures will trigger later on during - # the process. - self.log.warning("Unable to mount (%s) on (%s) - trying to continue", mp_dev[1], mp_dev[0]) - return g - - def _guestfs_remove_if_exists(self, g_handle, path): - """ - Method to remove a file if it exists in the disk image. - """ - if g_handle.exists(path): - g_handle.rm_rf(path) - - def _guestfs_move_if_exists(self, g_handle, orig_path, replace_path): - """ - Method to move a file if it exists in the disk image. - """ - if g_handle.exists(orig_path): - g_handle.mv(orig_path, replace_path) - - def _guestfs_path_backup(self, g_handle, orig): - """ - Method to backup a file in the disk image. - """ - self._guestfs_move_if_exists(g_handle, orig, orig + ".ozbackup") - - def _guestfs_path_restore(self, g_handle, orig): - """ - Method to restore a backup file in the disk image. - """ - backup = orig + ".ozbackup" - self._guestfs_remove_if_exists(g_handle, orig) - self._guestfs_move_if_exists(g_handle, backup, orig) - - def _guestfs_handle_cleanup(self, g_handle): - """ - Method to cleanup a handle previously setup by __guestfs_handle_setup. - """ - self.log.info("Cleaning up guestfs handle for %s", self.tdl.name) - self.log.debug("Syncing") - g_handle.sync() - - self.log.debug("Unmounting all") - g_handle.umount_all() - def _modify_libvirt_xml_for_serial(self, libvirt_xml): """ Internal method to take input libvirt XML (which may have been provided @@ -1188,8 +1063,7 @@ self.log.debug("Generated XML:\n%s", xml) return xml - def _modify_libvirt_xml_diskimage(self, libvirt_xml, new_diskimage, - image_type): + def _modify_libvirt_xml_diskimage(self, libvirt_xml, new_diskimage, image_type): """ Internal method to take input libvirt XML and replace the existing disk image details with a new disk image file and, potentially, disk image @@ -1228,69 +1102,70 @@ """ self.log.info("Waiting for guest %s to boot", self.tdl.name) - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: - sock.settimeout(1) - sock.connect(('127.0.0.1', self.listen_port)) + self.sock.settimeout(1) + self.sock.connect(('127.0.0.1', self.listen_port)) + + self.addr = None + self.data = '' - addr = None - count = 300 - data = '' - while count > 0: - do_sleep = True - if count % 10 == 0: - self.log.debug("Waiting for guest %s to boot, %d/300", self.tdl.name, count) + def _boot_cb(self): + ''' + The _looper callback to look for the guest to boot. + ''' try: # note that we have to build the data up here, since there # is no guarantee that we will get the whole write in one go - data += sock.recv(100) + self.data += self.sock.recv(100) except socket.timeout: # the socket times out after 1 second. We can just fall - # through to the below code because it is a noop, *except* that - # we don't want to sleep. Set the flag - do_sleep = False + # through to the below code because it is a noop. + pass # OK, we got data back from the socket. Check to see if it is # is what we expect; essentially, some up-front garbage, # followed by a !<ip>,<uuid>! # Exclude ! from the wildcard to avoid errors when receiving two # announce messages in the same string - match = re.search("!([^!]*?,[^!]*?)!$", data) + match = re.search("!([^!]*?,[^!]*?)!$", self.data) if match is not None: if len(match.groups()) != 1: raise oz.OzException.OzException("Guest checked in with no data") split = match.group(1).split(',') if len(split) != 2: raise oz.OzException.OzException("Guest checked in with bogus data") - addr = split[0] + self.addr = split[0] uuidstr = split[1] try: # use socket.inet_aton() to validate the IP address - socket.inet_aton(addr) + socket.inet_aton(self.addr) except socket.error: raise oz.OzException.OzException("Guest checked in with invalid IP address") if uuidstr != str(self.uuid): raise oz.OzException.OzException("Guest checked in with unknown UUID") - break + return True # if the data we got didn't match, we need to continue waiting. # before going to sleep, make sure that the domain is still # around libvirt_dom.info() - if do_sleep: - time.sleep(1) - count -= 1 + + return False + + oz.ozutil.timed_loop(self.boot_timeout, _boot_cb, "Waiting for %s to finish boot" % (self.tdl.name), self) + finally: - sock.close() + self.sock.close() - if addr is None: + if self.addr is None: raise oz.OzException.OzException("Timed out waiting for guest to boot") - self.log.debug("IP address of guest is %s", addr) + self.log.debug("IP address of guest is %s", self.addr) - return addr + return self.addr def _output_icicle_xml(self, lines, description, extra=None): """ @@ -1302,16 +1177,16 @@ """ icicle = lxml.etree.Element("icicle") if description is not None: - self.lxml_subelement(icicle, "description", description) - packages = self.lxml_subelement(icicle, "packages") + oz.ozutil.lxml_subelement(icicle, "description", description) + packages = oz.ozutil.lxml_subelement(icicle, "packages") for index, line in enumerate(lines): if line == "": continue - package = self.lxml_subelement(packages, "package", None, - {'name':line}) + package = oz.ozutil.lxml_subelement(packages, "package", None, + {'name':line}) if extra is not None: - self.lxml_subelement(package, "extra", extra[index]) + oz.ozutil.lxml_subelement(package, "extra", extra[index]) return lxml.etree.tostring(icicle, pretty_print=True) @@ -1399,26 +1274,6 @@ f.write(keystring) os.chmod(pubname, 0o644) - def _open_locked_file(self, filename): - """ - Method to open and lock a file. Returns a file descriptor referencing - the open and locked file. - """ - outdir = os.path.dirname(filename) - oz.ozutil.mkdir_p(outdir) - - fd = os.open(filename, os.O_RDWR|os.O_CREAT) - - try: - self.log.debug("Attempting to get the lock for %s", filename) - fcntl.lockf(fd, fcntl.LOCK_EX) - self.log.debug("Got the lock for %s", filename) - except: - os.close(fd) - raise - - return (fd, outdir) - class CDGuest(Guest): """ Class for guest installation via ISO. @@ -1522,9 +1377,14 @@ tar.kill() raise - # FIXME: we really should check tar.poll() here to get - # the return code, and print out stdout and stderr if - # we fail. This will make debugging problems easier + # Make sure tar is fully done with the archive before + # we can continue. + tar.wait() + if tar.returncode: + self.log.debug('tar stdout: %s', stdouttmp.read()) + self.log.debug('tar stderr: %s', stderrtmp.read()) + raise oz.OzException.OzException("Tar exited with an error") + finally: stdouttmp.close() stderrtmp.close() @@ -1679,7 +1539,7 @@ def _do_install(self, timeout=None, force=False, reboots=0, kernelfname=None, ramdiskfname=None, cmdline=None, - extrainstalldevs=None): + extrainstalldevs=None, virtio_channel_name=None): """ Internal method to actually run the installation. """ @@ -1691,10 +1551,10 @@ self.log.info("Running install for %s", self.tdl.name) if timeout is None: - timeout = 1200 + timeout = self.default_install_timeout cddev = self._InstallDev("cdrom", self.output_iso, "hdc") - if extrainstalldevs != None: + if extrainstalldevs is not None: extrainstalldevs.append(cddev) cddev = extrainstalldevs reboots_to_go = reboots @@ -1704,14 +1564,16 @@ if reboots_to_go == reboots: if kernelfname and os.access(kernelfname, os.F_OK) and ramdiskfname and os.access(ramdiskfname, os.F_OK) and cmdline: xml = self._generate_xml(None, None, kernelfname, - ramdiskfname, cmdline) + ramdiskfname, cmdline, + virtio_channel_name) else: - xml = self._generate_xml("cdrom", cddev) + xml = self._generate_xml("cdrom", cddev, None, None, None, + virtio_channel_name) else: - xml = self._generate_xml("hd", cddev) + xml = self._generate_xml("hd", cddev, None, None, None, + virtio_channel_name) - dom = self.libvirt_conn.createXML(xml, 0) - self._wait_for_install_finish(dom, timeout) + self._wait_for_install_finish(xml, timeout) reboots_to_go -= 1 @@ -1820,7 +1682,7 @@ shutil.copyfile(self.modified_iso_cache, self.output_iso) return - (fd, outdir) = self._open_locked_file(self.orig_iso) + (fd, outdir) = oz.ozutil.open_locked_file(self.orig_iso) try: self._get_original_iso(url, fd, outdir, force_download) @@ -1939,9 +1801,7 @@ if timeout is None: timeout = 1200 - dom = self.libvirt_conn.createXML(self._generate_xml("fd", fddev), - 0) - self._wait_for_install_finish(dom, timeout) + self._wait_for_install_finish(self._generate_xml("fd", fddev), timeout) if self.cache_jeos: self.log.info("Caching JEOS") diff -Nru oz-0.15.0/oz/Linux.py oz-0.16.0/oz/Linux.py --- oz-0.15.0/oz/Linux.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Linux.py 2017-08-08 18:46:43.000000000 +0000 @@ -20,9 +20,10 @@ import re import time -import libvirt import os +import libvirt + import oz.Guest import oz.OzException diff -Nru oz-0.15.0/oz/Mageia.py oz-0.16.0/oz/Mageia.py --- oz-0.15.0/oz/Mageia.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Mageia.py 2017-08-08 18:46:43.000000000 +0000 @@ -21,23 +21,28 @@ import shutil import os import re +try: + import urllib.parse as urlparse +except ImportError: + import urlparse -import oz.Guest +import oz.GuestFSManager +import oz.Linux import oz.ozutil import oz.OzException -class MageiaGuest(oz.Guest.CDGuest): +class MageiaGuest(oz.Linux.LinuxCDGuest): """ - Class for Mageia 4 installation. + Class for Mageia 2, 3, 4, 4.1, and 5 installation. """ def __init__(self, tdl, config, auto, output_disk, netdev, diskbus, macaddress): - oz.Guest.CDGuest.__init__(self, tdl, config, auto, output_disk, netdev, - None, None, diskbus, True, False, macaddress) + oz.Linux.LinuxCDGuest.__init__(self, tdl, config, auto, output_disk, + netdev, diskbus, True, True, + macaddress) - self.mageia_arch = self.tdl.arch - if self.mageia_arch == "i386": - self.mageia_arch = "i586" + self.sshd_was_active = False + self.crond_was_active = False def _modify_iso(self): """ @@ -72,16 +77,146 @@ "::AUTO_INST.CFG"]) self.log.debug("Modifying isolinux.cfg") - isolinuxcfg = os.path.join(self.iso_contents, "isolinux", "isolinux.cfg") + if self.tdl.update == "2": + ''' + Mageia 2 dual - isolinux/32.cfg + isolinux/64.cfg + isolinux/alt0/32/vmlinuz + isolinux/alt0/32/all.rdz + isolinux/alt0/64/vmlinuz + isolinux/alt0/64/all.rdz + Mageia 2 x86_64 - x86_64/isolinux/isolinux.cfg + x86_64/isolinux/alt0/vmlinuz + x86_64/isolinux/alt0/all.rdz + Mageia 2 i586 - i586/isolinux/isolinux.cfg + i586/isolinux/vmlinuz + i586/isolinux/all.rdz + ''' + if os.path.exists(os.path.join(self.iso_contents, 'isolinux')): + if self.tdl.arch == "i386": + mageia_arch = "32" + else: + mageia_arch = "64" + + # This looks like a dual CD, so let's set things up that way. + isolinuxcfg = os.path.join(self.iso_contents, 'isolinux', mageia_arch + ".cfg") + self.isolinuxbin = os.path.join('isolinux', mageia_arch + ".bin") + kernel = "alt0/" + mageia_arch + "/vmlinuz" + initrd = "alt0/" + mageia_arch + "/all.rdz" + else: + # This looks like an i586 or x86_64 ISO, so set things up that way. + mageia_arch = self.tdl.arch + if self.tdl.arch == "i386": + mageia_arch = "i586" + isolinuxcfg = os.path.join(self.iso_contents, mageia_arch, 'isolinux', 'isolinux.cfg') + self.isolinuxbin = os.path.join(mageia_arch, 'isolinux', 'isolinux.bin') + kernel = "alt0/vmlinuz" + initrd = "alt0/all.rdz" + flags = "ramdisk_size=128000 root=/dev/ram3 acpi=ht vga=788 automatic=method:cdrom" + elif self.tdl.update == "3": + ''' + Mageia 3 dual - syslinux/32.cfg + syslinux/64.cfg + syslinux/alt0/32/vmlinuz + syslinux/alt0/32/all.rdz + syslinux/alt0/64/vmlinuz + syslinux/alt0/64/all.rdz + Mageia 3 x86_64 - x86_64/isolinux/isolinux.cfg + x86_64/isolinux/alt0/vmlinuz + x86_64/isolinux/alt0/all.rdz + Mageia 3 i586 - i586/isolinux/isolinux.cfg + i586/isolinux/alt0/vmlinuz + i586/isolinux/alt0/all.rdz + ''' + if os.path.exists(os.path.join(self.iso_contents, 'syslinux')): + if self.tdl.arch == "i386": + mageia_arch = "32" + else: + mageia_arch = "64" + isolinuxcfg = os.path.join(self.iso_contents, 'syslinux', mageia_arch + ".cfg") + self.isolinuxbin = os.path.join('syslinux', mageia_arch + ".bin") + kernel = "alt0/" + mageia_arch + "/vmlinuz" + initrd = "alt0/" + mageia_arch + "/all.rdz" + else: + mageia_arch = self.tdl.arch + if self.tdl.arch == "i386": + mageia_arch = "i586" + isolinuxcfg = os.path.join(self.iso_contents, mageia_arch, 'isolinux', 'isolinux.cfg') + self.isolinuxbin = os.path.join(mageia_arch, 'isolinux', 'isolinux.bin') + kernel = "alt0/vmlinuz" + initrd = "alt0/all.rdz" + flags = "ramdisk_size=128000 root=/dev/ram3 acpi=ht vga=788 automatic=method:cdrom" + elif self.tdl.update in ["4", "4.1", "5"]: + ''' + Mageia 4 dual - isolinux/i586.cfg + isolinux/x86_64.cfg + isolinux/i586/vmlinuz + isolinux/i586/all.rdz + isolinux/x86_64/vmlinuz + isolinux/x86_64/all.rdz + Mageia 4 x86_64 - isolinux/isolinux.cfg + isolinux/x86_64/vmlinuz + isolinux/x86_64/all.rdz + Mageia 4 i586 - isolinux/isolinux.cfg + isolinux/i586/vmlinuz + isolinuz/i586/all.rdz + Mageia 4.1 dual - isolinux/i586.cfg + isolinux/x86_64.cfg + isolinux/i586/vmlinuz + isolinux/i586/all.rdz + isolinux/x86_64/vmlinuz + isolinux/x86_64/all.rdz + Mageia 4.1 x86_64 - isolinux/isolinux.cfg + isolinux/x86_64/vmlinuz + isolinux/x86_64/all.rdz + Mageia 4.1 i586 - isolinux/isolinux.cfg + isolinux/i586/vmlinuz + isolinux/i586/all.rdz + Mageia 5 dual - isolinux/i586.cfg + isolinux/x86_64.cfg + isolinux/i586/vmlinuz + isolinux/i586/all.rdz + isolinux/x86_64/vmlinuz + isolinux/x86_64/all.rdz + Mageia 5 x86_64 - isolinux/isolinux.cfg + isolinux/x86_64/vmlinuz + isolinux/x86_64/all.rdz + Mageia 5 i586 - isolinux/isolinux.cfg + isolinux/i586/vmlinuz + isolinux/i586/all.rdz + ''' + # Starting with Mageia 4, things are a lot more regular. The + # directory always starts with isolinux. If it is a dual ISO, then + # there is an i586.cfg and x86_64.cfg describing how to boot each + # of them. Otherwise, there is just an isolinux.cfg. The kernel + # and initrd are always in the same place. + mageia_arch = self.tdl.arch + if self.tdl.arch == "i386": + mageia_arch = "i586" + if os.path.exists(os.path.join(self.iso_contents, 'isolinux', 'i586.cfg')): + # A dual, so use the correct cfg + isolinuxcfg = os.path.join(self.iso_contents, 'isolinux', mageia_arch + ".cfg") + self.isolinuxbin = os.path.join('isolinux', mageia_arch + ".bin") + else: + isolinuxcfg = os.path.join(self.iso_contents, 'isolinux', 'isolinux.cfg') + self.isolinuxbin = os.path.join('isolinux', 'isolinux.bin') + kernel = mageia_arch + "/vmlinuz" + initrd = mageia_arch + "/all.rdz" + if self.tdl.installtype == "url": + url = urlparse.urlparse(self.tdl.url) + flags = "automatic=method:%s,ser:%s,dir:%s,int:eth0,netw:dhcp" % (url.scheme, url.hostname, url.path) + else: + flags = "automatic=method:cdrom" + with open(isolinuxcfg, 'w') as f: f.write("""\ default customiso timeout 1 prompt 0 label customiso - kernel alt0/vmlinuz - append initrd=alt0/all.rdz ramdisk_size=128000 root=/dev/ram3 acpi=ht vga=788 automatic=method:cdrom kickstart=floppy -""") + kernel %s + append initrd=%s kickstart=floppy %s +""" % (kernel, initrd, flags)) def _generate_new_iso(self): """ @@ -89,23 +224,311 @@ """ self.log.info("Generating new ISO") - isolinuxdir = "" - if self.tdl.update in ["4"]: - isolinuxdir = self.mageia_arch - - isolinuxbin = os.path.join(isolinuxdir, "isolinux/isolinux.bin") - isolinuxboot = os.path.join(isolinuxdir, "isolinux/boot.cat") - oz.ozutil.subprocess_check_output(["genisoimage", "-r", "-V", "Custom", "-J", "-l", "-no-emul-boot", - "-b", isolinuxbin, - "-c", isolinuxboot, + "-b", self.isolinuxbin, + "-c", "boot.catalog", "-boot-load-size", "4", "-cache-inodes", "-boot-info-table", "-v", "-o", self.output_iso, self.iso_contents], printfn=self.log.debug) + def _get_service_runlevel_link(self, g_handle, service): + """ + Method to find the runlevel link(s) for a service based on the name + and the (detected) default runlevel. + """ + runlevel = self.get_default_runlevel(g_handle) + + lines = g_handle.cat('/etc/init.d/' + service).split("\n") + startlevel = "99" + for line in lines: + if re.match('# chkconfig:', line): + try: + startlevel = line.split(':')[1].split()[1] + except: + pass + break + + return "/etc/rc" + runlevel + ".d/S" + startlevel + service + + def _image_ssh_teardown_step_1(self, g_handle): + """ + First step to undo _image_ssh_setup (remove authorized keys). + """ + self.log.debug("Teardown step 1") + # reset the authorized keys + self.log.debug("Resetting authorized_keys") + g_handle.path_restore('/root/.ssh/authorized_keys') + + def _image_ssh_teardown_step_2(self, g_handle): + """ + Second step to undo _image_ssh_setup (remove custom sshd_config). + """ + self.log.debug("Teardown step 2") + # remove custom sshd_config + self.log.debug("Resetting sshd_config") + g_handle.path_restore('/etc/ssh/sshd_config') + + # reset the service link + self.log.debug("Resetting sshd service") + if g_handle.exists('/usr/lib/systemd/system/sshd.service'): + if not self.sshd_was_active: + g_handle.rm('/etc/systemd/system/multi-user.target.wants/sshd.service') + else: + startuplink = self._get_service_runlevel_link(g_handle, 'sshd') + g_handle.path_restore(startuplink) + + def _image_ssh_teardown_step_3(self, g_handle): + """ + Third step to undo _image_ssh_setup (remove guest announcement). + """ + self.log.debug("Teardown step 3") + # remove announce cronjob + self.log.debug("Resetting announcement to host") + g_handle.remove_if_exists('/etc/cron.d/announce') + + # remove reportip + self.log.debug("Removing reportip") + g_handle.remove_if_exists('/root/reportip') + + # reset the service link + self.log.debug("Resetting cron service") + if g_handle.exists('/usr/lib/systemd/system/cron.service'): + if not self.crond_was_active: + g_handle.rm('/etc/systemd/system/multi-user.target.wants/cron.service') + else: + runlevel = self.get_default_runlevel(g_handle) + startuplink = '/etc/rc.d/rc' + runlevel + ".d/S06cron" + g_handle.path_restore(startuplink) + + def _image_ssh_teardown_step_4(self, g_handle): + """ + Fourth step to undo changes by the operating system. For instance, + during first boot openssh generates ssh host keys and stores them + in /etc/ssh. Since this image might be cached later on, this method + removes those keys. + """ + for f in ["/etc/ssh/ssh_host_dsa_key", "/etc/ssh/ssh_host_dsa_key.pub", + "/etc/ssh/ssh_host_rsa_key", "/etc/ssh/ssh_host_rsa_key.pub", + "/etc/ssh/ssh_host_ecdsa_key", "/etc/ssh/ssh_host_ecdsa_key.pub", + "/etc/ssh/ssh_host_key", "/etc/ssh/ssh_host_key.pub"]: + g_handle.remove_if_exists(f) + + # Remove any lease files; this is so that subsequent boots don't try + # to connect to a DHCP server that is on a totally different network + for lease in g_handle.glob_expand("/var/lib/dhcp/*.leases"): + g_handle.rm(lease) + + for lease in g_handle.glob_expand("/var/lib/dhcp6/*.leases"): + g_handle.rm(lease) + + for lease in g_handle.glob_expand("/var/lib/dhcp6/*.lease"): + g_handle.rm(lease) + + def _collect_teardown(self, libvirt_xml): + """ + Method to reverse the changes done in _collect_setup. + """ + self.log.info("Collection Teardown") + + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() + + try: + self._image_ssh_teardown_step_1(g_handle) + + self._image_ssh_teardown_step_2(g_handle) + + self._image_ssh_teardown_step_3(g_handle) + + self._image_ssh_teardown_step_4(g_handle) + finally: + g_handle.cleanup() + shutil.rmtree(self.icicle_tmp) + + def _image_ssh_setup_step_1(self, g_handle): + """ + First step for allowing remote access (generate and upload ssh keys). + """ + # part 1; upload the keys + self.log.debug("Step 1: Uploading ssh keys") + if not g_handle.exists('/root/.ssh'): + g_handle.mkdir('/root/.ssh') + + g_handle.path_backup('/root/.ssh/authorized_keys') + + self._generate_openssh_key(self.sshprivkey) + + g_handle.upload(self.sshprivkey + ".pub", '/root/.ssh/authorized_keys') + + def _image_ssh_setup_step_2(self, g_handle): + """ + Second step for allowing remote access (ensure sshd is running). + """ + # part 2; check and setup sshd + self.log.debug("Step 2: setup sshd") + if not g_handle.exists('/usr/sbin/sshd'): + raise oz.OzException.OzException("ssh not installed on the image, cannot continue") + + if g_handle.exists('/usr/lib/systemd/system/sshd.service'): + if g_handle.exists('/etc/systemd/system/multi-user.target.wants/sshd.service'): + self.sshd_was_active = True + else: + g_handle.ln_sf('/usr/lib/systemd/system/sshd.service', + '/etc/systemd/system/multi-user.target.wants/sshd.service') + else: + startuplink = self._get_service_runlevel_link(g_handle, 'sshd') + g_handle.path_backup(startuplink) + g_handle.ln_sf('/etc/init.d/sshd', startuplink) + + sshd_config_file = self.icicle_tmp + "/sshd_config" + with open(sshd_config_file, 'w') as f: + f.write("""PasswordAuthentication no +UsePAM yes + +X11Forwarding yes + +Subsystem sftp /usr/lib64/ssh/sftp-server + +AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES +AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT +AcceptEnv LC_IDENTIFICATION LC_ALL +""") + + try: + g_handle.path_backup("/etc/ssh/sshd_config") + g_handle.upload(sshd_config_file, '/etc/ssh/sshd_config') + finally: + os.unlink(sshd_config_file) + + def _image_ssh_setup_step_3(self, g_handle): + """ + Third step for allowing remote access (make the guest announce itself + on bootup). + """ + # part 3; make sure the guest announces itself + self.log.debug("Step 3: Guest announcement") + + scriptfile = os.path.join(self.icicle_tmp, "script") + + if not g_handle.exists('/usr/sbin/crond'): + raise oz.OzException.OzException("cron not installed on the image, cannot continue") + + with open(scriptfile, 'w') as f: + f.write("""\ +#!/bin/bash +DEV=$(/bin/awk '{if ($2 == 0) print $1}' /proc/net/route) && +[ -z "$DEV" ] && exit 0 +ADDR=$(/sbin/ip -4 -o addr show dev $DEV | /bin/awk '{print $4}' | /usr/bin/cut -d/ -f1) && +[ -z "$ADDR" ] && exit 0 +echo -n "!$ADDR,%s!" > /dev/ttyS1 +""" % (self.uuid)) + + try: + g_handle.upload(scriptfile, '/root/reportip') + g_handle.chmod(0o755, '/root/reportip') + finally: + os.unlink(scriptfile) + + announcefile = os.path.join(self.icicle_tmp, "announce") + with open(announcefile, 'w') as f: + f.write('*/1 * * * * root /bin/bash -c "/root/reportip"\n') + + try: + g_handle.upload(announcefile, '/etc/cron.d/announce') + finally: + os.unlink(announcefile) + + if g_handle.exists('/usr/lib/systemd/system/cron.service'): + if g_handle.exists('/etc/systemd/system/multi-user.target.wants/cron.service'): + self.crond_was_active = True + else: + g_handle.ln_sf('/lib/systemd/system/cron.service', + '/etc/systemd/system/multi-user.target.wants/cron.service') + else: + runlevel = self.get_default_runlevel(g_handle) + startuplink = '/etc/rc.d/rc' + runlevel + ".d/S06cron" + g_handle.path_backup(startuplink) + g_handle.ln_sf('/etc/init.d/cron', startuplink) + + def _collect_setup(self, libvirt_xml): + """ + Setup the guest for remote access. + """ + self.log.info("Collection Setup") + + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() + + # we have to do 3 things to make sure we can ssh into OpenSUSE: + # 1) Upload our ssh key + # 2) Make sure sshd is running on boot + # 3) Make the guest announce itself to the host + + try: + try: + self._image_ssh_setup_step_1(g_handle) + + try: + self._image_ssh_setup_step_2(g_handle) + + try: + self._image_ssh_setup_step_3(g_handle) + except: + self._image_ssh_teardown_step_3(g_handle) + raise + except: + self._image_ssh_teardown_step_2(g_handle) + raise + except: + self._image_ssh_teardown_step_1(g_handle) + raise + + finally: + g_handle.cleanup() + + def do_icicle(self, guestaddr): + """ + Method to collect the package information and generate the ICICLE + XML. + """ + self.log.debug("Generating ICICLE") + stdout, stderr, retcode = self.guest_execute_command(guestaddr, + 'rpm -qa', + timeout=30) + + package_split = stdout.split("\n") + + extrasplit = None + if self.tdl.icicle_extra_cmd: + extrastdout, stderr, retcode = self.guest_execute_command(guestaddr, + self.tdl.icicle_extra_cmd, + timeout=30) + extrasplit = extrastdout.split("\n") + + if len(package_split) != len(extrasplit): + raise oz.OzException.OzException("Invalid extra package command; it must return the same set of packages as 'rpm -qa'") + + return self._output_icicle_xml(package_split, self.tdl.description, + extrasplit) + + def generate_install_media(self, force_download=False, + customize_or_icicle=False): + """ + Method to generate the install media for Mageia based operating + systems. If force_download is False (the default), then the + original media will only be fetched if it is not cached locally. If + force_download is True, then the original media will be downloaded + regardless of whether it is cached locally. + """ + fetchurl = self.url + if self.tdl.installtype == 'url': + fetchurl += "/install/images/boot.iso" + return self._iso_generate_install_media(fetchurl, force_download, + customize_or_icicle) + def install(self, timeout=None, force=False): fddev = self._InstallDev("floppy", self.output_floppy, "fda") return self._do_install(timeout, force, 0, None, None, None, @@ -116,7 +539,11 @@ """ Factory method for Mageia installs. """ - if tdl.update in ["4"]: + if tdl.update in ["2", "3", "4", "4.1", "5"]: + if netdev is None: + netdev = 'virtio' + if diskbus is None: + diskbus = 'virtio' return MageiaGuest(tdl, config, auto, output_disk, netdev, diskbus, macaddress) @@ -124,4 +551,4 @@ """ Return supported versions as a string. """ - return "Mageia: 4" + return "Mageia: 2, 3, 4, 4.1, 5" diff -Nru oz-0.15.0/oz/Mandrake.py oz-0.16.0/oz/Mandrake.py --- oz-0.15.0/oz/Mandrake.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Mandrake.py 2017-08-08 18:46:43.000000000 +0000 @@ -93,6 +93,12 @@ self.iso_contents], printfn=self.log.debug) + def install(self, timeout=None, force=False): + internal_timeout = timeout + if internal_timeout is None and self.tdl.update == "9.0": + internal_timeout = 2500 + return self._do_install(internal_timeout, force, 0) + class Mandrake82Guest(oz.Guest.CDGuest): """ Class for Mandrake 8.2 installation. @@ -171,7 +177,7 @@ if tdl.update in ["8.2"]: return Mandrake82Guest(tdl, config, auto, output_disk, netdev, diskbus, macaddress) - if tdl.update in ["9.1", "9.2", "10.0", "10.1"]: + if tdl.update in ["9.0", "9.1", "9.2", "10.0", "10.1"]: return MandrakeGuest(tdl, config, auto, output_disk, netdev, diskbus, macaddress) @@ -179,4 +185,4 @@ """ Return supported versions as a string. """ - return "Mandrake: 8.2, 9.1, 9.2, 10.0, 10.1" + return "Mandrake: 8.2, 9.0, 9.1, 9.2, 10.0, 10.1" diff -Nru oz-0.15.0/oz/OpenSUSE.py oz-0.16.0/oz/OpenSUSE.py --- oz-0.15.0/oz/OpenSUSE.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/OpenSUSE.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2010,2011 Chris Lalancette <clalance@redhat.com> -# Copyright (C) 2012-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2012-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -24,6 +24,7 @@ import os import lxml.etree +import oz.GuestFSManager import oz.Linux import oz.ozutil import oz.OzException @@ -131,7 +132,7 @@ self.log.debug("Teardown step 1") # reset the authorized keys self.log.debug("Resetting authorized_keys") - self._guestfs_path_restore(g_handle, '/root/.ssh/authorized_keys') + g_handle.path_restore('/root/.ssh/authorized_keys') def _image_ssh_teardown_step_2(self, g_handle): """ @@ -140,15 +141,15 @@ self.log.debug("Teardown step 2") # remove custom sshd_config self.log.debug("Resetting sshd_config") - self._guestfs_path_restore(g_handle, '/etc/ssh/sshd_config') + g_handle.path_restore('/etc/ssh/sshd_config') # reset the service link self.log.debug("Resetting sshd service") if g_handle.exists('/usr/lib/systemd/system/sshd.service'): if not self.sshd_was_active: - g_handle.rm('/etc/systemd/system/multi-user.target.wants/sshd.service') + g_handle.remove_if_exists('/etc/systemd/system/multi-user.target.wants/sshd.service') else: - self._guestfs_path_restore(g_handle, '/etc/init.d/after.local') + g_handle.path_restore('/etc/init.d/after.local') def _image_ssh_teardown_step_3(self, g_handle): """ @@ -157,13 +158,12 @@ self.log.debug("Teardown step 3") # remove announce cronjob self.log.debug("Resetting announcement to host") - self._guestfs_remove_if_exists(g_handle, - '/etc/NetworkManager/dispatcher.d/99-reportip') - self._guestfs_remove_if_exists(g_handle, '/etc/cron.d/announce') + g_handle.remove_if_exists('/etc/NetworkManager/dispatcher.d/99-reportip') + g_handle.remove_if_exists('/etc/cron.d/announce') # remove reportip self.log.debug("Removing reportip") - self._guestfs_remove_if_exists(g_handle, '/root/reportip') + g_handle.remove_if_exists('/root/reportip') # reset the service link self.log.debug("Resetting cron service") @@ -173,7 +173,7 @@ else: runlevel = self.get_default_runlevel(g_handle) startuplink = '/etc/rc.d/rc' + runlevel + ".d/S06cron" - self._guestfs_path_restore(g_handle, startuplink) + g_handle.path_restore(startuplink) def _image_ssh_teardown_step_4(self, g_handle): """ @@ -186,18 +186,18 @@ "/etc/ssh/ssh_host_rsa_key", "/etc/ssh/ssh_host_rsa_key.pub", "/etc/ssh/ssh_host_ecdsa_key", "/etc/ssh/ssh_host_ecdsa_key.pub", "/etc/ssh/ssh_host_key", "/etc/ssh/ssh_host_key.pub"]: - self._guestfs_remove_if_exists(g_handle, f) + g_handle.remove_if_exists(f) # Remove any lease files; this is so that subsequent boots don't try # to connect to a DHCP server that is on a totally different network for lease in g_handle.glob_expand("/var/lib/dhcp/*.leases"): - g_handle.rm_f(lease) + g_handle.rm(lease) for lease in g_handle.glob_expand("/var/lib/dhcp6/*.leases"): - g_handle.rm_f(lease) + g_handle.rm(lease) for lease in g_handle.glob_expand("/var/lib/dhcp6/*.lease"): - g_handle.rm_f(lease) + g_handle.rm(lease) def _collect_teardown(self, libvirt_xml): """ @@ -205,7 +205,8 @@ """ self.log.info("Collection Teardown") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() try: self._image_ssh_teardown_step_1(g_handle) @@ -216,7 +217,7 @@ self._image_ssh_teardown_step_4(g_handle) finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() shutil.rmtree(self.icicle_tmp) def do_icicle(self, guestaddr): @@ -241,7 +242,7 @@ if not g_handle.exists('/root/.ssh'): g_handle.mkdir('/root/.ssh') - self._guestfs_path_backup(g_handle, '/root/.ssh/authorized_keys') + g_handle.path_backup('/root/.ssh/authorized_keys') self._generate_openssh_key(self.sshprivkey) @@ -253,7 +254,8 @@ """ # part 2; check and setup sshd self.log.debug("Step 2: setup sshd") - if not g_handle.exists('/etc/init.d/sshd') or not g_handle.exists('/usr/sbin/sshd'): + + if not g_handle.exists('/usr/sbin/sshd'): raise oz.OzException.OzException("ssh not installed on the image, cannot continue") if g_handle.exists('/usr/lib/systemd/system/sshd.service'): @@ -263,7 +265,7 @@ g_handle.ln_sf('/usr/lib/systemd/system/sshd.service', '/etc/systemd/system/multi-user.target.wants/sshd.service') else: - self._guestfs_path_backup(g_handle, "/etc/init.d/after.local") + g_handle.path_backup("/etc/init.d/after.local") local = os.path.join(self.icicle_tmp, "after.local") with open(local, "w") as f: f.write("/sbin/service sshd start\n") @@ -287,7 +289,7 @@ """) try: - self._guestfs_path_backup(g_handle, "/etc/ssh/sshd_config") + g_handle.path_backup("/etc/ssh/sshd_config") g_handle.upload(sshd_config_file, '/etc/ssh/sshd_config') finally: os.unlink(sshd_config_file) @@ -320,7 +322,7 @@ finally: os.unlink(scriptfile) - if not g_handle.exists('/etc/init.d/cron') or not g_handle.exists('/usr/sbin/cron'): + if not g_handle.exists('/usr/sbin/cron'): raise oz.OzException.OzException("cron not installed on the image, cannot continue") with open(scriptfile, 'w') as f: @@ -357,7 +359,7 @@ else: runlevel = self.get_default_runlevel(g_handle) startuplink = '/etc/rc.d/rc' + runlevel + ".d/S06cron" - self._guestfs_path_backup(g_handle, startuplink) + g_handle.path_backup(startuplink) g_handle.ln_sf('/etc/init.d/cron', startuplink) def _collect_setup(self, libvirt_xml): @@ -366,7 +368,8 @@ """ self.log.info("Collection Setup") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() # we have to do 3 things to make sure we can ssh into OpenSUSE: # 1) Upload our ssh key @@ -393,7 +396,7 @@ raise finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() def _customize_repos(self, guestaddr): """ @@ -415,8 +418,13 @@ if re.match("^[0-9]+", line): split = line.split('|') - if re.match("^cd://", split[7].strip()): + column = 7 + if self.tdl.update in ["42.1", "42.2"]: + # OpenSUSE Leap has the URI in the 8th column + column = 8 + if re.match("^cd://", split[column].strip()): removerepos.append(split[0].strip()) + for repo in removerepos: self.guest_execute_command(guestaddr, 'zypper removerepo %s' % (repo)) @@ -439,7 +447,7 @@ return OpenSUSEGuest(tdl, config, auto, output_disk, netdev, diskbus, macaddress) if tdl.update in ["11.0", "11.1", "11.2", "11.3", "11.4", "12.1", "12.2", - "12.3", "13.1", "13.2"]: + "12.3", "13.1", "13.2", "42.1", "42.2"]: if diskbus is None: diskbus = 'virtio' if netdev is None: @@ -451,4 +459,4 @@ """ Return supported versions as a string. """ - return "OpenSUSE: 10.3, 11.0, 11.1, 11.2, 11.3, 11.4, 12.1, 12.2, 12.3, 13.1, 13.2" + return "OpenSUSE: 10.3, 11.0, 11.1, 11.2, 11.3, 11.4, 12.1, 12.2, 12.3, 13.1, 13.2, 42.1, 42.2" diff -Nru oz-0.15.0/oz/ozutil.py oz-0.16.0/oz/ozutil.py --- oz-0.15.0/oz/ozutil.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/ozutil.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2010,2011 Chris Lalancette <clalance@redhat.com> -# Copyright (C) 2012-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2012-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,14 +22,13 @@ import os import random import subprocess -import tempfile import errno import stat import shutil -import pycurl import gzip import time import select +import urllib try: import configparser except ImportError: @@ -37,6 +36,13 @@ import collections import ftplib import struct +import fcntl +import logging +import socket + +import lxml.etree +import monotonic +import requests def generate_full_auto_path(relative): """ @@ -425,7 +431,7 @@ if retcode: cmd = ' '.join(*popenargs) - raise SubprocessException("'%s' failed(%d): %s" % (cmd, retcode, stderr), retcode) + raise SubprocessException("'%s' failed(%d): %s" % (cmd, retcode, stderr+stdout), retcode) return (stdout, stderr, retcode) @@ -674,12 +680,18 @@ cannot help if the system is otherwise very busy, but it does ensure that the problem is not self-inflicted. """ - shutil.rmtree(directory) - fd = os.open(os.path.dirname(directory), os.O_RDONLY) try: - os.fsync(fd) - finally: - os.close(fd) + shutil.rmtree(directory) + fd = os.open(os.path.dirname(directory), os.O_RDONLY) + try: + os.fsync(fd) + finally: + os.close(fd) + except OSError as err: + if err.errno == 2: + pass + else: + raise def parse_config(config_file): """ @@ -699,7 +711,7 @@ # /etc/oz/oz.cfg. If neither of those exist, we don't throw an error # but instead let Oz pick sane defaults internally. parsed = config.read(os.path.expanduser("~/.oz/oz.cfg")) - if len(parsed) == 0: + if len(parsed) == 0 and os.geteuid() == 0: config.read("/etc/oz/oz.cfg") return config @@ -738,6 +750,61 @@ """ return os.path.join(default_data_dir(), "screenshots") +class LocalFileAdapter(requests.adapters.BaseAdapter): + ''' + This class implements an adapter for requests so we can properly deal with file:// + local files. + ''' + @staticmethod + def _chkpath(method, path): + """Return an HTTP status for the given filesystem path.""" + if method.lower() in ('put', 'delete'): + return 501, "Not Implemented" # TODO + elif method.lower() not in ('get', 'head', 'post'): + return 405, "Method Not Allowed" + elif os.path.isdir(path): + return 400, "Path Not A File" + elif not os.path.isfile(path): + return 404, "File Not Found" + elif not os.access(path, os.R_OK): + return 403, "Access Denied" + else: + return 200, "OK" + + def send(self, req, **kwargs): + """Return the file specified by the given request + + @type req: C{PreparedRequest} + @todo: Should I bother filling `response.headers` and processing + If-Modified-Since and friends using `os.stat`? + """ + path = os.path.normcase(os.path.normpath(urllib.url2pathname(req.path_url))) + response = requests.Response() + + response.status_code, response.reason = self._chkpath(req.method, path) + if response.status_code == 200 and req.method.lower() != 'head': + try: + response.raw = open(path, 'rb') + except (OSError, IOError), err: + response.status_code = 500 + response.reason = str(err) + + if isinstance(req.url, bytes): + response.url = req.url.decode('utf-8') + else: + response.url = req.url + + response.headers['Content-Length'] = os.path.getsize(path) + response.headers['Accept-Ranges'] = 'bytes' + response.headers['Redirect-URL'] = req.url + response.request = req + response.connection = self + + return response + + def close(self): + pass + def http_get_header(url, redirect=True): """ Function to get the HTTP headers from a URL. The available headers will be @@ -749,48 +816,15 @@ 'Redirect-URL' will always be None in the redirect=True case, and may be None in the redirect=True case if no redirects were required. """ - info = {} - def _header(buf): - """ - Internal function that is called back from pycurl perform() for - header data. - """ - buf = buf.strip() - if len(buf) == 0: - return - - split = buf.split(':') - if len(split) < 2: - # not a valid header; skip - return - key = split[0].strip() - value = split[1].strip() - info[key] = value - - def _data(buf): - """ - Empty function that is called back from pycurl perform() for body data. - """ - pass - - c = pycurl.Curl() - c.setopt(c.URL, url) - c.setopt(c.NOBODY, True) - c.setopt(c.HEADERFUNCTION, _header) - c.setopt(c.HEADER, True) - c.setopt(c.WRITEFUNCTION, _data) - if redirect: - c.setopt(c.FOLLOWLOCATION, True) - c.perform() - info['HTTP-Code'] = c.getinfo(c.HTTP_CODE) - if info['HTTP-Code'] == 0: - # if this was a file:/// URL, then the HTTP_CODE returned 0. - # set it to 200 to be compatible with http - info['HTTP-Code'] = 200 - if not redirect: - info['Redirect-URL'] = c.getinfo(c.REDIRECT_URL) - - c.close() + with requests.Session() as requests_session: + requests_session.mount('file://', LocalFileAdapter()) + response = requests_session.post(url, allow_redirects=redirect, stream=True, timeout=10) + info = response.headers + info['HTTP-Code'] = response.status_code + if not redirect: + info['Redirect-URL'] = response.headers.get('Location') + else: + info['Redirect-URL'] = None return info @@ -798,45 +832,18 @@ """ Function to download a file from url to file descriptor fd. """ - class Progress(object): - """ - Internal class to represent progress on the connection. This is only - required so that we have somewhere to store the "last_mb" variable - that is not global. - """ - def __init__(self): - self.last_mb = -1 - - def progress(self, down_total, down_current, up_total, up_current): - """ - Function that is called back from the pycurl perform() method to - update the progress information. - """ - if down_total == 0: - return - current_mb = int(down_current) / 10485760 - if current_mb > self.last_mb or down_current == down_total: - self.last_mb = current_mb - logger.debug("%dkB of %dkB" % (down_current/1024, down_total/1024)) - - def _data(buf): - """ - Function that is called back from the pycurl perform() method to - actually write data to disk. - """ - write_bytes_to_fd(fd, buf) - - progress = Progress() - c = pycurl.Curl() - c.setopt(c.URL, url) - c.setopt(c.CONNECTTIMEOUT, 5) - c.setopt(c.WRITEFUNCTION, _data) - c.setopt(c.FOLLOWLOCATION, 1) - if show_progress: - c.setopt(c.NOPROGRESS, 0) - c.setopt(c.PROGRESSFUNCTION, progress.progress) - c.perform() - c.close() + with requests.Session() as requests_session: + requests_session.mount('file://', LocalFileAdapter()) + response = requests_session.get(url, stream=True, allow_redirects=True, + headers={'Accept-Encoding': ''}) + file_size = int(response.headers.get('Content-Length')) + chunk_size = 10*1024*1024 + done = 0 + for chunk in response.iter_content(chunk_size): + write_bytes_to_fd(fd, chunk) + done += len(chunk) + if show_progress: + logger.debug("%dkB of %dkB" % (done / 1024, file_size / 1024)) def ftp_download_directory(server, username, password, basepath, destination): """ @@ -970,3 +977,142 @@ except OSError as err: if err.errno != errno.ENOENT: raise + +def find_uefi_firmware(arch): + ''' + A function to find the UEFI firmware file that corresponds to a certain + architecture. + ''' + # Yuck. Finding the UEFI firmware to start certain guests (like aarch64) + # is a really nasty process. While slightly out of date, this blog post + # describes the mess: http://blog.wikichoon.com/2016/01/uefi-support-in-virt-install-and-virt.html + # Here, we replicate what libguestfs is doing here, which is to essentially + # hardcode paths where UEFI firmware can be found on popular distributions. + # I verified that these files exist on both Fedora/RHEL and Ubuntu. + # Hopefully there will be a nicer way to do this in the future. + class UEFI(object): + ''' + A private class to hold the path to the loader and the path to the + NVRAM file. + ''' + def __init__(self, loader, nvram): + self.loader = loader + self.nvram = nvram + + def exists(self): + ''' + A method that returns True if both the loader and the NVRAM files + exist, False otherwise. + ''' + if os.path.exists(self.loader) and os.path.exists(self.nvram): + return True + return False + + if arch in ['i386', 'i486', 'i586', 'i686']: + uefi_list = [UEFI('/usr/share/edk2.git/ovmf-ia32/OVMF_CODE-pure-efi.fd', + '/usr/share/edk2.git/ovmf-ia32/OVMF_VARS-pure-efi.fd')] + elif arch in ['x86_64']: + uefi_list = [UEFI('/usr/share/OVMF/OVMF_CODE.fd', + '/usr/share/OVMF/OVMF_VARS.fd'), + UEFI('/usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd', + '/usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd')] + elif arch in ['aarch64']: + uefi_list = [UEFI('/usr/share/AAVMF/AAVMF_CODE.fd', + '/usr/share/AAVMF/AAVMF_VARS.fd'), + UEFI('/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw', + '/usr/share/edk2/aarch64/vars-template-pflash.raw'), + UEFI('/usr/share/edk2.git/aarch64/QEMU_EFI-pflash.raw', + '/usr/share/edk2.git/aarch64/vars-template-pflash.raw')] + else: + raise Exception("Invalid arch for UEFI firmware") + + for uefi in uefi_list: + if uefi.exists(): + return uefi.loader, uefi.nvram + + raise Exception("UEFI firmware is not installed!") + +def open_locked_file(filename): + """ + A function to open and lock a file. Returns a file descriptor referencing + the open and locked file. + """ + outdir = os.path.dirname(filename) + mkdir_p(outdir) + + fd = os.open(filename, os.O_RDWR|os.O_CREAT) + + try: + fcntl.lockf(fd, fcntl.LOCK_EX) + except: + os.close(fd) + raise + + return (fd, outdir) + +def lxml_subelement(root, name, text=None, attributes=None): + """ + Function to add a new element to an LXML tree, optionally include text + and a dictionary of attributes. + """ + tmp = lxml.etree.SubElement(root, name) + if text is not None: + tmp.text = text + if attributes is not None: + for k, v in attributes.items(): + tmp.set(k, v) + return tmp + +def timed_loop(max_time, cb, msg, cb_arg=None): + ''' + A function to deal with waiting for an event to occur. Given a + maximum time to wait, a callback, and a message, it will wait until the maximum + time for the event to occur. Each time through the loop, it will do the following: + + 1. Check to see if it has been at least 10 seconds since it last logged. If so, it + will log right now. + 2. Call the callback to check for the event. If the callback returns True, the + loop quits immediately. If it returns False, go on to step 3. + 3. Sleep for the portion of 1 second that was not taken up by the callback. + + If the event occurred (the callback returned True), then this function returns + True. If we timed out while waiting for the event to occur, this function returns + False. + ''' + log = logging.getLogger('%s' % (__name__)) + now = monotonic.monotonic() + end = now + max_time + next_print = now + while now < end: + now = monotonic.monotonic() + if now >= next_print: + log.debug("%s, %d/%d", msg, int(end) - int(now), max_time) + next_print = now + 10 + + if cb(cb_arg): + return True + + # It's possible that the callback took longer than one second. + # In that case, just skip our sleep altogether in an attempt to + # catch up. + sleep_time = 1.0 - (monotonic.monotonic() - now) + if sleep_time > 0: + # Otherwise, sleep for a time. Note that we try to maintain + # on our starting boundary, so we'll sleep less than a second + # here almost always. + time.sleep(sleep_time) + + return False + +def get_free_port(): + """ + A function to find a free TCP port on the host. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # Bind to port 0 which will use a free socket to listen to. + sock.bind(("", 0)) + listen_port = sock.getsockname()[1] + # Close the socket to free it up for libvirt + sock.close() + + return listen_port diff -Nru oz-0.15.0/oz/RedHat.py oz-0.16.0/oz/RedHat.py --- oz-0.15.0/oz/RedHat.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/RedHat.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2010,2011 Chris Lalancette <clalance@redhat.com> -# Copyright (C) 2012-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2012-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -27,9 +27,9 @@ except ImportError: import ConfigParser as configparser import gzip -import guestfs import oz.Guest +import oz.GuestFSManager import oz.Linux import oz.ozutil import oz.OzException @@ -81,6 +81,8 @@ if self.tdl.kernel_param: self.cmdline += " " + self.tdl.kernel_param + self.virtio_channel_name = None + def _generate_new_iso(self): """ Method to create a new ISO based on the modified CD/DVD. @@ -166,7 +168,7 @@ self.log.debug("Teardown step 1") # reset the authorized keys self.log.debug("Resetting authorized_keys") - self._guestfs_path_restore(g_handle, '/root/.ssh') + g_handle.path_restore('/root/.ssh') def _image_ssh_teardown_step_2(self, g_handle): """ @@ -175,7 +177,7 @@ self.log.debug("Teardown step 2") # remove custom sshd_config self.log.debug("Resetting sshd_config") - self._guestfs_path_restore(g_handle, '/etc/ssh/sshd_config') + g_handle.path_restore('/etc/ssh/sshd_config') # reset the service link self.log.debug("Resetting sshd service") @@ -184,7 +186,7 @@ g_handle.rm('/etc/systemd/system/multi-user.target.wants/sshd.service') else: startuplink = self._get_service_runlevel_link(g_handle, 'sshd') - self._guestfs_path_restore(g_handle, startuplink) + g_handle.path_restore(startuplink) def _image_ssh_teardown_step_3(self, g_handle): """ @@ -193,7 +195,7 @@ self.log.debug("Teardown step 3") # reset iptables self.log.debug("Resetting iptables rules") - self._guestfs_path_restore(g_handle, '/etc/sysconfig/iptables') + g_handle.path_restore('/etc/sysconfig/iptables') def _image_ssh_teardown_step_4(self, g_handle): """ @@ -201,16 +203,15 @@ """ self.log.debug("Teardown step 4") self.log.debug("Removing announcement to host") - self._guestfs_remove_if_exists(g_handle, - '/etc/NetworkManager/dispatcher.d/99-reportip') + g_handle.remove_if_exists('/etc/NetworkManager/dispatcher.d/99-reportip') # remove announce cronjob self.log.debug("Resetting announcement to host") - self._guestfs_remove_if_exists(g_handle, '/etc/cron.d/announce') + g_handle.remove_if_exists('/etc/cron.d/announce') # remove reportip self.log.debug("Removing reportip") - self._guestfs_remove_if_exists(g_handle, '/root/reportip') + g_handle.remove_if_exists('/root/reportip') # reset the service link self.log.debug("Resetting crond service") @@ -219,14 +220,14 @@ g_handle.rm('/etc/systemd/system/multi-user.target.wants/crond.service') else: startuplink = self._get_service_runlevel_link(g_handle, 'crond') - self._guestfs_path_restore(g_handle, startuplink) + g_handle.path_restore(startuplink) def _image_ssh_teardown_step_5(self, g_handle): """ Fifth step to undo _image_ssh_setup (reset SELinux). """ self.log.debug("Teardown step 5") - self._guestfs_path_restore(g_handle, "/etc/selinux/config") + g_handle.path_restore("/etc/selinux/config") def _image_ssh_teardown_step_6(self, g_handle): """ @@ -235,19 +236,16 @@ in /etc/ssh. Since this image might be cached later on, this method removes those keys. """ - for f in ["/etc/ssh/ssh_host_dsa_key", "/etc/ssh/ssh_host_dsa_key.pub", - "/etc/ssh/ssh_host_rsa_key", "/etc/ssh/ssh_host_rsa_key.pub", - "/etc/ssh/ssh_host_ecdsa_key", "/etc/ssh/ssh_host_ecdsa_key.pub", - "/etc/ssh/ssh_host_key", "/etc/ssh/ssh_host_key.pub"]: - self._guestfs_remove_if_exists(g_handle, f) + for key in g_handle.glob_expand("/etc/ssh/*_key*"): + g_handle.remove_if_exists(key) # Remove any lease files; this is so that subsequent boots don't try # to connect to a DHCP server that is on a totally different network for lease in g_handle.glob_expand("/var/lib/dhclient/*.leases"): - g_handle.rm_f(lease) + g_handle.rm(lease) for lease in g_handle.glob_expand("/var/lib/NetworkManager/*.lease"): - g_handle.rm_f(lease) + g_handle.rm(lease) def _collect_teardown(self, libvirt_xml): """ @@ -255,7 +253,8 @@ """ self.log.info("Collection Teardown") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() try: self._image_ssh_teardown_step_1(g_handle) @@ -270,7 +269,7 @@ self._image_ssh_teardown_step_6(g_handle) finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() shutil.rmtree(self.icicle_tmp) def _image_ssh_setup_step_1(self, g_handle): @@ -279,10 +278,10 @@ """ # part 1; upload the keys self.log.debug("Step 1: Uploading ssh keys") - self._guestfs_path_backup(g_handle, '/root/.ssh') + g_handle.path_backup('/root/.ssh') g_handle.mkdir('/root/.ssh') - self._guestfs_path_backup(g_handle, '/root/.ssh/authorized_keys') + g_handle.path_backup('/root/.ssh/authorized_keys') self._generate_openssh_key(self.sshprivkey) @@ -305,7 +304,7 @@ '/etc/systemd/system/multi-user.target.wants/sshd.service') else: startuplink = self._get_service_runlevel_link(g_handle, 'sshd') - self._guestfs_path_backup(g_handle, startuplink) + g_handle.path_backup(startuplink) g_handle.ln_sf('/etc/init.d/sshd', startuplink) sshd_config_file = os.path.join(self.icicle_tmp, "sshd_config") @@ -313,7 +312,7 @@ f.write(self.sshd_config) try: - self._guestfs_path_backup(g_handle, '/etc/ssh/sshd_config') + g_handle.path_backup('/etc/ssh/sshd_config') g_handle.upload(sshd_config_file, '/etc/ssh/sshd_config') finally: os.unlink(sshd_config_file) @@ -324,7 +323,7 @@ """ # part 3; open up iptables self.log.debug("Step 3: Open up the firewall") - self._guestfs_path_backup(g_handle, '/etc/sysconfig/iptables') + g_handle.path_backup('/etc/sysconfig/iptables') def _image_ssh_setup_step_4(self, g_handle): """ @@ -334,6 +333,11 @@ # part 4; make sure the guest announces itself self.log.debug("Step 4: Guest announcement") + if self.tdl.arch in ['ppc64', 'ppc64le']: + announce_device = '/dev/hvc1' + else: + announce_device = '/dev/ttyS1' + scriptfile = os.path.join(self.icicle_tmp, "script") if g_handle.exists("/etc/NetworkManager/dispatcher.d"): @@ -342,9 +346,9 @@ #!/bin/bash if [ "$1" = "eth0" -a "$2" = "up" ]; then - echo -n "!$DHCP4_IP_ADDRESS,%s!" > /dev/ttyS1 + echo -n "!$DHCP4_IP_ADDRESS,%s!" > %s fi -""" % (self.uuid)) +""" % (self.uuid, announce_device)) try: g_handle.upload(scriptfile, @@ -364,8 +368,8 @@ [ -z "$DEV" ] && exit 0 ADDR=$(/sbin/ip -4 -o addr show dev $DEV | /bin/awk '{print $4}' | /bin/cut -d/ -f1) && [ -z "$ADDR" ] && exit 0 -echo -n "!$ADDR,%s!" > /dev/ttyS1 -""" % (self.uuid)) +echo -n "!$ADDR,%s!" > %s +""" % (self.uuid, announce_device)) try: g_handle.upload(scriptfile, '/root/reportip') @@ -390,7 +394,7 @@ '/etc/systemd/system/multi-user.target.wants/crond.service') else: startuplink = self._get_service_runlevel_link(g_handle, 'crond') - self._guestfs_path_backup(g_handle, startuplink) + g_handle.path_backup(startuplink) g_handle.ln_sf('/etc/init.d/crond', startuplink) def _image_ssh_setup_step_5(self, g_handle): @@ -400,7 +404,7 @@ # part 5; set SELinux to permissive mode so we don't have to deal with # incorrect contexts self.log.debug("Step 5: Set SELinux to permissive mode") - self._guestfs_path_backup(g_handle, '/etc/selinux/config') + g_handle.path_backup('/etc/selinux/config') selinuxfile = self.icicle_tmp + "/selinux" with open(selinuxfile, 'w') as f: @@ -418,7 +422,8 @@ """ self.log.info("Collection Setup") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() # we have to do 5 things to make sure we can ssh into RHEL/Fedora: # 1) Upload our ssh key @@ -459,7 +464,7 @@ raise finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() def do_icicle(self, guestaddr): """ @@ -564,18 +569,10 @@ outf.writelines(inf) inf.close() - g = guestfs.GuestFS() - g.add_drive_opts(ext2file, format='raw') - self.log.debug("Launching guestfs") - g.launch() - - g.mount_options('', g.list_devices()[0], "/") - - g.upload(kspath, "/ks.cfg") - - g.sync() - g.umount_all() - g.kill_subprocess() + g_handle = oz.GuestFSManager.GuestFS(ext2file, 'raw') + g_handle.mount_partitions() + g_handle.upload(kspath, "/ks.cfg") + g_handle.cleanup() # kickstart is added, lets recompress it oz.ozutil.gzip_create(ext2file, self.initrdfname) @@ -606,7 +603,7 @@ # hard-coded path initrd = "images/pxeboot/initrd.img" - (fd, outdir) = self._open_locked_file(self.kernelcache) + (fd, outdir) = oz.ozutil.open_locked_file(self.kernelcache) try: self._get_original_media('/'.join([self.url.rstrip('/'), @@ -618,7 +615,7 @@ finally: os.close(fd) - (fd, outdir) = self._open_locked_file(self.initrdcache) + (fd, outdir) = oz.ozutil.open_locked_file(self.initrdcache) try: try: @@ -702,12 +699,22 @@ Method to run the operating system installation. """ return self._do_install(timeout, force, 0, self.kernelfname, - self.initrdfname, self.cmdline) + self.initrdfname, self.cmdline, None, + self.virtio_channel_name) class RedHatLinuxCDYumGuest(RedHatLinuxCDGuest): """ Class for RedHat-based CD guests with yum support. """ + def __init__(self, tdl, config, auto, output_disk, nicmodel, diskbus, + iso_allowed, url_allowed, initrdtype, macaddress, use_yum): + oz.RedHat.RedHatLinuxCDGuest.__init__(self, tdl, config, auto, + output_disk, nicmodel, diskbus, + iso_allowed, url_allowed, + initrdtype, macaddress) + + self.use_yum = use_yum + def _check_url(self, iso=True, url=True): """ Method to check if a URL specified by the user is one that will work @@ -804,7 +811,10 @@ os.unlink(localname) def _install_packages(self, guestaddr, packstr): - self.guest_execute_command(guestaddr, 'yum -y install %s' % (packstr)) + if self.use_yum: + self.guest_execute_command(guestaddr, 'yum -y install %s' % (packstr)) + else: + self.guest_execute_command(guestaddr, 'dnf -y install %s' % (packstr)) def _remove_repos(self, guestaddr): for repo in list(self.tdl.repositories.values()): @@ -904,7 +914,7 @@ return # name of the output file - (fd, outdir) = self._open_locked_file(self.orig_floppy) + (fd, outdir) = oz.ozutil.open_locked_file(self.orig_floppy) try: self._get_original_floppy(self.url + "/images/bootnet.img", fd, diff -Nru oz-0.15.0/oz/RHEL_5.py oz-0.16.0/oz/RHEL_5.py --- oz-0.15.0/oz/RHEL_5.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/RHEL_5.py 2017-08-08 18:46:43.000000000 +0000 @@ -34,7 +34,8 @@ macaddress=None): oz.RedHat.RedHatLinuxCDYumGuest.__init__(self, tdl, config, auto, output_disk, nicmodel, diskbus, - True, True, "cpio", macaddress) + True, True, "cpio", macaddress, + True) def _modify_iso(self): """ diff -Nru oz-0.15.0/oz/RHEL_6.py oz-0.16.0/oz/RHEL_6.py --- oz-0.15.0/oz/RHEL_6.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/RHEL_6.py 2017-08-08 18:46:43.000000000 +0000 @@ -33,7 +33,8 @@ diskbus=None, macaddress=None): oz.RedHat.RedHatLinuxCDYumGuest.__init__(self, tdl, config, auto, output_disk, netdev, diskbus, - True, True, "cpio", macaddress) + True, True, "cpio", macaddress, + True) def _modify_iso(self): """ @@ -71,4 +72,4 @@ """ Return supported versions as a string. """ - return "RHEL/OL/CentOS/Scientific Linux{,CERN} 6: 0, 1, 2, 3, 4, 5, 6, 7" + return "RHEL/OL/CentOS/Scientific Linux{,CERN} 6: 0, 1, 2, 3, 4, 5, 6, 7, 8" diff -Nru oz-0.15.0/oz/RHEL_7.py oz-0.16.0/oz/RHEL_7.py --- oz-0.15.0/oz/RHEL_7.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/RHEL_7.py 2017-08-08 18:46:43.000000000 +0000 @@ -33,7 +33,8 @@ diskbus=None, macaddress=None): oz.RedHat.RedHatLinuxCDYumGuest.__init__(self, tdl, config, auto, output_disk, netdev, diskbus, - True, True, "cpio", macaddress) + True, True, "cpio", macaddress, + True) def _modify_iso(self): """ @@ -74,4 +75,4 @@ """ Return supported versions as a string. """ - return "RHEL 7: Beta, 0, 1" + return "RHEL 7: Beta, 0, 1, 2" diff -Nru oz-0.15.0/oz/TDL.py oz-0.16.0/oz/TDL.py --- oz-0.15.0/oz/TDL.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/TDL.py 2017-08-08 18:46:43.000000000 +0000 @@ -19,11 +19,10 @@ Template Description Language (TDL) """ -import lxml.etree import base64 +import os import re import tempfile -import os try: import urllib.parse as urlparse except ImportError: @@ -33,6 +32,8 @@ except ImportError: from io import StringIO +import lxml.etree + import oz.ozutil import oz.OzException @@ -205,7 +206,7 @@ self.arch = _xml_get_value(self.doc, '/template/os/arch', 'OS architecture') - if self.arch not in [ "i386", "x86_64", "ppc64", "ppc64le", "aarch64", "armv7l" ]: + if self.arch not in ["i386", "x86_64", "ppc64", "ppc64le", "aarch64", "armv7l"]: raise oz.OzException.OzException("Architecture must be one of 'i386, x86_64, ppc64, ppc64le, armv7l, or aarch64'") self.key = _xml_get_value(self.doc, '/template/os/key', 'OS key', @@ -299,7 +300,7 @@ optional=True) self.kernel_param = _xml_get_value(self.doc, - '/template/os/kernelparam', + '/template/os/kernelparam', 'custom kernel parameter', optional=True) diff -Nru oz-0.15.0/oz/tdl.rng oz-0.16.0/oz/tdl.rng --- oz-0.15.0/oz/tdl.rng 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/tdl.rng 2017-08-08 18:46:43.000000000 +0000 @@ -48,26 +48,26 @@ </choice> <optional> <element name='extras'> - <zeroOrMore> - <element name='directory'> - <attribute name='source'> - <text/> - </attribute> - <attribute name='destination'> - <text/> - </attribute> - </element> - </zeroOrMore> - <zeroOrMore> - <element name='file'> - <attribute name='source'> - <text/> - </attribute> - <attribute name='destination'> - <text/> - </attribute> - </element> - </zeroOrMore> + <oneOrMore> + <choice> + <element name='directory'> + <attribute name='source'> + <text/> + </attribute> + <attribute name='destination'> + <text/> + </attribute> + </element> + <element name='file'> + <attribute name='source'> + <text/> + </attribute> + <attribute name='destination'> + <text/> + </attribute> + </element> + </choice> + </oneOrMore> </element> </optional> </element> diff -Nru oz-0.15.0/oz/Ubuntu.py oz-0.16.0/oz/Ubuntu.py --- oz-0.15.0/oz/Ubuntu.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Ubuntu.py 2017-08-08 18:46:43.000000000 +0000 @@ -1,5 +1,5 @@ # Copyright (C) 2010,2011 Chris Lalancette <clalance@redhat.com> -# Copyright (C) 2012-2016 Chris Lalancette <clalancette@gmail.com> +# Copyright (C) 2012-2017 Chris Lalancette <clalancette@gmail.com> # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -24,13 +24,14 @@ import os import gzip +import oz.GuestFSManager import oz.Linux import oz.ozutil import oz.OzException class UbuntuGuest(oz.Linux.LinuxCDGuest): """ - Class for Ubuntu 5.04, 5.10, 6.06, 6.10, 7.04, 7.10, 8.04, 8.10, 9.04, 9.10, 10.04, 10.10, 11.04, 11.10, 12.04, 12.10, 13.04, 13.10, 14.04, 14.10, 15.04, and 15.10 installation. + Class for Ubuntu 5.04, 5.10, 6.06, 6.10, 7.04, 7.10, 8.04, 8.10, 9.04, 9.10, 10.04, 10.10, 11.04, 11.10, 12.04, 12.10, 13.04, 13.10, 14.04, 14.10, 15.04, 15.10, 16.04, 16.10, and 17.04 installation. """ def __init__(self, tdl, config, auto, output_disk, initrd, nicmodel, diskbus, macaddress): @@ -74,6 +75,8 @@ self.tdl.distro + self.tdl.update + self.tdl.arch + "-ramdisk") self.cmdline = "priority=critical locale=en_US" + if self.tdl.kernel_param: + self.cmdline += " " + self.tdl.kernel_param self.reboots = 0 if self.tdl.update in ["5.04", "5.10"]: @@ -172,7 +175,9 @@ kernelname = "/casper/vmlinuz" if self.tdl.update in ["12.04.2", "12.04.3", "12.04.4", "12.04.5", "13.04", "13.10", "14.04", - "14.04.1", "14.10", "15.04", "15.10"] and self.tdl.arch == "x86_64": + "14.04.1", "14.04.2", "14.04.3", "14.04.4", + "14.04.5", "14.10", "15.04", "15.10", + "16.04", "16.04.1", "16.10", "17.04"] and self.tdl.arch == "x86_64": kernelname += ".efi" f.write(" kernel " + kernelname + "\n") f.write(" append file=/cdrom/preseed/customiso.seed boot=casper automatic-ubiquity noprompt keyboard-configuration/layoutcode=us initrd=/casper/" + self.casper_initrd + "\n") @@ -183,7 +188,6 @@ f.write(" kernel /install/vmlinuz\n") f.write(" append preseed/file=/cdrom/preseed/customiso.seed debian-installer/locale=en_US " + keyboard + " netcfg/choose_interface=auto keyboard-configuration/layoutcode=us priority=critical initrd=/install/initrd.gz --\n") - def get_auto_path(self): """ Method to create the correct path to the Ubuntu preseed files. @@ -245,7 +249,7 @@ self.log.debug("Teardown step 1") # reset the authorized keys self.log.debug("Resetting authorized_keys") - self._guestfs_path_restore(g_handle, '/root/.ssh') + g_handle.path_restore('/root/.ssh') def _image_ssh_teardown_step_2(self, g_handle): """ @@ -254,12 +258,12 @@ self.log.debug("Teardown step 2") # remove custom sshd_config self.log.debug("Resetting sshd_config") - self._guestfs_path_restore(g_handle, '/etc/ssh/sshd_config') + g_handle.path_restore('/etc/ssh/sshd_config') # reset the service link self.log.debug("Resetting sshd service") if self.ssh_startuplink: - self._guestfs_path_restore(g_handle, self.ssh_startuplink) + g_handle.path_restore(self.ssh_startuplink) def _image_ssh_teardown_step_3(self, g_handle): """ @@ -268,19 +272,18 @@ self.log.debug("Teardown step 3") # remove announce cronjob self.log.debug("Resetting announcement to host") - self._guestfs_remove_if_exists(g_handle, - '/etc/NetworkManager/dispatcher.d/99-reportip') + g_handle.remove_if_exists('/etc/NetworkManager/dispatcher.d/99-reportip') - self._guestfs_remove_if_exists(g_handle, '/etc/cron.d/announce') + g_handle.remove_if_exists('/etc/cron.d/announce') # remove reportip self.log.debug("Removing reportip") - self._guestfs_remove_if_exists(g_handle, '/root/reportip') + g_handle.remove_if_exists('/root/reportip') # reset the service link self.log.debug("Resetting cron service") if self.cron_startuplink: - self._guestfs_path_restore(g_handle, self.cron_startuplink) + g_handle.path_restore(self.cron_startuplink) def _image_ssh_teardown_step_4(self, g_handle): """ @@ -294,12 +297,12 @@ "/etc/ssh/ssh_host_ecdsa_key", "/etc/ssh/ssh_host_ecdsa_key.pub", "/etc/ssh/ssh_host_ed25519_key", "/etc/ssh/ssh_host_ed25519_key.pub", "/etc/ssh/ssh_host_key", "/etc/ssh/ssh_host_key.pub"]: - self._guestfs_remove_if_exists(g_handle, f) + g_handle.remove_if_exists(f) # Remove any lease files; this is so that subsequent boots don't try # to connect to a DHCP server that is on a totally different network for lease in g_handle.glob_expand("/var/lib/dhcp/*.leases"): - g_handle.rm_f(lease) + g_handle.rm(lease) def _image_ssh_setup_step_1(self, g_handle): """ @@ -307,10 +310,10 @@ """ # part 1; upload the keys self.log.debug("Step 1: Uploading ssh keys") - self._guestfs_path_backup(g_handle, '/root/.ssh') + g_handle.path_backup('/root/.ssh') g_handle.mkdir('/root/.ssh') - self._guestfs_path_backup(g_handle, '/root/.ssh/authorized_keys') + g_handle.path_backup('/root/.ssh/authorized_keys') self._generate_openssh_key(self.sshprivkey) @@ -326,7 +329,7 @@ raise oz.OzException.OzException("ssh not installed on the image, cannot continue") self.ssh_startuplink = self._get_service_runlevel_link(g_handle, 'ssh') - self._guestfs_path_backup(g_handle, self.ssh_startuplink) + g_handle.path_backup(self.ssh_startuplink) g_handle.ln_sf('/etc/init.d/ssh', self.ssh_startuplink) sshd_config_file = os.path.join(self.icicle_tmp, "sshd_config") @@ -334,7 +337,7 @@ f.write(self.sshd_config) try: - self._guestfs_path_backup(g_handle, '/etc/ssh/sshd_config') + g_handle.path_backup('/etc/ssh/sshd_config') g_handle.upload(sshd_config_file, '/etc/ssh/sshd_config') finally: os.unlink(sshd_config_file) @@ -398,7 +401,7 @@ self.cron_startuplink = self._get_service_runlevel_link(g_handle, 'cron') - self._guestfs_path_backup(g_handle, self.cron_startuplink) + g_handle.path_backup(self.cron_startuplink) g_handle.ln_sf('/etc/init.d/cron', self.cron_startuplink) def _collect_setup(self, libvirt_xml): @@ -407,7 +410,8 @@ """ self.log.info("Collection Setup") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() # we have to do 3 things to make sure we can ssh into Ubuntu # 1) Upload our ssh key @@ -435,7 +439,7 @@ raise finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() def _collect_teardown(self, libvirt_xml): """ @@ -443,7 +447,8 @@ """ self.log.info("Collection Teardown") - g_handle = self._guestfs_handle_setup(libvirt_xml) + g_handle = oz.GuestFSManager.GuestFSLibvirtFactory(libvirt_xml, self.libvirt_conn) + g_handle.mount_partitions() try: self._image_ssh_teardown_step_1(g_handle) @@ -454,7 +459,7 @@ self._image_ssh_teardown_step_4(g_handle) finally: - self._guestfs_handle_cleanup(g_handle) + g_handle.cleanup() shutil.rmtree(self.icicle_tmp) def _customize_repos(self, guestaddr): @@ -552,7 +557,7 @@ # there is a bit of asymmetry here in that OSs that support cpio # archives have the initial initrdfname copied in the higher level # function, but we delete it here. OSs that don't support cpio, - # though, get the initrd created right here. C'est le vie + # though, get the initrd created right here. C'est la vie os.unlink(self.initrdfname) raise @@ -596,7 +601,7 @@ # hard-coded path initrd = "ubuntu-installer/%s/initrd.gz" % (self.debarch) - (fd, outdir) = self._open_locked_file(self.kernelcache) + (fd, outdir) = oz.ozutil.open_locked_file(self.kernelcache) try: self._get_original_media('/'.join([self.url.rstrip('/'), @@ -608,7 +613,7 @@ finally: os.close(fd) - (fd, outdir) = self._open_locked_file(self.initrdcache) + (fd, outdir) = oz.ozutil.open_locked_file(self.initrdcache) try: try: @@ -706,7 +711,9 @@ if tdl.update in ["9.10", "10.04", "10.04.1", "10.04.2", "10.04.3", "10.10", "11.04", "11.10", "12.04", "12.04.1", "12.04.2", "12.04.3", "12.04.4", "12.04.5", "12.10", "13.04", - "13.10", "14.04", "14.04.1", "14.10", "15.04", "15.10"]: + "13.10", "14.04", "14.04.1", "14.04.2", "14.04.3", "14.04.4", + "14.04.5", "14.10", "15.04", "15.10", + "16.04", "16.04.1", "16.10", "17.04"]: if netdev is None: netdev = 'virtio' if diskbus is None: @@ -718,4 +725,4 @@ """ Return supported versions as a string. """ - return "Ubuntu: 5.04, 5.10, 6.06[.1,.2], 6.10, 7.04, 7.10, 8.04[.1,.2,.3,.4], 8.10, 9.04, 9.10, 10.04[.1,.2,.3], 10.10, 11.04, 11.10, 12.04[.1,.2,.3,.4,.5], 12.10, 13.04, 13.10, 14.04[.1], 14.10, 15.04, 15.10" + return "Ubuntu: 5.04, 5.10, 6.06[.1,.2], 6.10, 7.04, 7.10, 8.04[.1,.2,.3,.4], 8.10, 9.04, 9.10, 10.04[.1,.2,.3], 10.10, 11.04, 11.10, 12.04[.1,.2,.3,.4,.5], 12.10, 13.04, 13.10, 14.04[.1,.2,.3,.4,.5], 14.10, 15.04, 15.10, 16.04[.1], 16.10, 17.04" diff -Nru oz-0.15.0/oz/Windows.py oz-0.16.0/oz/Windows.py --- oz-0.15.0/oz/Windows.py 2016-02-28 22:18:55.000000000 +0000 +++ oz-0.16.0/oz/Windows.py 2017-08-08 18:46:43.000000000 +0000 @@ -19,12 +19,13 @@ Windows installation """ +import os import random import re -import os -import lxml.etree import shutil +import lxml.etree + import oz.Guest import oz.ozutil import oz.OzException @@ -231,6 +232,89 @@ internal_timeout = 8500 return self._do_install(internal_timeout, force, 2) +class Windows_v10(Windows): + """ + Class for Windows versions based on kernel 10.x (2016 and 10). + """ + def __init__(self, tdl, config, auto, output_disk, netdev, diskbus, + macaddress): + Windows.__init__(self, tdl, config, auto, output_disk, netdev, diskbus, + macaddress) + + self.winarch = "x86" + if self.tdl.arch == "x86_64": + self.winarch = "amd64" + + def _generate_new_iso(self): + """ + Method to create a new ISO based on the modified CD/DVD. + """ + self.log.debug("Generating new ISO") + oz.ozutil.subprocess_check_output(["genisoimage", + "-b", "cdboot/boot.bin", + "-no-emul-boot", "-c", "BOOT.CAT", + "-iso-level", "2", "-J", "-l", "-D", + "-N", "-joliet-long", "-allow-limited-size", + "-relaxed-filenames", "-v", + "-V", "Custom", "-udf", + "-o", self.output_iso, + self.iso_contents], + printfn=self.log.debug) + + def _modify_iso(self): + """ + Method to make the boot ISO auto-boot with appropriate parameters. + """ + self.log.debug("Modifying ISO") + + os.mkdir(os.path.join(self.iso_contents, "cdboot")) + self._geteltorito(self.orig_iso, os.path.join(self.iso_contents, + "cdboot", "boot.bin")) + + outname = os.path.join(self.iso_contents, "autounattend.xml") + + if self.default_auto_file(): + # if this is the oz default unattend file, we modify certain + # parameters to make installation succeed + doc = lxml.etree.parse(self.auto) + + for component in doc.xpath('/ms:unattend/ms:settings/ms:component', + namespaces={'ms':'urn:schemas-microsoft-com:unattend'}): + component.set('processorArchitecture', self.winarch) + + keys = doc.xpath('/ms:unattend/ms:settings/ms:component/ms:ProductKey', + namespaces={'ms':'urn:schemas-microsoft-com:unattend'}) + if len(keys) != 1: + raise oz.OzException.OzException("Invalid autounattend file; expected 1 key, saw %d" % (len(keys))) + keys[0].text = self.tdl.key + + adminpw = doc.xpath('/ms:unattend/ms:settings/ms:component/ms:UserAccounts/ms:AdministratorPassword/ms:Value', + namespaces={'ms':'urn:schemas-microsoft-com:unattend'}) + if len(adminpw) != 1: + raise oz.OzException.OzException("Invalid autounattend file; expected 1 admin password, saw %d" % (len(adminpw))) + adminpw[0].text = self.rootpw + + autologinpw = doc.xpath('/ms:unattend/ms:settings/ms:component/ms:AutoLogon/ms:Password/ms:Value', + namespaces={'ms':'urn:schemas-microsoft-com:unattend'}) + if len(autologinpw) != 1: + raise oz.OzException.OzException("Invalid autounattend file; expected 1 auto logon password, saw %d" % (len(autologinpw))) + autologinpw[0].text = self.rootpw + + f = open(outname, 'w') + f.write(lxml.etree.tostring(doc, pretty_print=True)) + f.close() + else: + # if the user provided their own unattend file, do not override + # their choices; the user gets to keep both pieces if something + # breaks + shutil.copy(self.auto, outname) + + def install(self, timeout=None, force=False): + internal_timeout = timeout + if internal_timeout is None: + internal_timeout = 8500 + return self._do_install(internal_timeout, force, 2) + def get_class(tdl, config, auto, output_disk=None, netdev=None, diskbus=None, macaddress=None): """ @@ -242,9 +326,12 @@ if tdl.update in ["2008", "7", "2012", "8", "8.1"]: return Windows_v6(tdl, config, auto, output_disk, netdev, diskbus, macaddress) + if tdl.update in ["2016", "10"]: + return Windows_v10(tdl, config, auto, output_disk, netdev, diskbus, + macaddress) def get_supported_string(): """ Return supported versions as a string. """ - return "Windows: 2000, XP, 2003, 7, 2008, 2012, 8, 8.1" + return "Windows: 2000, XP, 2003, 7, 2008, 2012, 8, 8.1, 2016, 10" diff -Nru oz-0.15.0/oz.cfg oz-0.16.0/oz.cfg --- oz-0.15.0/oz.cfg 2014-12-30 02:22:58.000000000 +0000 +++ oz-0.16.0/oz.cfg 2017-08-08 18:46:43.000000000 +0000 @@ -19,3 +19,9 @@ [icicle] safe_generation = no + +[timeouts] +install = 1200 +inactivity = 300 +boot = 300 +shutdown = 90 diff -Nru oz-0.15.0/oz.spec.in oz-0.16.0/oz.spec.in --- oz-0.15.0/oz.spec.in 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/oz.spec.in 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,148 @@ +Summary: Library and utilities for automated guest OS installs +Name: oz +Version: @VERSION@ +Release: @RELEASE@%{?dist} +License: LGPLv2 +Group: Development/Libraries +URL: http://github.com/clalancette/oz +Source0: http://github.com/clalancette/%{name}/archive/%{name}-%{version}.tar.gz +BuildArch: noarch +Requires: python >= 2.5 +Requires: python-libguestfs >= 1.18 +Requires: python-lxml +Requires: libvirt-python >= 0.9.7 +# in theory, oz doesn't really require libvirtd to be local to operate +# properly. However, because of the libguestfs manipulations, in practice +# it really does. Make it depend on libvirt (so we get libvirtd) for now, +# unless/until we are able to make it really be remote. +%if 0%{?fedora} >= 17 +Requires: libvirt-daemon-kvm +Requires: libvirt-daemon-qemu +Requires: libvirt-daemon-config-network +%else +Requires: libvirt >= 0.9.7 +%endif +Requires: python-requests +Requires: genisoimage +Requires: mtools +%if 0%{?fedora} < 26 +Requires: python-uuid +%endif +Requires: openssh-clients +Requires: m2crypto +Requires: python-monotonic + +BuildRequires: python + +%description +Oz is a set of libraries and utilities for doing automated guest OS +installations, with minimal input from the user. + +%prep +%setup -q + +%build +python setup.py build + +%install +python setup.py install --root=$RPM_BUILD_ROOT --skip-build + +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/isocontent/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/isos/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/floppycontent/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/floppies/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/icicletmp/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/jeos/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/kernels/ +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/oz/screenshots/ + +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/oz +cp oz.cfg $RPM_BUILD_ROOT%{_sysconfdir}/oz + +%post +if [ ! -f %{_sysconfdir}/oz/id_rsa-icicle-gen ]; then + ssh-keygen -t rsa -b 2048 -N "" -f %{_sysconfdir}/oz/id_rsa-icicle-gen >& /dev/null +fi + +%files +%doc README COPYING examples +%dir %attr(0755, root, root) %{_sysconfdir}/oz/ +%config(noreplace) %{_sysconfdir}/oz/oz.cfg +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/isocontent/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/isos/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/floppycontent/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/floppies/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/icicletmp/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/jeos/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/kernels/ +%dir %attr(0755, root, root) %{_localstatedir}/lib/oz/screenshots/ +%{python_sitelib}/oz +%{_bindir}/oz-install +%{_bindir}/oz-generate-icicle +%{_bindir}/oz-customize +%{_bindir}/oz-cleanup-cache +%{python_sitelib}/oz-*.egg-info +%{_mandir}/man1/* + +%changelog +* Tue Aug 08 2016 Chris Lalancette <clalancette@gmail.com> - 0.16.0-1 +- Release 0.16.0 + +* Sun Feb 28 2016 Chris Lalancette <clalancette@gmail.com> - 0.15.0-1 +- Release 0.15.0 + +* Fri Jun 26 2015 Chris Lalancette <clalancette@gmail.com> - 0.14.0-1 +- Release 0.14.0 + +* Sat Mar 7 2015 Chris Lalancette <clalancette@gmail.com> - 0.13.0-1 +- Release 0.13.0 + +* Wed Jan 1 2014 Chris Lalancette <clalancette@gmail.com> - 0.12.0-1 +- Release 0.12.0 + +* Sun Jul 28 2013 Chris Lalancette <clalancette@gmail.com> - 0.11.0-1 +- Release 0.11.0 + +* Sat Mar 09 2013 Chris Lalancette <clalancette@gmail.com> - 0.10.0-1 +- Release 0.10.0 + +* Tue Jul 17 2012 Chris Lalancette <clalancette@gmail.com> - 0.9.0-1 +- Release 0.9.0 + +* Wed Jan 11 2012 Chris Lalancette <clalancette@gmail.com> - 0.8.0-1 +- Release 0.8.0 + +* Mon Sep 12 2011 Chris Lalancette <clalance@redhat.com> - 0.7.0-1 +- Release 0.7.0 + +* Wed Aug 17 2011 Chris Lalancette <clalance@redhat.com> - 0.6.0-1 +- Release 0.6.0 + +* Wed Jun 29 2011 Chris Lalancette <clalance@redhat.com> - 0.5.0-1 +- Release 0.5.0 + +* Mon Jun 20 2011 Pádraig Brady <P@draigBrady.com> - 0.4.0-4 +- Include examples/. + +* Wed Jun 15 2011 Pádraig Brady <P@draigBrady.com> - 0.4.0-3 +- Address rpmlint issues. + +* Fri Jun 10 2011 Pádraig Brady <P@draigBrady.com> - 0.4.0-2 +- Change to noarch. + +* Tue May 24 2011 Chris Lalancette <clalance@redhat.com> - 0.4.0-1 +- Release 0.4.0. + +* Wed Mar 30 2011 Chris Lalancette <clalance@redhat.com> - 0.3.0-1 +- Release 0.3.0. + +* Wed Mar 16 2011 Chris Lalancette <clalance@redhat.com> - 0.2.0-1 +- Release 0.2.0. + +* Thu Feb 3 2011 Chris Lalancette <clalance@redhat.com> - 0.1.0-1 +- Initial public release of Oz. + +* Wed Nov 3 2010 Chris Lalancette <clalance@redhat.com> - 0.0.4-1 +- Initial build. diff -Nru oz-0.15.0/PKG-INFO oz-0.16.0/PKG-INFO --- oz-0.15.0/PKG-INFO 2016-02-28 23:07:01.000000000 +0000 +++ oz-0.16.0/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -Metadata-Version: 1.0 -Name: oz -Version: 0.15.0 -Summary: Oz automated installer -Home-page: http://github.com/clalancette/oz -Author: Chris Lalancette -Author-email: clalancette@gmail.com -License: LGPLv2 -Description: UNKNOWN -Platform: UNKNOWN diff -Nru oz-0.15.0/pylint.conf oz-0.16.0/pylint.conf --- oz-0.15.0/pylint.conf 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/pylint.conf 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,217 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add <file or directory> to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +disable=C0325,C0103 + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. +generated-members=REQUEST,acl_users,aq_parent + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=200 + +# Maximum number of lines in a module +max-module-lines=2000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9_]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-zA-Z0-9_]{0,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-zA-Z0-9_]{0,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=10 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp diff -Nru oz-0.15.0/requirements.txt oz-0.16.0/requirements.txt --- oz-0.15.0/requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/requirements.txt 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,4 @@ +requests +m2crypto +libvirt-python +lxml diff -Nru oz-0.15.0/setup.py oz-0.16.0/setup.py --- oz-0.15.0/setup.py 2016-02-28 22:57:07.000000000 +0000 +++ oz-0.16.0/setup.py 2017-08-08 18:46:43.000000000 +0000 @@ -3,7 +3,7 @@ import subprocess import time -VERSION = '0.15.0' +VERSION = '0.16.0' RELEASE = '1' datafiles = [('share/man/man1', ['man/oz-install.1', 'man/oz-generate-icicle.1', diff -Nru oz-0.15.0/tests/factory/test_factory.py oz-0.16.0/tests/factory/test_factory.py --- oz-0.15.0/tests/factory/test_factory.py 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/factory/test_factory.py 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,306 @@ +#!/usr/bin/python + +from __future__ import print_function +import sys +try: + import configparser +except ImportError: + import ConfigParser as configparser +try: + from io import StringIO, BytesIO +except: + from StringIO import StringIO + BytesIO = StringIO +import logging +import os + +# Find oz library +prefix = '.' +for i in range(0,3): + if os.path.isdir(os.path.join(prefix, 'oz')): + sys.path.insert(0, prefix) + break + else: + prefix = '../' + prefix + +try: + import oz.TDL + import oz.GuestFactory +except ImportError as e: + print(e) + print('Unable to import oz. Is oz installed or in your PYTHONPATH?') + sys.exit(1) + +try: + import py.test +except ImportError: + print('Unable to import py.test. Is py.test installed?') + sys.exit(1) + +# Define a list to collect all tests +alltests = list() + +# Define an object to record test results +class TestResult(object): + def __init__(self, *args, **kwargs): + if len(args) == 4: + (self.distro, self.version, self.arch, self.installtype) = args + for k,v in list(kwargs.items()): + setattr(self, k, v) + + def __repr__(self): + '''String representation of object''' + return "test-{0}-{1}-{2}-{3}".format(*self.test_args()) + + @property + def name(self): + '''Convenience property for test name''' + return self.__repr__() + + def test_args(self): + return (self.distro, self.version, self.arch, self.installtype) + + def execute(self): + if self.expect_pass: + return (self.name, runtest, self.test_args()) + else: + return (self.name, handle_exception, self.test_args()) + +def default_route(): + route_file = "/proc/net/route" + d = file(route_file) + + for line in d: + info = line.split() + if (len(info) != 11): # 11 = typical num of fields in the file + logging.warn(_("Invalid line length while parsing %s.") % + (route_file)) + break + try: + route = int(info[1], 16) + if route == 0: + return info[0] + except ValueError: + continue + raise Exception("Could not find default route") + +# we find the default route for this machine. Note that this very well +# may not be a bridge, but for the purposes of testing the factory, it +# doesn't really matter; it just has to have an IP address +route = default_route() + +def runtest(args): + (distro, version, arch, installtype) = args + print("Testing %s-%s-%s-%s..." % (distro, version, arch, installtype), end=' ') + + tdlxml = """ +<template> + <name>tester</name> + <os> + <name>%s</name> + <version>%s</version> + <arch>%s</arch> + <install type='%s'> + <%s>http://example.org</%s> + </install> + <key>1234</key> + </os> +</template> +""" % (distro, version, arch, installtype, installtype, installtype) + + tdl = oz.TDL.TDL(tdlxml) + + print(route) + config = configparser.SafeConfigParser() + config.readfp(BytesIO("[libvirt]\nuri=qemu:///session\nbridge_name=%s" % route)) + + if os.getenv('DEBUG') is not None: + logging.basicConfig(level=logging.DEBUG, format="%(message)s") + else: + logging.basicConfig(level=logging.ERROR, format="%(message)s") + + oz.GuestFactory.guest_factory(tdl, config, None) + +def expect_success(*args): + '''Create a TestResult object using provided arguments. Append result to + global 'alltests' list.''' + global alltests + alltests.append(TestResult(*args, expect_pass=True)) + +def expect_fail(*args): + '''Create a TestResult object using provided arguments. Append result to + global 'alltests' list.''' + global alltests + alltests.append(TestResult(*args, expect_pass=False)) + +def handle_exception(args): + '''Helper method to capture OzException when executing 'runtest'.''' + with py.test.raises(oz.OzException.OzException): + runtest(args) + +def test_all(): + + # bad distro + expect_fail("foo", "1", "i386", "url") + # bad installtype + expect_fail("Fedora", "14", "i386", "dong") + # bad arch + expect_fail("Fedora", "14", "ia64", "iso") + + # FedoraCore + for version in ["1", "2", "3", "4", "5", "6"]: + for arch in ["i386", "x86_64"]: + for installtype in ["url", "iso"]: + expect_success("FedoraCore", version, arch, installtype) + # bad FedoraCore version + expect_fail("FedoraCore", "24", "x86_64", "iso") + + # Fedora + for version in ["7", "8", "9", "10", "11", "12", "13", "14", "15", "16", + "17", "18", "19", "20", "21", "22", "23"]: + for arch in ["i386", "x86_64"]: + for installtype in ["url", "iso"]: + expect_success("Fedora", version, arch, installtype) + + # RHL + for version in ["7.0", "7.1", "7.2", "7.3", "8", "9"]: + expect_success("RHL", version, "i386", "url") + # bad RHL version + expect_fail("RHL", "10", "i386", "url") + # bad RHL arch + expect_fail("RHL", "9", "x86_64", "url") + # bad RHL installtype + expect_fail("RHL", "9", "x86_64", "iso") + + # RHEL-2.1 + for version in ["GOLD", "U2", "U3", "U4", "U5", "U6"]: + expect_success("RHEL-2.1", version, "i386", "url") + # bad RHEL-2.1 version + expect_fail("RHEL-2.1", "U7", "i386", "url") + # bad RHEL-2.1 arch + expect_fail("RHEL-2.1", "U6", "x86_64", "url") + # bad RHEL-2.1 installtype + expect_fail("RHEL-2.1", "U6", "i386", "iso") + + # RHEL-3/CentOS-3 + for distro in ["RHEL-3", "CentOS-3"]: + for version in ["GOLD", "U1", "U2", "U3", "U4", "U5", "U6", "U7", "U8", + "U9"]: + for arch in ["i386", "x86_64"]: + expect_success(distro, version, arch, "url") + # bad RHEL-3 version + expect_fail("RHEL-3", "U10", "x86_64", "url") + # invalid RHEL-3 installtype + expect_fail("RHEL-3", "U9", "x86_64", "iso") + + # RHEL-4/CentOS-4 + for distro in ["RHEL-4", "CentOS-4", "ScientificLinux-4"]: + for version in ["GOLD", "U1", "U2", "U3", "U4", "U5", "U6", "U7", "U8", + "U9"]: + for arch in ["i386", "x86_64"]: + for installtype in ["url", "iso"]: + expect_success(distro, version, arch, installtype) + # bad RHEL-4 version + expect_fail("RHEL-4", "U10", "x86_64", "url") + + # RHEL-5/CentOS-5 + for distro in ["RHEL-5", "CentOS-5", "ScientificLinux-5"]: + for version in ["GOLD", "U1", "U2", "U3", "U4", "U5", "U6", "U7", "U8", + "U9", "U10", "U11"]: + for arch in ["i386", "x86_64"]: + for installtype in ["url", "iso"]: + expect_success(distro, version, arch, installtype) + # bad RHEL-5 version + expect_fail("RHEL-5", "U20", "x86_64", "url") + + # RHEL-6 + for distro in ["RHEL-6", "CentOS-6", "ScientificLinux-6", "OEL-6"]: + for version in ["0", "1", "2", "3", "4", "5", "6", "7", "8"]: + for arch in ["i386", "x86_64"]: + for installtype in ["url", "iso"]: + expect_success(distro, version, arch, installtype) + # bad RHEL-6 version + expect_fail("RHEL-6", "U10", "x86_64", "url") + + # RHEL-7 + for distro in ["RHEL-7", "CentOS-7"]: + for version in ["0", "1", "2"]: + for arch in ["i386", "x86_64"]: + for installtype in ["url", "iso"]: + expect_success(distro, version, arch, installtype) + # bad RHEL-7 version + expect_fail("RHEL-7", "U99", "x86_64", "url") + + # Debian + for version in ["5", "6", "7", "8"]: + for arch in ["i386", "x86_64"]: + expect_success("Debian", version, arch, "iso") + # bad Debian version + expect_fail("Debian", "12", "i386", "iso") + + # Windows + expect_success("Windows", "2000", "i386", "iso") + for version in ["XP", "2003", "2008", "7", "8", "2012", "8.1"]: + for arch in ["i386", "x86_64"]: + expect_success("Windows", version, arch, "iso") + # bad Windows 2000 arch + expect_fail("Windows", "2000", "x86_64", "iso") + # bad Windows version + expect_fail("Windows", "1999", "x86_64", "iso") + # invalid Windows installtype + expect_fail("Windows", "2008", "x86_64", "url") + + # OpenSUSE + for version in ["10.3", "11.0", "11.1", "11.2", "11.3", "11.4", "12.1", + "12.2", "12.3", "13.1", "13.2"]: + for arch in ["i386", "x86_64"]: + expect_success("OpenSUSE", version, arch, "iso") + # bad OpenSUSE version + expect_fail("OpenSUSE", "16", "x86_64", "iso") + # invalid OpenSUSE installtype + expect_fail("OpenSUSE", "11.4", "x86_64", "url") + + # Ubuntu + for version in ["5.04", "5.10", "6.06", "6.06.1", "6.06.2", "6.10", "7.04", + "7.10", "8.04", "8.04.1", "8.04.2", "8.04.3", "8.04.4", + "8.10", "9.04", "9.10", "10.04", "10.04.1", "10.04.2", + "10.04.3", "10.10", "11.04", "11.10", "12.04", "12.04.1", + "12.04.2", "12.04.3", "12.10", "13.04", "13.10", "14.04", + "14.10", "15.04", "15.10", "16.04"]: + for arch in ["i386", "x86_64"]: + for installtype in ["iso", "url"]: + expect_success("Ubuntu", version, arch, installtype) + # bad Ubuntu version + expect_fail("Ubuntu", "10.9", "i386", "iso") + + # Mandrake + for version in ["8.2", "9.1", "9.2", "10.0", "10.1"]: + expect_success("Mandrake", version, "i386", "iso") + # bad Mandrake version + expect_fail("Mandrake", "11", "i386", "iso") + # bad Mandrake arch + expect_fail("Mandrake", "8.2", "x86_64", "iso") + # bad Mandrake installtype + expect_fail("Mandrake", "8.2", "i386", "url") + + # Mandriva + for version in ["2005", "2006.0", "2007.0", "2008.0"]: + for arch in ["i386", "x86_64"]: + expect_success("Mandriva", version, arch, "iso") + # bad Mandriva version + expect_fail("Mandriva", "80", "i386", "iso") + # bad Mandriva installtype + expect_fail("Mandriva", "2005", "i386", "url") + + # Mageia + for version in ["2", "3", "4", "4.1", "5"]: + for arch in ["i386", "x86_64"]: + expect_success("Mageia", version, arch, "iso") + # bad Mandriva version + expect_fail("Mageia", "80", "i386", "iso") + # bad Mandriva installtype + expect_fail("Mageia", "2005", "i386", "url") + + # Now run all the tests + for tst in alltests: + yield tst.execute() diff -Nru oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target_device.xml oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target_device.xml --- oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target_device.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target_device.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target.xml oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target.xml --- oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_disk_target.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target_device.xml oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target_device.xml --- oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target_device.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target_device.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target.xml oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target.xml --- oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces_missing_interface_target.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces.xml oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces.xml --- oz-0.15.0/tests/guest/libvirt/test_get_disks_and_interfaces.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_get_disks_and_interfaces.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_final.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_final.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_final.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_final.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,43 @@ +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="qcow2"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_missing_disk_source.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_missing_disk_source.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_missing_disk_source.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_missing_disk_source.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_too_many_drivers.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_too_many_drivers.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_too_many_drivers.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage_too_many_drivers.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + <driver name="qemu" type="qcow2"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_diskimage.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,43 @@ +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_final.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_final.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_final.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_final.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,38 @@ +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + <serial type="tcp"><source host="127.0.0.1" mode="bind" service="%s"/><protocol type="raw"/><target port="1"/></serial></devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_missing_devices.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_missing_devices.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_missing_devices.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_missing_devices.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_devices.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_devices.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_devices.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_devices.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,68 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> + +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_targets.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_targets.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_targets.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial_too_many_targets.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + <target port="2"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial.xml oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial.xml --- oz-0.15.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_modify_libvirt_xml_for_serial.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,43 @@ +<domain type="qemu"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics port="-1" type="vnc"/> + <interface type="bridge"> + <source bridge="%s"/> + <target dev="vnet7"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target dev="vda" bus="virtio"/> + <source file="%s"/> + <driver name="qemu" type="raw"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_xml_generation_1.xml oz-0.16.0/tests/guest/libvirt/test_xml_generation_1.xml --- oz-0.15.0/tests/guest/libvirt/test_xml_generation_1.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_xml_generation_1.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,42 @@ +<domain type="kvm"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics type="vnc" port="-1"/> + <interface type="bridge"> + <source bridge="%s"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source host="127.0.0.1" mode="bind" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target bus="virtio" dev="vda"/> + <source file="%s"/> + <driver type="raw" name="qemu"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/libvirt/test_xml_generation_2.xml oz-0.16.0/tests/guest/libvirt/test_xml_generation_2.xml --- oz-0.15.0/tests/guest/libvirt/test_xml_generation_2.xml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/libvirt/test_xml_generation_2.xml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,49 @@ +<domain type="kvm"> + <name>tester</name> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <uuid>%s</uuid> + <clock offset="utc"/> + <vcpu>1</vcpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <os> + <type>hvm</type> + <boot dev="hd"/> + <kernel>kernel option</kernel> + <initrd>initrd option</initrd> + <cmdline>command line</cmdline> + </os> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <graphics type="vnc" port="-1"/> + <interface type="bridge"> + <source bridge="%s"/> + <mac address="52:54:00:04:cc:a6"/> + <model type="virtio"/> + </interface> + <input bus="ps2" type="mouse"/> + <serial type="pty"> + <target port="0"/> + </serial> + <serial type="tcp"> + <source host="127.0.0.1" mode="bind" service="%s"/> + <protocol type="raw"/> + <target port="1"/> + </serial> + <disk device="disk" type="file"> + <target bus="virtio" dev="vda"/> + <source file="%s"/> + <driver type="raw" name="qemu"/> + </disk> + <disk device="blue" type="file"> + <source file="/var/bin/foo"/> + <target dev="muni"/> + </disk> + </devices> +</domain> diff -Nru oz-0.15.0/tests/guest/test_guest.py oz-0.16.0/tests/guest/test_guest.py --- oz-0.15.0/tests/guest/test_guest.py 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/test_guest.py 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,633 @@ +#!/usr/bin/python + +import sys +import getpass +try: + import configparser +except ImportError: + import ConfigParser as configparser +try: + from io import StringIO, BytesIO +except: + from StringIO import StringIO + BytesIO = StringIO +import logging +import os + +# Find oz library +prefix = '.' +for i in range(0,3): + if os.path.isdir(os.path.join(prefix, 'oz')): + sys.path.insert(0, prefix) + break + else: + prefix = '../' + prefix + +try: + import oz.TDL + import oz.GuestFactory +except ImportError as e: + print(e) + print('Unable to import oz. Is oz installed or in your PYTHONPATH?') + sys.exit(1) + +try: + import py.test +except ImportError: + print('Unable to import py.test. Is py.test installed?') + sys.exit(1) + +def default_route(): + route_file = "/proc/net/route" + d = file(route_file) + + defn = 0 + for line in d: + info = line.split() + if (len(info) != 11): # 11 = typical num of fields in the file + logging.warn(_("Invalid line length while parsing %s.") % + (route_file)) + break + try: + route = int(info[1], 16) + if route == 0: + return info[0] + except ValueError: + continue + raise Exception("Could not find default route") + +# we find the default route for this machine. Note that this very well +# may not be a bridge, but for the purposes of testing the factory, it +# doesn't really matter; it just has to have an IP address +route = default_route() + +def setup_guest(xml, macaddress=None): + tdl = oz.TDL.TDL(xml) + + config = configparser.SafeConfigParser() + config.readfp(BytesIO("[libvirt]\nuri=qemu:///session\nbridge_name=%s" % route)) + + guest = oz.GuestFactory.guest_factory(tdl, config, None, macaddress=macaddress) + return guest + +tdlxml = """ +<template> + <name>tester</name> + <os> + <name>Fedora</name> + <version>14</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux//releases/14/Fedora/x86_64/os/</url> + </install> + </os> +</template> +""" + +tdlxml2 = """ +<template> + <name>tester</name> + <os> + <name>Fedora</name> + <version>14</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux//releases/14/Fedora/x86_64/os/</url> + </install> + </os> + <disk> + <size>20</size> + </disk> +</template> +""" + + +def test_geteltorito_none_src(): + guest = setup_guest(tdlxml) + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(None, None) + +def test_geteltorito_none_dst(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + open(src, 'w').write('src') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, None) + +def test_geteltorito_short_pvd(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + open(src, 'w').write('foo') + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(Exception): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_pvd_desc(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write('\0'*128) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_pvd_ident(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write('\0'*127) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_pvd_unused(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\0x1") + fd.write('\0'*127) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_pvd_unused2(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\x01") + fd.write("\x00") + fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + fd.write("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") + fd.write("\x01") + fd.write('\0'*127) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_short_boot_sector(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\x01") + fd.write("\x00") + fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + fd.write("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") + fd.write("\x00") + fd.write('\0'*127) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(Exception): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_boot_sector(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\x01") + fd.write("\x00") + fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + fd.write("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") + fd.write("\x00") + fd.write('\0'*127) + fd.seek(17*2048) + fd.write("\x01") + fd.write('\0'*75) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_boot_isoident(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\x01") + fd.write("\x00") + fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + fd.write("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") + fd.write("\x00") + fd.write('\0'*127) + fd.seek(17*2048) + fd.write("\x00") + fd.write("AAAAA") + fd.write('\0'*75) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_boot_version(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\x01") + fd.write("\x00") + fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + fd.write("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") + fd.write("\x00") + fd.write('\0'*127) + fd.seek(17*2048) + fd.write("\x00") + fd.write("CD001") + fd.write('\0'*75) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_boot_torito(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\x01") + fd.write("\x00") + fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + fd.write("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") + fd.write("\x00") + fd.write('\0'*127) + fd.seek(17*2048) + fd.write("\x00") + fd.write("CD001") + fd.write("\x01") + fd.write('\0'*75) + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(oz.OzException.OzException): + guest._geteltorito(src, dst) + +def test_geteltorito_bogus_bootp(tmpdir): + guest = setup_guest(tdlxml) + + src = os.path.join(str(tmpdir), 'src') + fd = open(src, 'w') + fd.seek(16*2048) + fd.write("\x01") + fd.write("CD001") + fd.write("\x01") + fd.write("\x00") + fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + fd.write("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") + fd.write("\x00") + fd.write('\0'*127) + fd.seek(17*2048) + fd.write("\x00") + fd.write("CD001") + fd.write("\x01") + fd.write("EL TORITO SPECIFICATION") + fd.write('\0'*41) + fd.write("\x20\x00\x00\x00") + fd.close() + + dst = os.path.join(str(tmpdir), 'dst') + + with py.test.raises(Exception): + guest._geteltorito(src, dst) + +def test_init_guest(): + guest = setup_guest(tdlxml2) + + assert guest.disksize == 20 + assert guest.image_name() == 'tester' + assert guest.output_image_path() == '/home/%s/.oz/images/tester.dsk' % getpass.getuser() + assert guest.default_auto_file() == True + +def test_init_guest_bad_arch(): + tdl = oz.TDL.TDL(tdlxml) + tdl.arch = 'armhf' # Done here to make sure the TDL class doesn't error + config = configparser.SafeConfigParser() + config.readfp(BytesIO("[libvirt]\nuri=qemu:///session\nbridge_name=%s" % route)) + with py.test.raises(Exception): + oz.GuestFactory.guest_factory(tdl, config, None) + +def test_icicle_generation(): + guest = setup_guest(tdlxml) + with open(os.path.dirname(__file__) + '/test.icicle', 'r') as handle: + test_icicle = handle.read() + + packages = [ + "accountsservice", + "adduser", + "apparmor", + "apt", + "apt-transport-https", + "apt-utils", + "apt-xapian-index", + "aptitude", + "at", + "base-files", + "base-passwd", + "bash", + "bash-completion", + "bind9-host", + "binutils", + "bsdmainutils", + "bsdutils", + "build-essential", + "busybox-initramfs", + "busybox-static", + "bzip2", + "ca-certificates", + "chef", + "cloud-init", + "cloud-initramfs-growroot", + "cloud-initramfs-rescuevol", + "cloud-utils", + "comerr-dev", + "console-setup", + "coreutils", + "cpio", + "cpp", + "cpp-4.6", + "crda", + "cron", + "curl", + "dash", + "dbus", + "debconf", + "debconf-i18n", + "debianutils", + "denyhosts", + "diffutils", + "discover", + "discover-data", + "dkms", + "dmidecode", + "dmsetup", + "dnsutils", + "dosfstools", + "dpkg", + "dpkg-dev", + "e2fslibs", + "e2fsprogs", + "ed", + "eject", + "euca2ools", + "fakeroot", + "file", + "findutils", + "friendly-recovery", + "ftp", + "fuse", + "g++", + "g++-4.6" + ] + + icicle = guest._output_icicle_xml(packages, 'Icicle Description') + assert test_icicle == icicle + +def test_xml_generation_1(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + with open(os.path.dirname(__file__) + '/libvirt/test_xml_generation_1.xml', 'r') as handle: + test_xml = handle.read() + + # Replace various smaller items as they are auto generated + test_xml = test_xml % (guest.uuid, route, guest.listen_port, guest.diskimage) + + bootdev = 'hd' + installdev = None + libvirt = guest._generate_xml(bootdev, installdev) + + assert test_xml == libvirt + +def test_xml_generation_2(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + with open(os.path.dirname(__file__) + '/libvirt/test_xml_generation_2.xml', 'r') as handle: + test_xml = handle.read() + + # Replace various smaller items as they are auto generated + test_xml = test_xml % (guest.uuid, route, guest.listen_port, guest.diskimage) + + bootdev = 'hd' + installdev = guest._InstallDev('blue', '/var/bin/foo', 'muni') + libvirt = guest._generate_xml(bootdev, installdev, kernel='kernel option', initrd='initrd option', cmdline='command line') + + assert test_xml == libvirt + +def test_get_disks_and_interfaces(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + path = os.path.dirname(__file__) + '/libvirt/test_get_disks_and_interfaces.xml' + + # Get the comparision xml + with open(path, 'r') as handle: + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + disks, interfaces = guest._get_disks_and_interfaces(test_xml) + + assert disks == ['vda'] + assert interfaces == ['vnet7'] + +def test_get_disks_and_interfaces_missing_interface_target(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + path = os.path.dirname(__file__) + '/libvirt/test_get_disks_and_interfaces_missing_interface_target.xml' + + # Get the comparision xml + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._get_disks_and_interfaces(test_xml) + +def test_get_disks_and_interfaces_missing_interface_target_device(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + path = os.path.dirname(__file__) + '/libvirt/test_get_disks_and_interfaces_missing_interface_target_device.xml' + + # Get the comparision xml + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._get_disks_and_interfaces(test_xml) + +def test_get_disks_and_interfaces_missing_disk_target(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + path = os.path.dirname(__file__) + '/libvirt/test_get_disks_and_interfaces_missing_disk_target.xml' + + # Get the comparision xml + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._get_disks_and_interfaces(test_xml) + +def test_get_disks_and_interfaces_missing_disk_target_device(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_get_disks_and_interfaces_missing_disk_target_device.xml' + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._get_disks_and_interfaces(test_xml) + +def test_modify_libvirt_xml_for_serial(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_for_serial.xml' + with open(path, 'r') as handle: + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + final = guest._modify_libvirt_xml_for_serial(test_xml) + + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_for_serial_final.xml' + with open(path, 'r') as handle: + # Replace various smaller items as they are auto generated + final_xml = handle.read() % (guest.uuid, route, guest.diskimage, guest.listen_port) + assert final_xml == final + +def test_modify_libvirt_xml_for_serial_too_many_targets(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_for_serial_too_many_targets.xml' + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._modify_libvirt_xml_for_serial(test_xml) + +def test_modify_libvirt_xml_for_serial_missing_devices(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_for_serial_missing_devices.xml' + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._modify_libvirt_xml_for_serial(test_xml) + +def test_modify_libvirt_xml_for_serial_too_many_devices(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_for_serial_too_many_devices.xml' + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._modify_libvirt_xml_for_serial(test_xml) + + +def test_modify_libvirt_xml_diskimage(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_diskimage.xml' + with open(path, 'r') as handle: + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + + name, ext = os.path.splitext(guest.diskimage) + image = name + '.qcow2' + final = guest._modify_libvirt_xml_diskimage(test_xml, image, 'qcow2') + + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_diskimage_final.xml' + with open(path, 'r') as handle: + # Replace various smaller items as they are auto generated + final_xml = handle.read() % (guest.uuid, route, guest.listen_port, image) + assert final_xml == final + +def test_modify_libvirt_xml_diskimage_missing_disk_source(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_diskimage_missing_disk_source.xml' + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._modify_libvirt_xml_diskimage(test_xml, guest.diskimage, 'qcow2') + +def test_modify_libvirt_xml_diskimage_too_many_drivers(): + # Provide a macaddress so testing is easier + guest = setup_guest(tdlxml, macaddress='52:54:00:04:cc:a6') + + # Get the comparision xml + path = os.path.dirname(__file__) + '/libvirt/test_modify_libvirt_xml_diskimage_too_many_drivers.xml' + with open(path, 'r') as handle: + with py.test.raises(Exception): + # Replace various smaller items as they are auto generated + test_xml = handle.read() % (guest.uuid, route, guest.listen_port, guest.diskimage) + guest._modify_libvirt_xml_diskimage(test_xml, guest.diskimage, 'qcow2') diff -Nru oz-0.15.0/tests/guest/test.icicle oz-0.16.0/tests/guest/test.icicle --- oz-0.15.0/tests/guest/test.icicle 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/guest/test.icicle 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,70 @@ +<icicle> + <description>Icicle Description</description> + <packages> + <package name="accountsservice"/> + <package name="adduser"/> + <package name="apparmor"/> + <package name="apt"/> + <package name="apt-transport-https"/> + <package name="apt-utils"/> + <package name="apt-xapian-index"/> + <package name="aptitude"/> + <package name="at"/> + <package name="base-files"/> + <package name="base-passwd"/> + <package name="bash"/> + <package name="bash-completion"/> + <package name="bind9-host"/> + <package name="binutils"/> + <package name="bsdmainutils"/> + <package name="bsdutils"/> + <package name="build-essential"/> + <package name="busybox-initramfs"/> + <package name="busybox-static"/> + <package name="bzip2"/> + <package name="ca-certificates"/> + <package name="chef"/> + <package name="cloud-init"/> + <package name="cloud-initramfs-growroot"/> + <package name="cloud-initramfs-rescuevol"/> + <package name="cloud-utils"/> + <package name="comerr-dev"/> + <package name="console-setup"/> + <package name="coreutils"/> + <package name="cpio"/> + <package name="cpp"/> + <package name="cpp-4.6"/> + <package name="crda"/> + <package name="cron"/> + <package name="curl"/> + <package name="dash"/> + <package name="dbus"/> + <package name="debconf"/> + <package name="debconf-i18n"/> + <package name="debianutils"/> + <package name="denyhosts"/> + <package name="diffutils"/> + <package name="discover"/> + <package name="discover-data"/> + <package name="dkms"/> + <package name="dmidecode"/> + <package name="dmsetup"/> + <package name="dnsutils"/> + <package name="dosfstools"/> + <package name="dpkg"/> + <package name="dpkg-dev"/> + <package name="e2fslibs"/> + <package name="e2fsprogs"/> + <package name="ed"/> + <package name="eject"/> + <package name="euca2ools"/> + <package name="fakeroot"/> + <package name="file"/> + <package name="findutils"/> + <package name="friendly-recovery"/> + <package name="ftp"/> + <package name="fuse"/> + <package name="g++"/> + <package name="g++-4.6"/> + </packages> +</icicle> diff -Nru oz-0.15.0/tests/ozutil/test_ozutil.py oz-0.16.0/tests/ozutil/test_ozutil.py --- oz-0.15.0/tests/ozutil/test_ozutil.py 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/ozutil/test_ozutil.py 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,448 @@ +#!/usr/bin/python + +import sys +import os + +try: + import py.test +except ImportError: + print('Unable to import py.test. Is py.test installed?') + sys.exit(1) + +# Find oz +prefix = '.' +for i in range(0,3): + if os.path.isdir(os.path.join(prefix, 'oz')): + sys.path.insert(0, prefix) + break + else: + prefix = '../' + prefix + +try: + import oz.ozutil +except ImportError: + print('Unable to import oz. Is oz installed?') + sys.exit(1) + +# test oz.ozutil.generate_full_auto_path +def test_auto(): + oz.ozutil.generate_full_auto_path('fedora-14-jeos.ks') + +def test_auto_none(): + with py.test.raises(Exception): + oz.ozutil.generate_full_auto_path(None) + +# test oz.ozutil.executable_exists +def test_exe_exists_bin_ls(): + oz.ozutil.executable_exists('/bin/ls') + +def test_exe_exists_foo(): + with py.test.raises(Exception): + oz.ozutil.executable_exists('foo') + +def test_exe_exists_full_foo(): + with py.test.raises(Exception): + oz.ozutil.executable_exists('/bin/foo') + +def test_exe_exists_not_x(): + with py.test.raises(Exception): + oz.ozutil.executable_exists('/etc/hosts') + +def test_exe_exists_relative_false(): + oz.ozutil.executable_exists('false') + +def test_exe_exists_none(): + with py.test.raises(Exception): + oz.ozutil.executable_exists(None) + +# test oz.ozutil.copyfile_sparse +def test_copy_sparse_none_src(): + with py.test.raises(Exception): + oz.ozutil.copyfile_sparse(None, None) + +def test_copy_sparse_none_dst(tmpdir): + fullname = os.path.join(str(tmpdir), 'src') + open(fullname, 'w').write('src') + with py.test.raises(Exception): + oz.ozutil.copyfile_sparse(fullname, None) + +def test_copy_sparse_bad_src_mode(tmpdir): + if os.geteuid() == 0: + # this test won't work as root, since root can copy any mode files + return + fullname = os.path.join(str(tmpdir), 'writeonly') + open(fullname, 'w').write('writeonly') + os.chmod(fullname, 0000) + # because copyfile_sparse uses os.open() instead of open(), it throws an + # OSError + with py.test.raises(OSError): + oz.ozutil.copyfile_sparse(fullname, 'output') + +def test_copy_sparse_bad_dst_mode(tmpdir): + if os.geteuid() == 0: + # this test won't work as root, since root can copy any mode files + return + srcname = os.path.join(str(tmpdir), 'src') + open(srcname, 'w').write('src') + dstname = os.path.join(str(tmpdir), 'dst') + open(dstname, 'w').write('dst') + os.chmod(dstname, 0o444) + with py.test.raises(OSError): + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_zero_size_src(tmpdir): + srcname = os.path.join(str(tmpdir), 'src') + fd = open(srcname, 'w') + fd.close() + dstname = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_small_src(tmpdir): + srcname = os.path.join(str(tmpdir), 'src') + open(srcname, 'w').write('src') + dstname = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_one_block_src(tmpdir): + infd = open('/dev/urandom', 'r') + # we read 32*1024 to make sure we use one big buf_size block (see the + # implementation of copyfile_sparse) + data = infd.read(32*1024) + infd.close + + srcname = os.path.join(str(tmpdir), 'src') + outfd = open(srcname, 'w') + outfd.write(data) + outfd.close() + dstname = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_many_blocks_src(tmpdir): + infd = open('/dev/urandom', 'r') + # we read 32*1024 to make sure we use one big buf_size block (see the + # implementation of copyfile_sparse) + data = infd.read(32*1024*10) + infd.close + + srcname = os.path.join(str(tmpdir), 'src') + outfd = open(srcname, 'w') + outfd.write(data) + outfd.close() + dstname = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_zero_blocks(tmpdir): + infd = open('/dev/urandom', 'r') + # we read 32*1024 to make sure we use one big buf_size block (see the + # implementation of copyfile_sparse) + data1 = infd.read(32*1024) + data2 = infd.read(32*1024) + infd.close + + srcname = os.path.join(str(tmpdir), 'src') + outfd = open(srcname, 'w') + outfd.write(data1) + outfd.write('\0'*32*1024) + outfd.write(data2) + outfd.close() + dstname = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_src_not_exists(tmpdir): + srcname = os.path.join(str(tmpdir), 'src') + dstname = os.path.join(str(tmpdir), 'dst') + open(dstname, 'w').write('dst') + with py.test.raises(Exception): + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_dest_not_exists(tmpdir): + srcname = os.path.join(str(tmpdir), 'src') + open(srcname, 'w').write('src') + dstname = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copyfile_sparse(srcname, dstname) + +def test_copy_sparse_src_is_dir(tmpdir): + dstname = os.path.join(str(tmpdir), 'dst') + open(dstname, 'w').write('dst') + with py.test.raises(Exception): + oz.ozutil.copyfile_sparse(tmpdir, dstname) + +def test_copy_sparse_dst_is_dir(tmpdir): + srcname = os.path.join(str(tmpdir), 'src') + open(srcname, 'w').write('src') + with py.test.raises(Exception): + oz.ozutil.copyfile_sparse(srcname, tmpdir) + +# test oz.ozutil.string_to_bool +def test_stb_no(): + for nletter in ['n', 'N']: + for oletter in ['o', 'O']: + curr = nletter+oletter + yield ('bool-'+curr, oz.ozutil.string_to_bool, curr) + +def test_stb_false(): + for fletter in ['f', 'F']: + for aletter in ['a', 'A']: + for lletter in ['l', 'L']: + for sletter in ['s', 'S']: + for eletter in ['e', 'E']: + curr = fletter+aletter+lletter+sletter+eletter + yield ('bool-'+curr, oz.ozutil.string_to_bool, curr) + +def test_stb_yes(): + for yletter in ['y', 'Y']: + for eletter in ['e', 'E']: + for sletter in ['s', 'S']: + curr = yletter+eletter+sletter + yield ('bool-'+curr, oz.ozutil.string_to_bool, curr) + +def test_stb_true(): + for tletter in ['t', 'T']: + for rletter in ['r', 'R']: + for uletter in ['u', 'U']: + for eletter in ['e', 'E']: + curr = tletter+rletter+uletter+eletter + yield ('bool-'+curr, oz.ozutil.string_to_bool, curr) + +def test_stb_none(): + with py.test.raises(Exception): + oz.ozutil.string_to_bool(None) + + +def test_stb_invalid(): + if oz.ozutil.string_to_bool('foobar') is not None: + raise Exception("Expected None return from string_to_bool") + +# test oz.ozutil.generate_macaddress +def test_genmac(): + oz.ozutil.generate_macaddress() + +# test oz.ozutil.mkdir_p +def test_mkdir_p(tmpdir): + fullname = os.path.join(str(tmpdir), 'foo') + oz.ozutil.mkdir_p(fullname) + +def test_mkdir_p_twice(tmpdir): + fullname = os.path.join(str(tmpdir), 'foo') + oz.ozutil.mkdir_p(fullname) + oz.ozutil.mkdir_p(fullname) + +def test_mkdir_p_file_exists(tmpdir): + fullname = os.path.join(str(tmpdir), 'file_exists') + open(fullname, 'w').write('file_exists') + with py.test.raises(OSError): + oz.ozutil.mkdir_p(fullname) + +def test_mkdir_p_none(): + with py.test.raises(Exception): + oz.ozutil.mkdir_p(None) + +def test_mkdir_p_empty_string(): + oz.ozutil.mkdir_p('') + +# test oz.ozutil.copy_modify_file +def test_copy_modify_none_src(): + with py.test.raises(Exception): + oz.ozutil.copy_modify_file(None, None, None) + +def test_copy_modify_none_dst(tmpdir): + fullname = os.path.join(str(tmpdir), 'src') + open(fullname, 'w').write('src') + with py.test.raises(Exception): + oz.ozutil.copy_modify_file(fullname, None, None) + +def test_copy_modify_none_subfunc(tmpdir): + src = os.path.join(str(tmpdir), 'src') + open(src, 'w').write('src') + dst = os.path.join(str(tmpdir), 'dst') + with py.test.raises(Exception): + oz.ozutil.copy_modify_file(src, dst, None) + +def test_copy_modify_bad_src_mode(tmpdir): + if os.geteuid() == 0: + # this test won't work as root, since root can copy any mode files + return + def sub(line): + return line + fullname = os.path.join(str(tmpdir), 'writeonly') + open(fullname, 'w').write('writeonly') + os.chmod(fullname, 0000) + dst = os.path.join(str(tmpdir), 'dst') + with py.test.raises(IOError): + oz.ozutil.copy_modify_file(fullname, dst, sub) + +def test_copy_modify_empty_file(tmpdir): + def sub(line): + return line + src = os.path.join(str(tmpdir), 'src') + f = open(src, 'w') + f.close() + dst = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copy_modify_file(src, dst, sub) + +def test_copy_modify_file(tmpdir): + def sub(line): + return line + src = os.path.join(str(tmpdir), 'src') + f = open(src, 'w') + f.write("this is a line in the file\n") + f.write("this is another line in the file\n") + f.close() + dst = os.path.join(str(tmpdir), 'dst') + oz.ozutil.copy_modify_file(src, dst, sub) + +# test oz.ozutil.write_cpio +def test_write_cpio_none_input(): + with py.test.raises(Exception): + oz.ozutil.write_cpio(None, None) + +def test_write_cpio_none_output(): + with py.test.raises(Exception): + oz.ozutil.write_cpio({}, None) + +def test_write_cpio_empty_dict(tmpdir): + dst = os.path.join(str(tmpdir), 'dst') + oz.ozutil.write_cpio({}, dst) + +def test_write_cpio_existing_file(tmpdir): + if os.geteuid() == 0: + # this test won't work as root, since root can copy any mode files + return + dst = os.path.join(str(tmpdir), 'dst') + open(dst, 'w').write('hello') + os.chmod(dst, 0000) + with py.test.raises(IOError): + oz.ozutil.write_cpio({}, dst) + +def test_write_cpio_single_file(tmpdir): + src = os.path.join(str(tmpdir), 'src') + open(src, 'w').write('src') + dst = os.path.join(str(tmpdir), 'dst') + oz.ozutil.write_cpio({src: 'src'}, dst) + +def test_write_cpio_multiple_files(tmpdir): + src1 = os.path.join(str(tmpdir), 'src1') + open(src1, 'w').write('src1') + src2 = os.path.join(str(tmpdir), 'src2') + open(src2, 'w').write('src2') + dst = os.path.join(str(tmpdir), 'dst') + oz.ozutil.write_cpio({src1: 'src1', src2: 'src2'}, dst) + +def test_write_cpio_not_multiple_of_4(tmpdir): + src = os.path.join(str(tmpdir), 'src') + open(src, 'w').write('src') + dst = os.path.join(str(tmpdir), 'dst') + oz.ozutil.write_cpio({src: 'src'}, dst) + +def test_write_cpio_exception(tmpdir): + if os.geteuid() == 0: + # this test won't work as root, since root can copy any mode files + return + src = os.path.join(str(tmpdir), 'src') + open(src, 'w').write('src') + os.chmod(src, 0000) + dst = os.path.join(str(tmpdir), 'dst') + with py.test.raises(IOError): + oz.ozutil.write_cpio({src: 'src'}, dst) + +def test_md5sum_regular(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('# this is a comment line, followed by a blank line\n\n6e812e782e52b536c0307bb26b3c244e *Fedora-11-i386-DVD.iso\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_sha1sum_regular(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('6e812e782e52b536c0307bb26b3c244e1c42b644 *Fedora-11-i386-DVD.iso\n') + f.close() + + oz.ozutil.get_sha1sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_sha256sum_regular(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('6e812e782e52b536c0307bb26b3c244e1c42b644235f5a4b242786b1ef375358 *Fedora-11-i386-DVD.iso\n') + f.close() + + oz.ozutil.get_sha256sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_bsd(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('MD5 (Fedora-11-i386-DVD.iso)=6e812e782e52b536c0307bb26b3c244e1c42b644235f5a4b242786b1ef375358\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_bsd_no_start_paren(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + # if BSD is missing a paren, we don't raise an exception, just ignore and + # continue + f.write('MD5 Fedora-11-i386-DVD.iso)=6e812e782e52b536c0307bb26b3c244e1c42b644235f5a4b242786b1ef375358\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_bsd_no_end_paren(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + # if BSD is missing a paren, we don't raise an exception, just ignore and + # continue + f.write('MD5 (Fedora-11-i386-DVD.iso=6e812e782e52b536c0307bb26b3c244e1c42b644235f5a4b242786b1ef375358\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_bsd_no_equal(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + # if BSD is missing a paren, we don't raise an exception, just ignore and + # continue + f.write('MD5 (Fedora-11-i386-DVD.iso) 6e812e782e52b536c0307bb26b3c244e1c42b644235f5a4b242786b1ef375358\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_regular_escaped(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('\\6e812e782e52b536c0307bb26b3c244e *Fedora-11-i386-DVD.iso\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_regular_too_short(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('6e *F\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_regular_no_star(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('6e812e782e52b536c0307bb26b3c244e Fedora-11-i386-DVD.iso\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_regular_no_newline(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('6e812e782e52b536c0307bb26b3c244e *Fedora-11-i386-DVD.iso') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') + +def test_md5sum_regular_no_space(tmpdir): + src = os.path.join(str(tmpdir), 'md5sum') + f = open(src, 'w') + f.write('6e812e782e52b536c0307bb26b3c244e_*Fedora-11-i386-DVD.iso\n') + f.close() + + oz.ozutil.get_md5sum_from_file(src, 'Fedora-11-i386-DVD.iso') diff -Nru oz-0.15.0/tests/tdl/hello.cmd oz-0.16.0/tests/tdl/hello.cmd --- oz-0.15.0/tests/tdl/hello.cmd 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/hello.cmd 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1 @@ +echo "hello from file://url" > /tmp/foo diff -Nru oz-0.15.0/tests/tdl/test-01-simple-iso.tdl oz-0.16.0/tests/tdl/test-01-simple-iso.tdl --- oz-0.15.0/tests/tdl/test-01-simple-iso.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-01-simple-iso.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,11 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-02-simple-url.tdl oz-0.16.0/tests/tdl/test-02-simple-url.tdl --- oz-0.15.0/tests/tdl/test-02-simple-url.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-02-simple-url.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,11 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='iso'> + <iso>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</iso> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-03-empty-template.tdl oz-0.16.0/tests/tdl/test-03-empty-template.tdl --- oz-0.15.0/tests/tdl/test-03-empty-template.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-03-empty-template.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,2 @@ +<template> +</template> diff -Nru oz-0.15.0/tests/tdl/test-04-no-os.tdl oz-0.16.0/tests/tdl/test-04-no-os.tdl --- oz-0.15.0/tests/tdl/test-04-no-os.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-04-no-os.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,3 @@ +<template> + <name>wow</name> +</template> diff -Nru oz-0.15.0/tests/tdl/test-05-no-name.tdl oz-0.16.0/tests/tdl/test-05-no-name.tdl --- oz-0.15.0/tests/tdl/test-05-no-name.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-05-no-name.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,10 @@ +<template> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-06-simple-iso-description.tdl oz-0.16.0/tests/tdl/test-06-simple-iso-description.tdl --- oz-0.15.0/tests/tdl/test-06-simple-iso-description.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-06-simple-iso-description.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> +</template> diff -Nru oz-0.15.0/tests/tdl/test-07-packages-no-package.tdl oz-0.16.0/tests/tdl/test-07-packages-no-package.tdl --- oz-0.15.0/tests/tdl/test-07-packages-no-package.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-07-packages-no-package.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,13 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <packages> + </packages> +</template> diff -Nru oz-0.15.0/tests/tdl/test-08-repositories-no-repository.tdl oz-0.16.0/tests/tdl/test-08-repositories-no-repository.tdl --- oz-0.15.0/tests/tdl/test-08-repositories-no-repository.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-08-repositories-no-repository.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,13 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <repositories> + </repositories> +</template> diff -Nru oz-0.15.0/tests/tdl/test-09-os-invalid-arch.tdl oz-0.16.0/tests/tdl/test-09-os-invalid-arch.tdl --- oz-0.15.0/tests/tdl/test-09-os-invalid-arch.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-09-os-invalid-arch.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,11 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>arm</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-10-os-invalid-install-type.tdl oz-0.16.0/tests/tdl/test-10-os-invalid-install-type.tdl --- oz-0.15.0/tests/tdl/test-10-os-invalid-install-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-10-os-invalid-install-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,11 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='foo'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-11-description-packages-repositories.tdl oz-0.16.0/tests/tdl/test-11-description-packages-repositories.tdl --- oz-0.15.0/tests/tdl/test-11-description-packages-repositories.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-11-description-packages-repositories.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,24 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <repositories> + <repository name='myrepo'> + <url>http://path/to/my/repo</url> + </repository> + </repositories> + <packages> + <package name='chris'> + <repository>myrepo</repository> + <file>myfilename</file> + <arguments>args</arguments> + </package> + </packages> +</template> diff -Nru oz-0.15.0/tests/tdl/test-12-os-no-name.tdl oz-0.16.0/tests/tdl/test-12-os-no-name.tdl --- oz-0.15.0/tests/tdl/test-12-os-no-name.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-12-os-no-name.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,10 @@ +<template> + <name>f12jeos</name> + <os> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-13-os-no-version.tdl oz-0.16.0/tests/tdl/test-13-os-no-version.tdl --- oz-0.15.0/tests/tdl/test-13-os-no-version.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-13-os-no-version.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,10 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-14-os-no-arch.tdl oz-0.16.0/tests/tdl/test-14-os-no-arch.tdl --- oz-0.15.0/tests/tdl/test-14-os-no-arch.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-14-os-no-arch.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,10 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-15-os-no-install.tdl oz-0.16.0/tests/tdl/test-15-os-no-install.tdl --- oz-0.15.0/tests/tdl/test-15-os-no-install.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-15-os-no-install.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,8 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-16-signed-repository.tdl oz-0.16.0/tests/tdl/test-16-signed-repository.tdl --- oz-0.15.0/tests/tdl/test-16-signed-repository.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-16-signed-repository.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,25 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <repositories> + <repository name='myrepo'> + <url>http://path/to/my/repo</url> + <signed>yes</signed> + </repository> + </repositories> + <packages> + <package name='chris'> + <repository>myrepo</repository> + <file>myfilename</file> + <arguments>args</arguments> + </package> + </packages> +</template> diff -Nru oz-0.15.0/tests/tdl/test-17-repo-invalid-signed.tdl oz-0.16.0/tests/tdl/test-17-repo-invalid-signed.tdl --- oz-0.15.0/tests/tdl/test-17-repo-invalid-signed.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-17-repo-invalid-signed.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,25 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <repositories> + <repository name='myrepo'> + <url>http://path/to/my/repo</url> + <signed>foo</signed> + </repository> + </repositories> + <packages> + <package name='chris'> + <repository>myrepo</repository> + <file>myfilename</file> + <arguments>args</arguments> + </package> + </packages> +</template> diff -Nru oz-0.15.0/tests/tdl/test-18-rootpw.tdl oz-0.16.0/tests/tdl/test-18-rootpw.tdl --- oz-0.15.0/tests/tdl/test-18-rootpw.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-18-rootpw.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + <rootpw>foo</rootpw> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-19-key.tdl oz-0.16.0/tests/tdl/test-19-key.tdl --- oz-0.15.0/tests/tdl/test-19-key.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-19-key.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + </install> + <key>1234-456-789</key> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-20-multiple-install.tdl oz-0.16.0/tests/tdl/test-20-multiple-install.tdl --- oz-0.15.0/tests/tdl/test-20-multiple-install.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-20-multiple-install.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + </install> + <install type='iso'> + <iso>http://example2.org/Windows2003.iso</iso> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-21-missing-install-type.tdl oz-0.16.0/tests/tdl/test-21-missing-install-type.tdl --- oz-0.15.0/tests/tdl/test-21-missing-install-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-21-missing-install-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,11 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install> + <iso>http://example.org/Windows2003.iso</iso> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-22-md5sum.tdl oz-0.16.0/tests/tdl/test-22-md5sum.tdl --- oz-0.15.0/tests/tdl/test-22-md5sum.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-22-md5sum.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + <md5sum>http://example.org/MD5SUM</md5sum> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-23-sha1sum.tdl oz-0.16.0/tests/tdl/test-23-sha1sum.tdl --- oz-0.15.0/tests/tdl/test-23-sha1sum.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-23-sha1sum.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + <sha1sum>http://example.org/SHA1SUM</sha1sum> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-24-sha256sum.tdl oz-0.16.0/tests/tdl/test-24-sha256sum.tdl --- oz-0.15.0/tests/tdl/test-24-sha256sum.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-24-sha256sum.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + <sha256sum>http://example.org/SHA256SUM</sha256sum> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-25-md5sum-and-sha1sum.tdl oz-0.16.0/tests/tdl/test-25-md5sum-and-sha1sum.tdl --- oz-0.15.0/tests/tdl/test-25-md5sum-and-sha1sum.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-25-md5sum-and-sha1sum.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,13 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + <md5sum>http://example.org/MD5SUM</md5sum> + <sha1sum>http://example.org/SHA1SUM</sha1sum> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-26-md5sum-and-sha256sum.tdl oz-0.16.0/tests/tdl/test-26-md5sum-and-sha256sum.tdl --- oz-0.15.0/tests/tdl/test-26-md5sum-and-sha256sum.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-26-md5sum-and-sha256sum.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,13 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + <md5sum>http://example.org/MD5SUM</md5sum> + <sha256sum>http://example.org/SHA256SUM</sha256sum> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-27-sha1sum-and-sha256sum.tdl oz-0.16.0/tests/tdl/test-27-sha1sum-and-sha256sum.tdl --- oz-0.15.0/tests/tdl/test-27-sha1sum-and-sha256sum.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-27-sha1sum-and-sha256sum.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,13 @@ +<template> + <name>windows</name> + <os> + <name>Windows</name> + <version>2003</version> + <arch>i386</arch> + <install type='iso'> + <iso>http://example.org/Windows2003.iso</iso> + <sha1sum>http://example.org/SHA1SUM</sha1sum> + <sha256sum>http://example.org/SHA256SUM</sha256sum> + </install> + </os> +</template> diff -Nru oz-0.15.0/tests/tdl/test-28-package-no-name.tdl oz-0.16.0/tests/tdl/test-28-package-no-name.tdl --- oz-0.15.0/tests/tdl/test-28-package-no-name.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-28-package-no-name.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,20 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <repositories> + <repository name='myrepo'> + <url>http://path/to/my/repo</url> + </repository> + </repositories> + <packages> + <package/> + </packages> +</template> diff -Nru oz-0.15.0/tests/tdl/test-29-files.tdl oz-0.16.0/tests/tdl/test-29-files.tdl --- oz-0.15.0/tests/tdl/test-29-files.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-29-files.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,16 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo'> +hello there + </file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-30-file-no-name.tdl oz-0.16.0/tests/tdl/test-30-file-no-name.tdl --- oz-0.15.0/tests/tdl/test-30-file-no-name.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-30-file-no-name.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,16 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file> +hello there + </file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-31-file-raw-type.tdl oz-0.16.0/tests/tdl/test-31-file-raw-type.tdl --- oz-0.15.0/tests/tdl/test-31-file-raw-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-31-file-raw-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,16 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='raw'> +hello there + </file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-32-file-base64-type.tdl oz-0.16.0/tests/tdl/test-32-file-base64-type.tdl --- oz-0.15.0/tests/tdl/test-32-file-base64-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-32-file-base64-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='base64'>aGVsbG8K</file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-33-file-invalid-type.tdl oz-0.16.0/tests/tdl/test-33-file-invalid-type.tdl --- oz-0.15.0/tests/tdl/test-33-file-invalid-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-33-file-invalid-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='foo'>aGVsbG8K</file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-34-file-invalid-base64.tdl oz-0.16.0/tests/tdl/test-34-file-invalid-base64.tdl --- oz-0.15.0/tests/tdl/test-34-file-invalid-base64.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-34-file-invalid-base64.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='base64'>BASE64GOBBLEDYGOOK-</file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-35-repository-no-name.tdl oz-0.16.0/tests/tdl/test-35-repository-no-name.tdl --- oz-0.15.0/tests/tdl/test-35-repository-no-name.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-35-repository-no-name.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,17 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <repositories> + <repository> + <url>http://path/to/my/repo</url> + </repository> + </repositories> +</template> diff -Nru oz-0.15.0/tests/tdl/test-36-repository-no-url.tdl oz-0.16.0/tests/tdl/test-36-repository-no-url.tdl --- oz-0.15.0/tests/tdl/test-36-repository-no-url.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-36-repository-no-url.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,16 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <repositories> + <repository name='chris'> + </repository> + </repositories> +</template> diff -Nru oz-0.15.0/tests/tdl/test-37-command.tdl oz-0.16.0/tests/tdl/test-37-command.tdl --- oz-0.15.0/tests/tdl/test-37-command.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-37-command.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,20 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1'> +echo "hello" > /tmp/foo + </command> + <command name='cmd2'> +echo "there" > /tmp/bar + </command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-38-command-no-name.tdl oz-0.16.0/tests/tdl/test-38-command-no-name.tdl --- oz-0.15.0/tests/tdl/test-38-command-no-name.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-38-command-no-name.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,17 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command> +echo "hello" > /tmp/foo + </command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-39-command-raw-type.tdl oz-0.16.0/tests/tdl/test-39-command-raw-type.tdl --- oz-0.15.0/tests/tdl/test-39-command-raw-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-39-command-raw-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,17 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' type='raw'> +echo "hello" > /tmp/foo + </command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-40-command-base64-type.tdl oz-0.16.0/tests/tdl/test-40-command-base64-type.tdl --- oz-0.15.0/tests/tdl/test-40-command-base64-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-40-command-base64-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' type='base64'>ZWNobyAiaGVsbG8iID4gL3RtcC9mb28K</command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-41-command-bogus-base64.tdl oz-0.16.0/tests/tdl/test-41-command-bogus-base64.tdl --- oz-0.15.0/tests/tdl/test-41-command-bogus-base64.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-41-command-bogus-base64.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' type='base64'>ZWNobyAiaGVsbG8iID4gL3RtcC!28K</command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-42-command-bogus-type.tdl oz-0.16.0/tests/tdl/test-42-command-bogus-type.tdl --- oz-0.15.0/tests/tdl/test-42-command-bogus-type.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-42-command-bogus-type.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' type='mytype'>ZWNobyAiaGVsbG8iID4gL3RtcC!28K</command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-43-persisted-repos.tdl oz-0.16.0/tests/tdl/test-43-persisted-repos.tdl --- oz-0.15.0/tests/tdl/test-43-persisted-repos.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-43-persisted-repos.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,64 @@ +<template> + <name>rhel5-u7</name> + <os> + <name>RHEL-5</name> + <version>U7</version> + <arch>i386</arch> + <install type='url'> + <url>http://someserver/path/to/installation</url> + </install> + </os> + <description>A RHEL5 Update 7 image</description> + <repositories> + <!-- Different ways to represent persisted=False --> + <repository name='persist-1-false'> + <url>http://path/to/my/repo-1</url> + <persisted>No</persisted> + </repository> + <repository name='persist-2-false'> + <url>http://path/to/my/repo-2</url> + <persisted>no</persisted> + </repository> + <repository name='persist-3-false'> + <url>http://path/to/my/repo-3</url> + <persisted>NO</persisted> + </repository> + <repository name='persist-4-false'> + <url>http://path/to/my/repo-4</url> + <persisted>false</persisted> + </repository> + <repository name='persist-5-false'> + <url>http://path/to/my/repo-5</url> + <persisted>FALSE</persisted> + </repository> + <repository name='persist-6-false'> + <url>http://path/to/my/repo-6</url> + <persisted>False</persisted> + </repository> + <!-- Different ways to represent persisted --> + <repository name='persist-7-true'> + <url>http://path/to/my/repo-7</url> + <persisted>Yes</persisted> + </repository> + <repository name='persist-8-true'> + <url>http://path/to/my/repo-8</url> + <persisted>yes</persisted> + </repository> + <repository name='persist-9-true'> + <url>http://path/to/my/repo-9</url> + <persisted>YES</persisted> + </repository> + <repository name='persist-10-true'> + <url>http://path/to/my/repo-10</url> + <persisted>True</persisted> + </repository> + <repository name='persist-11-true'> + <url>http://path/to/my/repo-11</url> + <persisted>true</persisted> + </repository> + <repository name='persist-12-true'> + <url>http://path/to/my/repo-12</url> + <persisted>TRUE</persisted> + </repository> + </repositories> +</template> diff -Nru oz-0.15.0/tests/tdl/test-44-version.tdl oz-0.16.0/tests/tdl/test-44-version.tdl --- oz-0.15.0/tests/tdl/test-44-version.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-44-version.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template version="1.0"> + <name>rhel5-u7</name> + <os> + <name>RHEL-5</name> + <version>U7</version> + <arch>i386</arch> + <install type='url'> + <url>http://someserver/path/to/installation</url> + </install> + </os> + <description>A RHEL5 Update 7 image</description> +</template> diff -Nru oz-0.15.0/tests/tdl/test-45-bogus-version.tdl oz-0.16.0/tests/tdl/test-45-bogus-version.tdl --- oz-0.15.0/tests/tdl/test-45-bogus-version.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-45-bogus-version.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<template version="2.0"> + <name>rhel5-u7</name> + <os> + <name>RHEL-5</name> + <version>U7</version> + <arch>i386</arch> + <install type='url'> + <url>http://someserver/path/to/installation</url> + </install> + </os> + <description>A RHEL5 Update 7 image</description> +</template> diff -Nru oz-0.15.0/tests/tdl/test-46-duplicate-name.tdl oz-0.16.0/tests/tdl/test-46-duplicate-name.tdl --- oz-0.15.0/tests/tdl/test-46-duplicate-name.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-46-duplicate-name.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,13 @@ +<template version="1.0"> + <name>rhel5-u7</name> + <name>rhel5-u7</name> + <os> + <name>RHEL-5</name> + <version>U7</version> + <arch>i386</arch> + <install type='url'> + <url>http://someserver/path/to/installation</url> + </install> + </os> + <description>A RHEL5 Update 7 image</description> +</template> diff -Nru oz-0.15.0/tests/tdl/test-47-invalid-template.tdl oz-0.16.0/tests/tdl/test-47-invalid-template.tdl --- oz-0.15.0/tests/tdl/test-47-invalid-template.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-47-invalid-template.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,12 @@ +<templ version="1.0"> + <name>rhel5-u7</name> + <os> + <name>RHEL-5</name> + <version>U7</version> + <arch>i386</arch> + <install type='url'> + <url>http://someserver/path/to/installation</url> + </install> + </os> + <description>A RHEL5 Update 7 image</description> +</templ> diff -Nru oz-0.15.0/tests/tdl/test-48-file-empty-base64.tdl oz-0.16.0/tests/tdl/test-48-file-empty-base64.tdl --- oz-0.15.0/tests/tdl/test-48-file-empty-base64.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-48-file-empty-base64.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='base64'></file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-49-file-empty-raw.tdl oz-0.16.0/tests/tdl/test-49-file-empty-raw.tdl --- oz-0.15.0/tests/tdl/test-49-file-empty-raw.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-49-file-empty-raw.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='raw'></file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-50-command-base64-empty.tdl oz-0.16.0/tests/tdl/test-50-command-base64-empty.tdl --- oz-0.15.0/tests/tdl/test-50-command-base64-empty.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-50-command-base64-empty.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>help</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='zazz' type='base64'></command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-51-disk-size.tdl oz-0.16.0/tests/tdl/test-51-disk-size.tdl --- oz-0.15.0/tests/tdl/test-51-disk-size.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-51-disk-size.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>help</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <disk> + <size>10</size> + </disk> +</template> diff -Nru oz-0.15.0/tests/tdl/test-52-command-file-url.tdl oz-0.16.0/tests/tdl/test-52-command-file-url.tdl --- oz-0.15.0/tests/tdl/test-52-command-file-url.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-52-command-file-url.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' type='url'>file://./tests/tdl/hello.cmd</command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-53-command-http-url.tdl oz-0.16.0/tests/tdl/test-53-command-http-url.tdl --- oz-0.15.0/tests/tdl/test-53-command-http-url.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-53-command-http-url.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' type='url'>https://raw.github.com/clalancette/oz/master/tests/tdl/hello.cmd</command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-54-files-file-url.tdl oz-0.16.0/tests/tdl/test-54-files-file-url.tdl --- oz-0.15.0/tests/tdl/test-54-files-file-url.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-54-files-file-url.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='url'>file://tests/tdl/hello.cmd</file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-55-files-http-url.tdl oz-0.16.0/tests/tdl/test-55-files-http-url.tdl --- oz-0.15.0/tests/tdl/test-55-files-http-url.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-55-files-http-url.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,14 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>x86_64</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <files> + <file name='/tmp/foo' type='url'>https://raw.github.com/clalancette/oz/master/tests/tdl/hello.cmd</file> + </files> +</template> diff -Nru oz-0.15.0/tests/tdl/test-56-invalid-disk-size.tdl oz-0.16.0/tests/tdl/test-56-invalid-disk-size.tdl --- oz-0.15.0/tests/tdl/test-56-invalid-disk-size.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-56-invalid-disk-size.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>help</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <disk> + <size>10E</size> + </disk> +</template> diff -Nru oz-0.15.0/tests/tdl/test-57-invalid-disk-size.tdl oz-0.16.0/tests/tdl/test-57-invalid-disk-size.tdl --- oz-0.15.0/tests/tdl/test-57-invalid-disk-size.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-57-invalid-disk-size.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>help</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <disk> + <size>T10</size> + </disk> +</template> diff -Nru oz-0.15.0/tests/tdl/test-58-disk-size-terabyte.tdl oz-0.16.0/tests/tdl/test-58-disk-size-terabyte.tdl --- oz-0.15.0/tests/tdl/test-58-disk-size-terabyte.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-58-disk-size-terabyte.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,15 @@ +<template> + <name>help</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <disk> + <size>10T</size> + </disk> +</template> diff -Nru oz-0.15.0/tests/tdl/test-59-command-sorting.tdl oz-0.16.0/tests/tdl/test-59-command-sorting.tdl --- oz-0.15.0/tests/tdl/test-59-command-sorting.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-59-command-sorting.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,23 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' position="1"> +echo "hello" > /tmp/foo + </command> + <command name='cmd2' position="3"> +echo "there" > /tmp/bar + </command> + <command name='cmd3' position="2"> +echo "there" > /tmp/foobar + </command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-60-command-mix-positions-and-not.tdl oz-0.16.0/tests/tdl/test-60-command-mix-positions-and-not.tdl --- oz-0.15.0/tests/tdl/test-60-command-mix-positions-and-not.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-60-command-mix-positions-and-not.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,23 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' position="1"> +echo "hello" > /tmp/foo + </command> + <command name='cmd2' position="3"> +echo "there" > /tmp/bar + </command> + <command name='cmd3'> +echo "there" > /tmp/foobar + </command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-61-command-duplicate-position.tdl oz-0.16.0/tests/tdl/test-61-command-duplicate-position.tdl --- oz-0.15.0/tests/tdl/test-61-command-duplicate-position.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-61-command-duplicate-position.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,23 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <commands> + <command name='cmd1' position="1"> +echo "hello" > /tmp/foo + </command> + <command name='cmd2' position="3"> +echo "there" > /tmp/bar + </command> + <command name='cmd3' position="1"> +echo "there" > /tmp/foobar + </command> + </commands> +</template> diff -Nru oz-0.15.0/tests/tdl/test-62-repository-localhost.tdl oz-0.16.0/tests/tdl/test-62-repository-localhost.tdl --- oz-0.15.0/tests/tdl/test-62-repository-localhost.tdl 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test-62-repository-localhost.tdl 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,18 @@ +<template> + <name>f12jeos</name> + <os> + <name>Fedora</name> + <version>12</version> + <arch>i386</arch> + <install type='url'> + <url>http://download.fedoraproject.org/pub/fedora/linux/releases/12/Fedora/x86_64/os/</url> + </install> + </os> + <description>My Fedora 12 JEOS image</description> + <repositories> + <repository name='myrepo'> + <url>http://localhost/to/my/repo</url> + <signed>yes</signed> + </repository> + </repositories> +</template> diff -Nru oz-0.15.0/tests/tdl/test_tdl.py oz-0.16.0/tests/tdl/test_tdl.py --- oz-0.15.0/tests/tdl/test_tdl.py 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/tests/tdl/test_tdl.py 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,199 @@ +#!/usr/bin/python + +import sys +import os + +try: + import lxml.etree +except ImportError: + print('Unable to import lxml. Is python-lxml installed?') + sys.exit(1) + +try: + import py.test +except ImportError: + print('Unable to import py.test. Is py.test installed?') + sys.exit(1) + +# Find oz +prefix = '.' +for i in range(0,3): + if os.path.isdir(os.path.join(prefix, 'oz')): + sys.path.insert(0, prefix) + break + else: + prefix = '../' + prefix + +try: + import oz + import oz.TDL +except ImportError: + print('Unable to import oz. Is oz installed?') + sys.exit(1) + +# the tests dictionary lists all of the test we will run. The key for the +# dictionary is the filename of the test, and the value is whether the test +# is expected to succeed (True) or not (False) +tests = { + "test-01-simple-iso.tdl": True, + "test-02-simple-url.tdl": True, + "test-03-empty-template.tdl": False, + "test-04-no-os.tdl": False, + "test-05-no-name.tdl": False, + "test-06-simple-iso-description.tdl": True, + "test-07-packages-no-package.tdl": True, + "test-08-repositories-no-repository.tdl": True, + "test-09-os-invalid-arch.tdl": False, + "test-10-os-invalid-install-type.tdl": False, + "test-11-description-packages-repositories.tdl": True, + "test-12-os-no-name.tdl": False, + "test-13-os-no-version.tdl": False, + "test-14-os-no-arch.tdl": False, + "test-15-os-no-install.tdl": False, + "test-16-signed-repository.tdl": True, + "test-17-repo-invalid-signed.tdl": False, + "test-18-rootpw.tdl": True, + "test-19-key.tdl": True, + "test-20-multiple-install.tdl": False, + "test-21-missing-install-type.tdl": False, + "test-22-md5sum.tdl": True, + "test-23-sha1sum.tdl": True, + "test-24-sha256sum.tdl": True, + "test-25-md5sum-and-sha1sum.tdl": False, + "test-26-md5sum-and-sha256sum.tdl": False, + "test-27-sha1sum-and-sha256sum.tdl": False, + "test-28-package-no-name.tdl": False, + "test-29-files.tdl": True, + "test-30-file-no-name.tdl": False, + "test-31-file-raw-type.tdl": True, + "test-32-file-base64-type.tdl": True, + "test-33-file-invalid-type.tdl": False, + "test-34-file-invalid-base64.tdl": False, + "test-35-repository-no-name.tdl": False, + "test-36-repository-no-url.tdl": False, + "test-37-command.tdl": True, + "test-38-command-no-name.tdl": False, + "test-39-command-raw-type.tdl": True, + "test-40-command-base64-type.tdl": True, + "test-41-command-bogus-base64.tdl": False, + "test-42-command-bogus-type.tdl": False, + "test-43-persisted-repos.tdl": True, + "test-44-version.tdl": True, + "test-45-bogus-version.tdl": False, + "test-46-duplicate-name.tdl": False, + "test-47-invalid-template.tdl": False, + "test-48-file-empty-base64.tdl": True, + "test-49-file-empty-raw.tdl": True, + "test-50-command-base64-empty.tdl": False, + "test-51-disk-size.tdl": True, + "test-52-command-file-url.tdl": True, + "test-53-command-http-url.tdl": True, + "test-54-files-file-url.tdl": True, + "test-55-files-http-url.tdl": True, + "test-56-invalid-disk-size.tdl": False, + "test-57-invalid-disk-size.tdl": False, + "test-58-disk-size-terabyte.tdl": True, + "test-59-command-sorting.tdl": True, +} + +def get_tdl(filename): + # locate full path for tdl file + tdl_prefix = '' + for tdl_prefix in ['tests/tdl/', 'tdl/', '']: + if os.path.isfile(tdl_prefix + filename): + break + if not os.path.isfile(tdl_prefix + filename): + raise Exception('Unable to locate TDL: %s' % filename) + tdl_file = tdl_prefix + filename + + # Grab TDL object + tdl = validate_ozlib(tdl_file) + return tdl + +# Validate oz handling of tdl file +def validate_ozlib(tdl_file): + xmldata = open(tdl_file, 'r').read() + return oz.TDL.TDL(xmldata) + +# Validate schema +def validate_schema(tdl_file): + + # Locate relaxng schema + rng_file = None + for tryme in ['../../oz/tdl.rng', + '../oz/tdl.rng', + 'oz/tdl.rng', + 'tdl.rng',]: + if os.path.isfile(tryme): + rng_file = tryme + break + + if rng_file is None: + raise Exception('RelaxNG schema file not found: tdl.rng') + + relaxng = lxml.etree.RelaxNG(file=rng_file) + xml = open(tdl_file, 'r') + doc = lxml.etree.parse(xml) + xml.close() + + valid = relaxng.validate(doc) + if not valid: + errstr = "\n%s XML schema validation failed:\n" % (tdl_file) + for error in relaxng.error_log: + errstr += "\tline %s: %s\n" % (error.line, error.message) + raise Exception(errstr) + +# Test generator that iterates over all .tdl files +def test(): + + # Define a helper to expect an exception + def handle_exception(func, *args): + with py.test.raises(Exception): + func(*args) + + # Sanity check to see if any tests are unaccounted for in the config file + for (tdl, expected_pass) in list(tests.items()): + + # locate full path for tdl file + tdl_prefix = '' + for tdl_prefix in ['tests/tdl/', 'tdl/', '']: + if os.path.isfile(tdl_prefix + tdl): + break + tdl_file = tdl_prefix + tdl + test_name = os.path.splitext(tdl,)[0] + + # Generate a unique unittest test for each validate_* method + for tst in (validate_ozlib, validate_schema, ): + # We need a unique method name + unique_name = test_name + tst.__name__ + + # Are we expecting the test to fail? + if expected_pass: + yield '%s_%s' % (test_name, tst.__name__), tst, tdl_file + else: + yield '%s_%s' % (test_name, tst.__name__), handle_exception,\ + tst, tdl_file + +def test_persisted(tdl='test-43-persisted-repos.tdl'): + # locate full path for tdl file + tdl_prefix = '' + for tdl_prefix in ['tests/tdl/', 'tdl/', '']: + if os.path.isfile(tdl_prefix + tdl): + break + if not os.path.isfile(tdl_prefix + tdl): + raise Exception('Unable to locate TDL: %s' % tdl) + tdl_file = tdl_prefix + tdl + test_name = os.path.splitext(tdl,)[0] + + # Grab TDL object + tdl = validate_ozlib(tdl_file) + + def assert_persisted_value(persisted, value): + assert persisted == value, \ + "expected %s, got %s" % (value, persisted) + + for repo in list(tdl.repositories.values()): + if repo.name.endswith('true'): + yield '%s_%s' % (test_name, repo.name), assert_persisted_value, repo.persisted, True + else: + yield '%s_%s' % (test_name, repo.name), assert_persisted_value, repo.persisted, False diff -Nru oz-0.15.0/TODO oz-0.16.0/TODO --- oz-0.15.0/TODO 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/TODO 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,116 @@ +1. More OS support: + - Debian Etch, Lenny, Squeeze + - SUSE Linux 10.0, 10.1 + - openSUSE 10.2 + - OpenBSD (http://people.cs.uchicago.edu/~brendan/howtos/openbsd_install/) + - NetBSD (anita) + - FreeBSD + - Dragonfly BSD + - Windows Vista, 98, 95, ME + - RHL 5.2, 6.0, 6.1, 6.2 (only nfs installs work) +2. Add partition support to the TDL. +3. Add improved error logging to guest installation. That is, if the guest + installation fails, we want to get detailed error information out of the + installer to find out why. Fedora/RHEL support doing remote syslog via + network or virtio-serial, and I also believe anaconda will dump data to + syslog during installation, so that is one avenue to pursue. NOTE: anaconda + virtio-serial support does not exist until F-14. See + http://fedoraproject.org/wiki/Anaconda/Logging +4. Before attempting an install, check the output_dir to make sure it will + approximately have enough space for the output disk image. +5. With windows from MSDN (at least 2000, maybe other versions too), the + top-level doesn't really contain the bits you care about. What happens is + that there are 3 boot options on the CD: installer for Professional, Server, + and Advanced Server. The directory structure on the CD is something like: + + english + win2000 + adv_server + pro + server + + and underneath each of (adv_server, pro, server) are the bits you care + about. To automate this you really need to figure out which one the user + cares about, then copy just that subdirectory. You'll also need to extract + the appropriate parts of eltorito so it boots automatically, and then + you'll also need to set i386/txtsetup.sif [SetupData]: SetupSourcePath = "\". + See http://old.bink.nu/bootcd/ +6. Take a screen shot if some customization or generate-icicle steps time out. +7. Try to do automatic detection of distro/version/architecture from the ISO + that are provided to us. That will make it so that a minimal TDL will only + include the path to the ISO, and we can figure out the rest of the + information. +8. Add support for additional drivers during install (needed for Windows virtio + support). +9. Make "python setup.py bdist_rpm" work. The problem is that bdist_rpm + generates its own SPEC file based on the information in setup.py. When it + does this, it somehow messes up the %description (minor, probably a change + to the setup macro), fails to create the /var/lib/oz subdirectories (which + could be moved into setup.py as well), and also fails to find the gzipped + man page files (which is what eventually causes the build to fail). +10. RHL 6.2 does not work via HTTP because of a bug in the installer; when + parsing a URL passed in via "method", it fails to put a / at the beginning + of the URL. What this means is that when the installer goes to fetch the + install images via "GET path/to/netstg2.img HTTP/0.9", the web server then + returns an error. To do a fully automated install, we need to use an ISO, + NFS or FTP install method; I could not get FTP to work, but I did not try + that hard. +11. RHL 6.1 fails because there is no netstg2.img available in the distribution + I have. Unfortunately, I have not been able to find the netstg2.img, nor an + ISO of 6.1 to do an alternate install. NFS may still work here. +12. RHL 6.0 fails because the kernel panics on boot: + VFS: Cannot open root device 08:21 + Kernel panic: VFS: Unable to mount root fs on 08:21 +13. Enable SELinux in enforcing mode inside of Fedora/RHEL guests. The current + problem with this is that prior to launching the customize step, we upload + files like ssh keys via libguestfs. In the default mode libguestfs uploads + them with no SELinux context, which means reads are denied. + + Jim Meyering suggests that we add a script to the end of /etc/rc.d/rc.local + that does a "restorecon" on the files we care about. Then we don't need + to worry about knowing the context in Oz; we just make it do the right + thing. There are two problems with this: 1) if we upload /etc/rc.d/rc.local, + that will have the wrong context that we have to deal with, and 2) there + is a race condition between icicle-nc checking in and this restorecon + script finishing. Neither problem is insurmountable, but we should make + the effort to get this working. + +14. Do preflight checks on detected bridges. That is, if we detected the + bridge (i.e. it was not specified by the user), then we should do some + basic checks to make sure that we think it will work. We should check the + state of at least: + + /proc/sys/vm/net/ipv4/ip_forward + /proc/sys/net/bridge/bridge-nf-call-arptables + /proc/sys/net/bridge/bridge-nf-call-ip6tables + /proc/sys/net/bridge/bridge-nf-call-iptables + iptables -t filter -L -v + iptables -t nat -L -v + +15. We should add a mode where we assume the responsibility for uploading RPMs + into the guest (via ssh) and then doing a yum localinstall on those RPMs. +16. We should add additional commands section. In particular, we should have + "pre" package installation and "post" package installation command + sections. +17. Support hashes for the root password support. +18. Add a mode where we use a reverse ssh tunnel with a SOCKS proxy to + download the packages. This means that the packages need to be visible to + the imagefactory machine, but not necessarily to the instance. +19. During customization/icicle generation, we should check to make sure that + the bridge listed in the libvirt XML matches up with what we detected (if + anything). This isn't really an issue when doing oz-install -g -u, but if + you use the short-circuit oz-customize or oz-generate-icicle, the user + could have fed us bad data. +20. Currently we use libguestfs to extract the installation ISO, make changes + to it, and then rebundle the ISO using mkisofs. This is time consuming, + partially because we are doing two copies of the ISO (once from the ISO to + the filesystem, and once from the filesystem to the new ISO), and partially + because we are only modifying a very small portion of the ISO. We can + improve this situation and get rid of one whole copy by mounting the ISO + with fuseiso/guestmount, symlinking most of the contents to a temporary + directory, and only modifying the small bits we need to. Then we can pack + the whole thing back up using mkisofs. This gets rid of the first copy, so + it should cut our initial time in half (plus or minus the page cache). +21. Change setup.py to make the directories /var/lib/oz/*. Since that is + repeated in both of the RPM and deb building stuff, it would be better to + just do it on python setup.py install instead. diff -Nru oz-0.15.0/.travis.yml oz-0.16.0/.travis.yml --- oz-0.15.0/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ oz-0.16.0/.travis.yml 2017-08-08 18:46:43.000000000 +0000 @@ -0,0 +1,33 @@ +language: python + +python: +# - "3.3" +# - "3.2" + - "2.7" + +before_install: + - sudo add-apt-repository -y ppa:pdffs/precise-virt + - sudo apt-get update + - sudo apt-get install -qq genisoimage libvirt-dev mtools openssh-client python-dev python-guestfs swig libssl1.0.0 python-m2crypto python-libvirt + +# Travis uses an isolated virtualenv (see http://about.travis-ci.org/docs/user/languages/python/#Travis-CI-Uses-Isolated-virtualenvs) +# Install the system python packages to get their deps and then install the pip version to have them locally +install: + - pip install -r requirements.txt + - sudo cp /usr/lib/python2.7/dist-packages/*guestfs* $VIRTUAL_ENV/lib/python$TRAVIS_PYTHON_VERSION/site-packages/ + - python setup.py install + - pip install coverage + - pip install coveralls + - py.test --genscript=runtests.py + +env: + - TESTFOLDER=tdl + - TESTFOLDER=guest + - TESTFOLDER=ozutil + - TESTFOLDER=factory + +script: + - coverage run -p --source=oz runtests.py --verbose --tb=short tests/$TESTFOLDER + +after_success: + - coveralls