diff -Nru maas-0.1+bzr462+dfsg/contrib/maas_local_settings_sample.py maas-0.1+bzr482+dfsg/contrib/maas_local_settings_sample.py
--- maas-0.1+bzr462+dfsg/contrib/maas_local_settings_sample.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/contrib/maas_local_settings_sample.py 2012-04-19 04:11:04.000000000 +0000
@@ -70,5 +70,4 @@
}
# The location of the Provisioning API XML-RPC endpoint.
-from getpass import getuser
-PSERV_URL = "http://%s:password@localhost:5241/api" % getuser()
+PSERV_URL = "http://maas:password@localhost:5241/api"
diff -Nru maas-0.1+bzr462+dfsg/contrib/wsgi.py maas-0.1+bzr482+dfsg/contrib/wsgi.py
--- maas-0.1+bzr462+dfsg/contrib/wsgi.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/contrib/wsgi.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""WSGI Application."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/debian/changelog maas-0.1+bzr482+dfsg/debian/changelog
--- maas-0.1+bzr462+dfsg/debian/changelog 2012-04-12 20:46:28.000000000 +0000
+++ maas-0.1+bzr482+dfsg/debian/changelog 2012-04-19 04:20:52.000000000 +0000
@@ -1,3 +1,27 @@
+maas (0.1+bzr482+dfsg-0ubuntu1) precise-proposed; urgency=low
+
+ * New upstream release (Fixes LP: #981103)
+ * debian/maas.postinst:
+ - Make sure rabbitmq and postgresql are started on upgrade (LP: #981282)
+ - Handle upgrades from any lower than 0.1+bzr462+dfsg-0ubuntu1 to
+ correctly re-generate passwords, and not have db sync/migrate issues
+ as config has changed upstream.
+ - Correctly set Passwords for PSERV, otherwise it won't set new passwords.
+ * Allow MAAS_DEFAULT_URL reconfiguration. (LP: #980970)
+ - debian/maas.config: Add reconfigure validation to correctly allow it,
+ and ask a question.
+ - debian/maas.postinst: Reconfigure DEFAULT_MAAS_URL as well as cobbler
+ server and next_server for PXE/Provisioning.
+ - debian/maas.templates: Add debconf question and update info.
+ * Do not lose MAAS_DEFAULT_URL settings on upgrade (LP: #984309)
+ * debian/maas.postinst:
+ - Set cobbler password in between quotes (LP: #984427)
+ - Do not change permissions to maas.log (LP: #980915)
+ * no longer use maas-cloudimg2ephemeral, but rather use premade images
+ at http://maas.ubuntu.com
+
+ -- Andres Rodriguez Tue, 17 Apr 2012 23:44:46 -0700
+
maas (0.1+bzr462+dfsg-0ubuntu1) precise; urgency=low
* New upstream release (LP: #980240)
@@ -38,7 +62,7 @@
* debian/maas.postinst: Update pserv.yaml and maas_local_settings.py to use
password.
- -- Andres Rodriguez Thu, 12 Apr 2012 16:46:22 -0400
+ -- Andres Rodriguez Thu, 12 Apr 2012 16:37:53 -0400
maas (0.1+bzr415+dfsg-0ubuntu2) precise; urgency=low
diff -Nru maas-0.1+bzr462+dfsg/debian/control maas-0.1+bzr482+dfsg/debian/control
--- maas-0.1+bzr462+dfsg/debian/control 2012-04-12 20:38:52.000000000 +0000
+++ maas-0.1+bzr482+dfsg/debian/control 2012-04-19 04:19:51.000000000 +0000
@@ -11,7 +11,7 @@
Architecture: all
Depends: apache2,
avahi-daemon,
- cobbler,
+ maas-provision (>= 2.2.2),
dbconfig-common,
distro-info,
libapache2-mod-wsgi,
diff -Nru maas-0.1+bzr462+dfsg/debian/maas.config maas-0.1+bzr482+dfsg/debian/maas.config
--- maas-0.1+bzr462+dfsg/debian/maas.config 2012-04-12 20:38:52.000000000 +0000
+++ maas-0.1+bzr482+dfsg/debian/maas.config 2012-04-19 04:19:51.000000000 +0000
@@ -5,15 +5,15 @@
# creates question
set_question() {
- if ! db_fget "$1" seen; then
- db_register dbconfig-common/dbconfig-install "$1"
- db_subst "$1" ID "$1"
- db_fget "$1" seen
- fi
- if [ "$RET" = false ]; then
- db_set "$1" "$2"
- db_fset "$1" seen true
- fi
+ if ! db_fget "$1" seen; then
+ db_register dbconfig-common/dbconfig-install "$1"
+ db_subst "$1" ID "$1"
+ db_fget "$1" seen
+ fi
+ if [ "$RET" = false ]; then
+ db_set "$1" "$2"
+ db_fset "$1" seen true
+ fi
}
# source dbconfig-common shell library, and call the hook function
@@ -30,4 +30,15 @@
set_question maas/dbconfig-install true
set_question maas/pgsql/app-pass ""
dbc_go maas $@
+
+elif [ "$1" = "reconfigure" ] || [ -n "$DEBCONF_RECONFIGURE" ]; then
+ db_get maas/default-maas-url || true
+ if [ -n "$RET" ]; then
+ db_set maas/default-maas-url "$RET"
+ else
+ ipaddr=$(awk '$1 == "DEFAULT_MAAS_URL" { split($0,array,"/")} END{print array[3] }' /etc/maas/maas_local_settings.py)
+ db_set maas/default-maas-url "$ipaddr"
+ fi
+ db_input low maas/default-maas-url || true
+ db_go
fi
diff -Nru maas-0.1+bzr462+dfsg/debian/maas.install maas-0.1+bzr482+dfsg/debian/maas.install
--- maas-0.1+bzr462+dfsg/debian/maas.install 2012-04-12 20:38:52.000000000 +0000
+++ maas-0.1+bzr482+dfsg/debian/maas.install 2012-04-19 04:19:51.000000000 +0000
@@ -25,7 +25,6 @@
man/maas-import-isos.8 usr/share/man/man8
scripts/maas-import-isos usr/sbin
scripts/maas-import-ephemerals usr/sbin
-scripts/maas-cloudimg2ephemeral usr/sbin
debian/extras/maas usr/bin
debian/extras/20-maas.conf etc/rsyslog.d
debian/extras/maas_remote_syslog_compress etc/cron.d
diff -Nru maas-0.1+bzr462+dfsg/debian/maas.postinst maas-0.1+bzr482+dfsg/debian/maas.postinst
--- maas-0.1+bzr462+dfsg/debian/maas.postinst 2012-04-12 20:38:52.000000000 +0000
+++ maas-0.1+bzr482+dfsg/debian/maas.postinst 2012-04-19 04:19:51.000000000 +0000
@@ -21,6 +21,30 @@
fi
}
+restart_rabbitmq(){
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ invoke-rc.d rabbitmq-server restart || true
+ else
+ /etc/init.d/rabbitmq-server restart || true
+ fi
+}
+
+restart_postgresql(){
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ invoke-rc.d --force postgresql restart || true
+ else
+ /etc/init.d/postgresql restart || true
+ fi
+}
+
+restart_cobbler(){
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ invoke-rc.d cobbler restart || true
+ else
+ /etc/init.d/cobbler restart || true
+ fi
+}
+
add_user_group(){
local user="maas"
local group="maas"
@@ -46,19 +70,19 @@
htpasswd -D /etc/cobbler/users.digest "maas" || true
printf "maas:Cobbler:$hash\n" >> /etc/cobbler/users.digest
- if grep -qs "^\ \{1,\}password: [a-zA-Z0-9]\{1,\}$" /etc/maas/pserv.yaml; then
- sed -i "s/^\ \{1,\}password: [a-zA-Z0-9]\{1,\}$/ password: "$cblr_pass"/" /etc/maas/pserv.yaml
+ if grep -qs "^\ \{1,\}password:.*$" /etc/maas/pserv.yaml; then
+ sed -i "s/^\ \{1,\}password:.*$/ password: \""$cblr_pass"\"/" /etc/maas/pserv.yaml
fi
}
configure_maas_pserv_user() {
local pserv_pass=
pserv_pass="$(pwgen -s 20)"
- if grep -qs "^password: \"test\"" /etc/maas/pserv.yaml; then
- sed -i '/^password:/s/"test"/"'"${pserv_pass}"'"/' /etc/maas/pserv.yaml
+ if grep -qs "^password: \".*\"$" /etc/maas/pserv.yaml; then
+ sed -i '/^password:/s/".*"$/"'"${pserv_pass}"'"/' /etc/maas/pserv.yaml
fi
if grep -qs "^PSERV_URL\ =\ " /etc/maas/maas_local_settings.py; then
- sed -i '/^PSERV_URL[ =]/s/:password@/:'"${pserv_pass}"'@/' /etc/maas/maas_local_settings.py
+ sed -i '/^PSERV_URL[ =]/s/maas:.*@/'"maas:${pserv_pass}"'@/' /etc/maas/maas_local_settings.py
fi
}
@@ -103,6 +127,23 @@
ln -sf /var/lib/maas/ephemeral/tgt.conf /etc/tgt/conf.d/maas.conf
}
+configure_maas_default_url() {
+ local ipaddr="$1"
+
+ if grep -qs "^DEFAULT_MAAS_URL\ \= \"[a-zA-Z0-9:/.]\{0,\}\"$" /etc/maas/maas_local_settings.py; then
+ sed -i "s/^DEFAULT_MAAS_URL\ \= \"[a-zA-Z0-9:/.]\{0,\}\"$/DEFAULT_MAAS_URL = \"http:\/\/"$ipaddr"\/\"/" \
+ /etc/maas/maas_local_settings.py
+ fi
+
+ # Replace for PXE
+ if grep -qs "^next_server:.*$" /etc/cobbler/settings; then
+ sed -i "s/^next_server:.*$/next_server: $ipaddr/" /etc/cobbler/settings
+ fi
+ if grep -qs "^server:.*$" /etc/cobbler/settings; then
+ sed -i "s/^server:.*$/server: $ipaddr/" /etc/cobbler/settings
+ fi
+}
+
if [ "$1" = "configure" ] && [ -z "$2" ]; then
#########################################################
################ User/Group Creatiion ##################
@@ -157,11 +198,9 @@
ipaddr=${ipaddr%%/*}
# Set the IP address of the interface with default route
if [ -n "$ipaddr" ]; then
- if grep -qs "^DEFAULT_MAAS_URL\ \= \"[a-zA-Z0-9:/.]\{0,\}\"$" /etc/maas/maas_local_settings.py; then
- sed -i "s/^DEFAULT_MAAS_URL\ \= \"[a-zA-Z0-9:/.]\{0,\}\"$/DEFAULT_MAAS_URL = \"http:\/\/"$ipaddr"\/\"/" \
- /etc/maas/maas_local_settings.py
- fi
+ configure_maas_default_url "$ipaddr"
db_subst maas/installation-note MAAS_URL "$ipaddr"
+ db_set maas/default-maas-url "$ipaddr"
fi
#########################################################
@@ -173,7 +212,6 @@
touch /var/log/maas/maas.log
fi
chown -R maas:maas /var/log/maas
- chmod 620 /var/log/maas/maas.log
chmod -R 775 /var/log/maas/oops
# Create log directory base
@@ -197,11 +235,7 @@
#########################################################
# Handle longpoll/rabbitmq publishing
- if [ -x /usr/sbin/invoke-rc.d ]; then
- invoke-rc.d rabbitmq-server restart || true
- else
- /etc/init.d/rabbitmq-server restart || true
- fi
+ restart_rabbitmq
configure_maas_txlongpoll_rabbitmq_user
#########################################################
@@ -214,11 +248,7 @@
#########################################################
# Need to for postgresql start so it doesn't fail on the installer
- if [ -x /usr/sbin/invoke-rc.d ]; then
- invoke-rc.d --force postgresql restart || true
- else
- /etc/init.d/postgresql restart || true
- fi
+ restart_postgresql
# Create the database
dbc_go maas $@
@@ -234,28 +264,62 @@
db_input high maas/installation-note || true
db_go
+elif [ "$1" = "reconfigure" ] || [ -n "$DEBCONF_RECONFIGURE" ]; then
+ # Set the IP address of the interface with default route
+ db_get maas/default-maas-url
+ ipaddr="$RET"
+ db_set maas/default-maas-url "$ipaddr"
+ if [ -n "$ipaddr" ]; then
+ configure_maas_default_url "$ipaddr"
+ fi
+ restart_cobbler
+
elif [ "$1" = "configure" ] && dpkg --compare-versions "$2" gt 0.1+bzr266+dfsg-0ubuntu1; then
# If upgrading to any later package version, then upgrade db.
if [ -x /usr/sbin/invoke-rc.d ]; then
invoke-rc.d apache2 stop || true
fi
+ # make sure postgresql is running
+ restart_postgresql
+
# If upgrading from any version lower than 0.1+bzr445+dfsg-0ubuntu1
# we need to update the user/group.
if dpkg --compare-versions "$2" lt 0.1+bzr445+dfsg-0ubuntu1; then
+ # add user/group
add_user_group
+ # set correct permissions
chown -R maas:maas /var/lib/maas/
chown -R maas:maas /var/log/maas
chown -R syslog:syslog /var/log/maas/rsyslog
+ fi
+ if dpkg --compare-versions "$2" lt 0.1+bzr459+dfsg-0ubuntu1; then
+ configure_maas_tgt
+ fi
+ # If upgrading from any version lower than 0.1+bzr462+dfsg-0ubuntu1
+ # we need to regenerate the passwords and update configs.
+ if dpkg --compare-versions "$2" lt 0.1+bzr473+dfsg-0ubuntu1; then
+ # configure maas user for cobbler
configure_maas_cobbler_user
+ # configure pserv user for cobbler
configure_maas_pserv_user
+ # configure maas default url
+ db_get maas/default-maas-url
+ ipaddr="$RET"
+ # If nothing in the database, obtain the IP from cobbler settings
+ # which is the same as MAAS_DEFAULT_URL
+ if [ -z "$ipaddr" ]; then
+ ipaddr=$(awk '$1 == "server:" { print $2 }' /etc/cobbler/settings)
+ db_set maas/default-maas-url "$ipaddr"
+ fi
+ configure_maas_default_url "$ipaddr"
+ # make sure rabbitmq is running
+ restart_rabbitmq
configure_maas_txlongpoll_rabbitmq_user
+ # handle database upgrade
dbc_go maas $@
configure_maas_database "$dbc_dbpass"
fi
- if dpkg --compare-versions "$2" lt 0.1+bzr459+dfsg-0ubuntu1; then
- configure_maas_tgt
- fi
maas_sync_migrate_db
restart_apache2
diff -Nru maas-0.1+bzr462+dfsg/debian/maas.templates maas-0.1+bzr482+dfsg/debian/maas.templates
--- maas-0.1+bzr462+dfsg/debian/maas.templates 2012-04-12 20:38:52.000000000 +0000
+++ maas-0.1+bzr482+dfsg/debian/maas.templates 2012-04-19 04:19:51.000000000 +0000
@@ -6,3 +6,17 @@
can access the MAAS Web interface here:
.
http://${MAAS_URL}/MAAS
+ .
+ If the automatically detected address above is not in the same
+ network as the MAAS clients, you need to reconfigure it:
+ .
+ sudo dpkg-reconfigure maas
+
+Template: maas/default-maas-url
+Type: string
+_Description: Ubuntu MAAS PXE/Provisioning network address:
+ The Ubuntu MAAS Server automatically detects the IP address
+ that is used for PXE and provisioning. However, it needs to be
+ in the same network as the clients. If the automatically
+ detected address is not in the same network as the clients, it
+ must be changed.
diff -Nru maas-0.1+bzr462+dfsg/etc/maas/commissioning-user-data maas-0.1+bzr482+dfsg/etc/maas/commissioning-user-data
--- maas-0.1+bzr462+dfsg/etc/maas/commissioning-user-data 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/etc/maas/commissioning-user-data 2012-04-19 04:11:04.000000000 +0000
@@ -73,7 +73,13 @@
exit 1
}
+shutdown() {
+ echo "Finished, powering off."
+ /sbin/shutdown -P now
+}
+
main() {
+ trap shutdown EXIT
# the main function, actually execute stuff that is written below
local script total=0 creds=""
diff -Nru maas-0.1+bzr462+dfsg/etc/maas/import_ephemerals maas-0.1+bzr482+dfsg/etc/maas/import_ephemerals
--- maas-0.1+bzr462+dfsg/etc/maas/import_ephemerals 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/etc/maas/import_ephemerals 2012-04-19 04:11:04.000000000 +0000
@@ -7,7 +7,7 @@
#EPH_KOPTS_ISCSI="ip=dhcp iscsi_target_name=@@iscsi_target@@ iscsi_target_ip=@@iscsi_target_ip@@ iscsi_target_port=3260"
#EPH_KOPTS_ROOT="root=cloudimg-rootfs ro"
#EPH_KOPTS_LOGGING="log_host=@@server_ip@@ log_port=514"
-#EPH_UPDATE_CMD="maas-cloudimg2ephemeral"
+#EPH_UPDATE_CMD=""
#TARGET_NAME_PREFIX="iqn.2004-05.com.ubuntu:maas:"
#DATA_DIR="/var/lib/maas/ephemeral"
#RELEASES="precise"
diff -Nru maas-0.1+bzr462+dfsg/scripts/maas-cloudimg2ephemeral maas-0.1+bzr482+dfsg/scripts/maas-cloudimg2ephemeral
--- maas-0.1+bzr462+dfsg/scripts/maas-cloudimg2ephemeral 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/scripts/maas-cloudimg2ephemeral 1970-01-01 00:00:00.000000000 +0000
@@ -1,493 +0,0 @@
-#!/bin/bash
-#
-# maas-cloudimg2ephemeral - update a cloud image to make it sufficient
-# for use as a maas ephemeral image
-#
-# Copyright (C) 2011-2012 Canonical
-#
-# Authors:
-# Scott Moser
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, version 3 of the License.
-#
-# This program 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-
-VERBOSITY=0
-
-error() { echo "$@" 1>&2; }
-errorp() { printf "$@" 1>&2; }
-fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
-failp() { [ $# -eq 0 ] || errorp "$@"; exit 1; }
-
-Usage() {
- cat <&2; [ $# -eq 0 ] || error "$@"; exit 1; }
-cleanup() {
- [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || {
- unmount_under "${TEMP_D}" &&
- rm -Rf "${TEMP_D}"
- }
-}
-
-debug() {
- local level=${1}; shift;
- [ "${level}" -gt "${VERBOSITY}" ] && return
- error "${@}"
-}
-
-unmount_under() {
- # unmount_under(dir)
- # unmount all mounts under 'dir'
- [ -f /proc/mounts ] ||
- { error "/proc/mounts not a file"; return 1; }
- tac /proc/mounts | sh -c '
- under=$1
- while read s mp t opt a b ; do
- [ "${mp#${under}}" != "${mp}" ] || continue;
- umount $mp ||
- { echo "failed umount $mp, waiting, trying again" 1>&2;
- sleep 10;
- umount $mp || exit 1; }
- done' -- "$1"
-}
-
-loop_mount() {
- # Create more loop nodes, if necessary
- local mounts=$(grep -c /dev/loop /proc/mounts) || mounts=0
- local loops=$(ls /dev/loop* | wc -l) || loops=0
- if [ $mounts -ge $loops ]; then
- mknod -m 660 /dev/loop$loops b 7 $loops &&
- chown root:disk /dev/loop$loops ||
- return 1
- fi
- # Do the loop mount
- mount -o loop "$1" "$2"
-}
-
-mount_callback_umount() {
- # mount_callback_umount(img_or_device, func, args)
- # mount the image given, call function with args,
- # umount the image, return function's exit value
- local device="$1" cb="$2" mp="" opts="" ret=0 m=""
- shift 2;
- mp=$(mktemp -d "$TEMP_D/mp.XXXXXX")
- if [ -b "$device" ]; then
- mount $opts "$device" "$mp" || return 1
- else
- loop_mount "$device" "$mp" || return 1
- fi
- for m in "/proc" "/sys"; do
- [ -d "$mp/$m" ] || continue
- mount --bind "$m" "$mp/$m" || {
- error "failed to mount $mp/$m";
- unmount_under "$mp";
- return 1;
- }
- done
- "$cb" "$mp" "$@"
- ret=$?
- unmount_under "$mp" && rmdir "$mp" ||
- { error "WARN! failed to umount $device from $mp"; return 2; }
- return $ret
-}
-
-add_initramfs_hooks() {
- local dir="$1" idir="" hook="" script=""
- idir="$dir/etc/initramfs-tools"
- mkdir -p "$idir/hooks" "$idir/scripts/init-bottom" ||
- return 1
- hook="$idir/hooks/overlay-ro"
- cat > "$hook" <<"ENDEND"
-#!/bin/sh
-set -e
-
-PREREQS=""
-case $1 in
- prereqs) echo "${PREREQS}"; exit 0;;
-esac
-
-. /usr/share/initramfs-tools/hook-functions
-
-##
-manual_add_modules overlayfs
-force_load overlayfs
-
-# vi: ts=4 noexpandtab
-ENDEND
-
- [ $? -eq 0 ] || { error "failed to write $hook"; return 1; }
-
- script="$idir/scripts/init-bottom/root-ro"
- cat > "$script" <<"ENDEND"
-#!/bin/sh
-# Copyright, 2012 Axel Heider
-#
-# Based on scrpts from
-# Sebastian P.
-# Nicholas A. Schembri State College PA USA
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-# .
-#
-#
-# Tested with Ubuntu 11.10
-#
-# Notes:
-# * no changes to the root fs are made by this script.
-# * if /home/[user] is on the RO root fs, files are in ram and not saved.
-#
-# Install:
-# put this file in /etc/initramfs-tools/scripts/init-bottom/root-ro
-# chmod 0755 root-ro
-# optional: clean up menu.lst, update-grub
-# update-initramfs -u
-#
-# Disable read-only root fs
-# * option 1: kernel boot parameter "disable-root-ro=true"
-# * option 2: create file "/disable-root-ro"
-#
-# ROOT_RO_DRIVER variable controls which driver isused for the ro/rw layering
-# Supported drivers are: overlayfs, aufs
-# the kernel parameter "root-ro-driver=[driver]" can be used to initialize
-# the variable ROOT_RO_DRIVER. If nothing is given, overlayfs is used.
-#
-
-# no pre requirement
-PREREQ=""
-
-prereqs()
-{
- echo "${PREREQ}"
-}
-
-case "$1" in
- prereqs)
- prereqs
- exit 0
- ;;
-esac
-
-. /scripts/functions
-
-MYTAG="root-ro"
-DISABLE_MAGIC_FILE="/disable-root-ro"
-
-# parse kernel boot command line
-ROOT_RO_DRIVER=
-DISABLE_ROOT_RO=
-for CMD_PARAM in $(cat /proc/cmdline); do
- case ${CMD_PARAM} in
- disable-root-ro=*)
- DISABLE_ROOT_RO=${CMD_PARAM#disable-root-ro=}
- ;;
- root-ro-driver=*)
- ROOT_RO_DRIVER=${CMD_PARAM#root-ro-driver=}
- ;;
- esac
-done
-
-# check if read-only root fs is disabled
-if [ ! -z "${DISABLE_ROOT_RO}" ]; then
- log_warning_msg "${MYTAG}: disabled, found boot parameter disable-root-ro=${DISABLE_ROOT_RO}"
- exit 0
-fi
-if [ -e "${rootmnt}${DISABLE_MAGIC_FILE}" ]; then
- log_warning_msg "${MYTAG}: disabled, found file ${rootmnt}${DISABLE_MAGIC_FILE}"
- exit 0
-fi
-
-# generic settings
-# ${ROOT} and ${rootmnt} are predefined by caller of this script. Note that
-# the root fs ${rootmnt} it mounted readonly on the initrams, which fits nicely
-# for our purposes.
-ROOT_RW=/mnt/root-rw
-ROOT_RO=/mnt/root-ro
-
-# check if ${ROOT_RO_DRIVER} is defined, otherwise set default
-if [ -z "${ROOT_RO_DRIVER}" ]; then
- ROOT_RO_DRIVER=overlayfs
-fi
-# settings based in ${ROOT_RO_DRIVER}, stop here if unsupported.
-case ${ROOT_RO_DRIVER} in
- overlayfs)
- MOUNT_PARMS="-t overlayfs -o lowerdir=${ROOT_RO},upperdir=${ROOT_RW} overlayfs-root ${rootmnt}"
- ;;
- aufs)
- MOUNT_PARMS="-t aufs -o dirs=${ROOT_RW}:${ROOT_RO}=ro aufs-root ${rootmnt}"
- ;;
- *)
- panic "${MYTAG} ERROR: invalide ROOT_RO_DRIVER ${ROOT_RO_DRIVER}"
- ;;
-esac
-
-
-# check if kernel module exists
-modprobe -qb ${ROOT_RO_DRIVER}
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: missing kernel module ${ROOT_RO_DRIVER}"
- exit 0
-fi
-
-# make the mount point on the init root fs ${ROOT_RW}
-[ -d ${ROOT_RW} ] || mkdir -p ${ROOT_RW}
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to create ${ROOT_RW}"
- exit 0
-fi
-
-# make the mount point on the init root fs ${ROOT_RO}
-[ -d ${ROOT_RO} ] || mkdir -p ${ROOT_RO}
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to create ${ROOT_RO}"
- exit 0
-fi
-
-# mount a tempfs using the device name tmpfs-root at ${ROOT_RW}
-mount -t tmpfs tmpfs-root ${ROOT_RW}
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to create tmpfs"
- exit 0
-fi
-
-
-# root is mounted on ${rootmnt}, move it to ${ROOT_RO}.
-mount --move ${rootmnt} ${ROOT_RO}
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to move root away from ${rootmnt} to ${ROOT_RO}"
- exit 0
-fi
-
-# there is nothing left at ${rootmnt} now. So for any error we get we should
-# either do recovery to restore ${rootmnt} for drop to a initramfs shell using
-# "panic". Otherwise the boot process is very likely to fail with even more
-# errors and leave the system in a wired state.
-
-# mount virtual fs ${rootmnt} with rw-fs ${ROOT_RW} on top or ro-fs ${ROOT_RO}.
-mount ${MOUNT_PARMS}
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to create new ro/rw layerd ${rootmnt}"
- # do recovery and try resoring the mount for ${rootmnt}
- mount --move ${ROOT_RO} ${rootmnt}
- if [ $? -ne 0 ]; then
- # thats badm, drpo to s shell to let the user try fixing this
- panic "${MYTAG} RECOVERY ERROR: failed to move ${ROOT_RO} back to ${rootmnt}"
- fi
- exit 0
-fi
-
-# now the real root fs is on ${ROOT_RO} of the init file system, our layered
-# root fs is set up at ${rootmnt}. So we can write anywhere in {rootmnt} and the
-# changes will end up in ${ROOT_RW} while ${ROOT_RO} it not touched. However
-# ${ROOT_RO} and ${ROOT_RW} are on the initramfs root fs, which will be removed
-# an replaced by ${rootmnt}. Thus we must move ${ROOT_RO} and ${ROOT_RW} to the
-# rootfs visible later, ie. ${rootmnt}${ROOT_RO} and ${rootmnt}${ROOT_RO}.
-# Since the layered ro/rw is already up, these changes also end up on
-# ${ROOT_RW} while ${ROOT_RO} is not touched.
-
-# move mount from ${ROOT_RO} to ${rootmnt}${ROOT_RO}
-[ -d ${rootmnt}${ROOT_RO} ] || mkdir -p ${rootmnt}${ROOT_RO}
-mount --move ${ROOT_RO} ${rootmnt}${ROOT_RO}
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to move ${ROOT_RO} to ${rootmnt}${ROOT_RO}"
- exit 0
-fi
-
-# move mount from ${ROOT_RW} to ${rootmnt}${ROOT_RW}
-[ -d ${rootmnt}${ROOT_RW} ] || mkdir -p ${rootmnt}${ROOT_RW}
-mount --move ${ROOT_RW} ${rootmnt}${ROOT_RW}
-if [ $? -ne 0 ]; then
- s "${MYTAG}: ERROR: failed to move ${ROOT_RW} to ${rootmnt}${ROOT_RW}"
- exit 0
-fi
-
-# technically, everything is set up nicely now. Since ${rootmnt} had beend
-# mounted read-only on the initfamfs already, ${rootmnt}${ROOT_RO} is it, too.
-# Now we init process could run - but unfortunately, we may have to prepare
-# some more things here.
-# Basically, there are two ways to deal with the read-only root fs. If the
-# system is made aware of this, things can be simplified a lot.
-# If it is not, things need to be done to our best knowledge.
-#
-# So we assume here, the system does not really know about our read-only root fs.
-#
-# Let's deal with /etc/fstab first. It usually contains an entry for the root
-# fs, which is no longer valid now. We have to remove it and add our new
-# ${ROOT_RO} entry.
-# Remember we are still on the initramfs root fs here, so we have to work on
-# ${rootmnt}/etc/fstab. The original fstab is ${rootmnt}${ROOT_RO}/etc/fstab.
-ROOT_TYPE=$(cat /proc/mounts | grep ${ROOT} | cut -d' ' -f3)
-ROOT_OPTIONS=$(cat /proc/mounts | grep ${ROOT} | cut -d' ' -f4)
-cat <${rootmnt}/etc/fstab
-#
-# This fstab is in RAM, the real one can be found at ${ROOT_RO}/etc/fstab
-# The original entry for '/' and all swap files have been removed. The new
-# entry for the read-only the real root fs follows. Write access can be
-# enabled using:
-# sudo mount -o remount,rw ${ROOT_RO}
-# re-mounting it read-only is done using:
-# sudo mount -o remount,ro ${ROOT_RO}
-#
-
-${ROOT} ${ROOT_RO} ${ROOT_TYPE} ${ROOT_OPTIONS} 0 0
-
-#
-# remaining entries from the original ${ROOT_RO}/etc/fstab follow.
-#
-EOF
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to modify /etc/fstab (step 1)"
- #exit 0
-fi
-
-#remove root entry and swap from fstab
-cat ${rootmnt}${ROOT_RO}/etc/fstab | grep -v ' / ' | grep -v swap >>${rootmnt}/etc/fstab
-if [ $? -ne 0 ]; then
- log_failure_msg "${MYTAG} ERROR: failed to modify etc/fstab (step 2)"
- #exit 0
-fi
-
-# now we are done. Additinal steps may be necessary depending on the actualy
-# distribution and/or its configuration.
-
-log_success_msg "${MYTAG} sucessfully set up ro/tmpfs-rw layered root fs using ${ROOT_RO_DRIVER}"
-
-exit 0
-ENDEND
- [ $? -eq 0 ] || { error "failed to write $script"; return 1; }
- chmod 755 "$hook" "$script" ||
- { error "failed to chmod $hook, $script"; return 1; }
-}
-
-apply_updates() {
- # apply_updates(dir, kernel_out, initramfs_out)
- # update directory given, and pull out kernel and initramfs
- # to given locations
- local dir=$1 kernel_out=$2 initrd_out=$3
- if [ -f "$dir/etc/resolv.conf" ]; then
- mv "$dir/etc/resolv.conf" "$dir/etc/resolv.conf.dist" || return 1
- fi
- cp "/etc/resolv.conf" "$dir/etc/resolv.conf" ||
- return 1
-
- cat > "$dir/usr/sbin/policy-rc.d" <<"EOF"
-#!/bin/sh
-while true; do
- case "$1" in
- -*) shift ;;
- makedev) exit 0 ;;
- x11-common) exit 0 ;;
- *) exit 101 ;;
- esac
-done
-EOF
- [ $? -eq 0 ] && chmod 755 "$dir/usr/sbin/policy-rc.d" ||
- { error "failed to write policy-rc.d"; return 1; }
-
- add_initramfs_hooks "$dir" || return
-
- local prox="" apt_opts=""
- out=$(apt-config shell prox Acquire::HTTP::Proxy) &&
- eval $out && [ -n "$prox" ] &&
- apt_opts="--option=Acquire::HTTP::Proxy=${prox}"
-
- apt_opts="${apt_opts} --option=Dpkg::Options::=--force-confold"
- [ -n "${apt_opts}" ] &&
- debug 1 "using apt options ${apt_opts} for install"
-
- LC_ALL=C DEBIAN_FRONTEND=noninteractive \
- apt_opts="${apt_opts}" chroot "$dir" sh -c '
- mkdir -p /etc/iscsi && touch /etc/iscsi/iscsi.initramfs &&
- apt-get -q ${apt_opts} update &&
- apt-get remove "linux.*virtual" ${apt_opts} --assume-yes &&
- apt-get ${apt_opts} install -q -y linux-server open-iscsi ||
- exit
- k=""
- for i in /boot/vmlinuz-*; do
- [ "${i%-virtual}" = "${i}" ] && k=${i}; done
- ver=${k##*/vmlinuz-}
- mkinitramfs -o /tmp/initrd.img $ver &&
- cp $k /tmp/kernel.img && chmod ugo+r /tmp/kernel.img' .
VERBOSITY=0
-REMOTE_IMAGES_MIRROR="https://cloud-images.ubuntu.com"
+REMOTE_IMAGES_MIRROR="https://maas.ubuntu.com/images"
CONSOLE="ttyS0,9600n8"
EPH_KOPTS_CONSOLE="console=$CONSOLE"
EPH_KOPTS_ISCSI="ip=dhcp iscsi_target_name=@@iscsi_target@@ iscsi_target_ip=@@iscsi_target_ip@@ iscsi_target_port=3260"
EPH_KOPTS_ROOT="root=LABEL=cloudimg-rootfs ro"
EPH_KOPTS_LOGGING="log_host=@@server_ip@@ log_port=514"
-EPH_UPDATE_CMD="maas-cloudimg2ephemeral"
+EPH_UPDATE_CMD=""
TARGET_NAME_PREFIX="iqn.2004-05.com.ubuntu:maas:"
DATA_DIR="/var/lib/maas/ephemeral"
CONFIG="/etc/maas/import_ephemerals"
RELEASES="precise"
ARCHES="amd64 i386"
-BUILD_NAME="server"
+BUILD_NAME="ephemeral"
STREAM="released"
KSDIR="/var/lib/cobbler/kickstarts"
KICKSTART="$KSDIR/maas-commissioning.preseed"
diff -Nru maas-0.1+bzr462+dfsg/scripts/maas-import-isos maas-0.1+bzr482+dfsg/scripts/maas-import-isos
--- maas-0.1+bzr462+dfsg/scripts/maas-import-isos 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/scripts/maas-import-isos 2012-04-19 04:11:04.000000000 +0000
@@ -190,3 +190,6 @@
# Sync changes with cobbler daemon
cobbler sync
+
+# Clear MAAS' cache to force the profile check.
+maas clearcache --key=profile-check-done
diff -Nru maas-0.1+bzr462+dfsg/setup.py maas-0.1+bzr482+dfsg/setup.py
--- maas-0.1+bzr462+dfsg/setup.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/setup.py 2012-04-19 04:11:04.000000000 +0000
@@ -5,6 +5,7 @@
"""Distutils installer for maas."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maas/demo.py maas-0.1+bzr482+dfsg/src/maas/demo.py
--- maas-0.1+bzr462+dfsg/src/maas/demo.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maas/demo.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django DEMO settings for maas project."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maas/development.py maas-0.1+bzr482+dfsg/src/maas/development.py
--- maas-0.1+bzr462+dfsg/src/maas/development.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maas/development.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django DEVELOPMENT settings for maas project."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maas/__init__.py maas-0.1+bzr482+dfsg/src/maas/__init__.py
--- maas-0.1+bzr462+dfsg/src/maas/__init__.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maas/__init__.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""MAAS web."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maas/settings.py maas-0.1+bzr482+dfsg/src/maas/settings.py
--- maas-0.1+bzr462+dfsg/src/maas/settings.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maas/settings.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django settings for maas project."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -174,6 +175,12 @@
# Don't forget to use absolute paths, not relative paths.
)
+CACHES = {
+ 'default': {
+ 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
+ }
+}
+
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
@@ -208,7 +215,12 @@
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
+ # ErrorsMiddleware catches ExternalComponentException and redirects.
+ # Specialised error handling middleware (like APIErrorsMiddleware)
+ # should be placed after it.
+ 'maasserver.middleware.ErrorsMiddleware',
'maasserver.middleware.APIErrorsMiddleware',
+ 'maasserver.middleware.ExternalComponentsMiddleware',
'metadataserver.middleware.MetadataErrorsMiddleware',
'django.middleware.transaction.TransactionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
diff -Nru maas-0.1+bzr462+dfsg/src/maas/tests/test_maas.py maas-0.1+bzr482+dfsg/src/maas/tests/test_maas.py
--- maas-0.1+bzr462+dfsg/src/maas/tests/test_maas.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maas/tests/test_maas.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test the maas package."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maas/urls.py maas-0.1+bzr482+dfsg/src/maas/urls.py
--- maas-0.1+bzr462+dfsg/src/maas/urls.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maas/urls.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""URL configuration for the maas project."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/api_auth.py maas-0.1+bzr482+dfsg/src/maasserver/api_auth.py
--- maas-0.1+bzr462+dfsg/src/maasserver/api_auth.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/api_auth.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""OAuth authentication for the various APIs."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/api.py maas-0.1+bzr482+dfsg/src/maasserver/api.py
--- maas-0.1+bzr462+dfsg/src/maasserver/api.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/api.py 2012-04-19 04:11:04.000000000 +0000
@@ -47,6 +47,7 @@
"""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/components.py maas-0.1+bzr482+dfsg/src/maasserver/components.py
--- maas-0.1+bzr462+dfsg/src/maasserver/components.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/components.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""MAAS components management."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/context_processors.py maas-0.1+bzr482+dfsg/src/maasserver/context_processors.py
--- maas-0.1+bzr462+dfsg/src/maasserver/context_processors.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/context_processors.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Context processors."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/exceptions.py maas-0.1+bzr482+dfsg/src/maasserver/exceptions.py
--- maas-0.1+bzr462+dfsg/src/maasserver/exceptions.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/exceptions.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,12 +4,14 @@
"""Exceptions."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
__metaclass__ = type
__all__ = [
+ "ExternalComponentException",
"MAASException",
"MAASAPIBadRequest",
"MAASAPIException",
@@ -43,6 +45,10 @@
api_error = httplib.INTERNAL_SERVER_ERROR
+class ExternalComponentException(MAASAPIException):
+ """An external component failed."""
+
+
class MAASAPIBadRequest(MAASAPIException):
api_error = httplib.BAD_REQUEST
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/fields.py maas-0.1+bzr482+dfsg/src/maasserver/fields.py
--- maas-0.1+bzr462+dfsg/src/maasserver/fields.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/fields.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Custom model fields."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/forms.py maas-0.1+bzr482+dfsg/src/maasserver/forms.py
--- maas-0.1+bzr462+dfsg/src/maasserver/forms.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/forms.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Forms."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/__init__.py maas-0.1+bzr482+dfsg/src/maasserver/__init__.py
--- maas-0.1+bzr462+dfsg/src/maasserver/__init__.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/__init__.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""MAAS Server application."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/maasavahi.py maas-0.1+bzr482+dfsg/src/maasserver/maasavahi.py
--- maas-0.1+bzr462+dfsg/src/maasserver/maasavahi.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/maasavahi.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Glue to publish MAAS over Avahi."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/management/commands/clearcache.py maas-0.1+bzr482+dfsg/src/maasserver/management/commands/clearcache.py
--- maas-0.1+bzr462+dfsg/src/maasserver/management/commands/clearcache.py 1970-01-01 00:00:00.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/management/commands/clearcache.py 2012-04-19 04:11:04.000000000 +0000
@@ -0,0 +1,34 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Django command: clear the cache."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = [
+ 'Command',
+ ]
+
+from django.core.cache import cache
+from optparse import make_option
+from django.core.management.base import BaseCommand
+
+
+class Command(BaseCommand):
+ option_list = BaseCommand.option_list + (
+ make_option('--key', dest='username', default=None,
+ help="Specify a specific key to delete."),
+ )
+ help = "Clear the cache (the entire cache or only a specific key)."
+
+ def handle(self, *args, **options):
+ key = options.get('key', None)
+ if key is None:
+ cache.clear()
+ else:
+ cache.delete(key)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/management/commands/createadmin.py maas-0.1+bzr482+dfsg/src/maasserver/management/commands/createadmin.py
--- maas-0.1+bzr462+dfsg/src/maasserver/management/commands/createadmin.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/management/commands/createadmin.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django command: create a superuser with an empty email."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/management/commands/deletedb.py maas-0.1+bzr482+dfsg/src/maasserver/management/commands/deletedb.py
--- maas-0.1+bzr462+dfsg/src/maasserver/management/commands/deletedb.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/management/commands/deletedb.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django command: stop and delete the local database cluster."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/management/commands/gc.py maas-0.1+bzr482+dfsg/src/maasserver/management/commands/gc.py
--- maas-0.1+bzr462+dfsg/src/maasserver/management/commands/gc.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/management/commands/gc.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Custom django command: garabge-collect."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/management/commands/generate_api_doc.py maas-0.1+bzr482+dfsg/src/maasserver/management/commands/generate_api_doc.py
--- maas-0.1+bzr462+dfsg/src/maasserver/management/commands/generate_api_doc.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/management/commands/generate_api_doc.py 2012-04-19 04:11:04.000000000 +0000
@@ -1,3 +1,19 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Django command: generate the API documentation."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = [
+ 'Command',
+ ]
+
from django.core.management.base import BaseCommand
from maasserver.api import (
api_doc_title,
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/management/commands/query.py maas-0.1+bzr482+dfsg/src/maasserver/management/commands/query.py
--- maas-0.1+bzr462+dfsg/src/maasserver/management/commands/query.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/management/commands/query.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django command: access the development database directly in SQL."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/management/commands/runserver.py maas-0.1+bzr482+dfsg/src/maasserver/management/commands/runserver.py
--- maas-0.1+bzr462+dfsg/src/maasserver/management/commands/runserver.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/management/commands/runserver.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django command: run the server. Overrides the default implementation."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/messages.py maas-0.1+bzr482+dfsg/src/maasserver/messages.py
--- maas-0.1+bzr462+dfsg/src/maasserver/messages.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/messages.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Messages."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/middleware.py maas-0.1+bzr482+dfsg/src/maasserver/middleware.py
--- maas-0.1+bzr462+dfsg/src/maasserver/middleware.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/middleware.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -12,6 +13,7 @@
__all__ = [
"AccessMiddleware",
"APIErrorsMiddleware",
+ "ErrorsMiddleware",
"ExceptionMiddleware",
]
@@ -25,6 +27,8 @@
import re
from django.conf import settings
+from django.contrib import messages
+from django.core.cache import cache
from django.core.exceptions import (
PermissionDenied,
ValidationError,
@@ -37,7 +41,10 @@
HttpResponseRedirect,
)
from django.utils.http import urlquote_plus
-from maasserver.exceptions import MAASAPIException
+from maasserver.exceptions import (
+ ExternalComponentException,
+ MAASAPIException,
+ )
def get_relative_path(path):
@@ -94,6 +101,47 @@
return None
+PROFILES_CHECK_DONE_KEY = 'profile-check-done'
+
+# The profiles check done by check_profiles_cached is only done at most once
+# every PROFILE_CHECK_DELAY seconds for efficiency.
+PROFILE_CHECK_DELAY = 2 * 60
+
+
+def check_profiles_cached():
+ """Check Cobbler's profiles. The check is actually done at most once every
+ PROFILE_CHECK_DELAY seconds for performance reasons.
+ """
+ # Avoid circular imports.
+ from maasserver.provisioning import check_profiles
+ if not cache.get(PROFILES_CHECK_DONE_KEY, False):
+ # Mark the profile check as done beforehand as the actual check
+ # might raise an exception.
+ cache.set(PROFILES_CHECK_DONE_KEY, True, PROFILE_CHECK_DELAY)
+ check_profiles()
+
+
+def clear_profiles_check_cache():
+ """Force a profile check next time the MAAS server is accessed."""
+ cache.delete(PROFILES_CHECK_DONE_KEY)
+
+
+class ExternalComponentsMiddleware:
+ """This middleware performs checks for external components (right
+ now only Cobbler is checked) at regular intervals.
+ """
+ def process_request(self, request):
+ # This middleware hijacks the request to perform checks. Any
+ # error raised during these checks should be caught to avoid
+ # disturbing the handling of the request. Proper error reporting
+ # should be handled in the check method itself.
+ try:
+ check_profiles_cached()
+ except Exception:
+ pass
+ return None
+
+
class ExceptionMiddleware:
"""Convert exceptions into appropriate HttpResponse responses.
@@ -163,6 +211,24 @@
path_regex = settings.API_URL_REGEXP
+class ErrorsMiddleware:
+ """Handle ExternalComponentException exceptions in POST requests: add a
+ message with the error string and redirect to the same page (using GET).
+ """
+
+ def process_exception(self, request, exception):
+ should_process_exception = (
+ request.method == 'POST' and
+ isinstance(exception, ExternalComponentException))
+ if should_process_exception:
+ messages.error(request, unicode(exception))
+ return HttpResponseRedirect(request.path)
+ else:
+ # Not an ExternalComponentException or not a POST request: do not
+ # handle it.
+ return None
+
+
class ExceptionLoggerMiddleware:
def process_exception(self, request, exception):
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/migrations/0001_initial.py maas-0.1+bzr482+dfsg/src/maasserver/migrations/0001_initial.py
--- maas-0.1+bzr462+dfsg/src/maasserver/migrations/0001_initial.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/migrations/0001_initial.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Initial maasserver migration."""
from __future__ import (
+ absolute_import,
print_function,
# This breaks South.
#unicode_literals,
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/migrations/0002_macaddress_unique.py maas-0.1+bzr482+dfsg/src/maasserver/migrations/0002_macaddress_unique.py
--- maas-0.1+bzr462+dfsg/src/maasserver/migrations/0002_macaddress_unique.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/migrations/0002_macaddress_unique.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Maasserver migration 0002_macaddress_unique."""
from __future__ import (
+ absolute_import,
print_function,
# This breaks South.
#unicode_literals,
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/migrations/0006_increase_filestorage_filename_length.py maas-0.1+bzr482+dfsg/src/maasserver/migrations/0006_increase_filestorage_filename_length.py
--- maas-0.1+bzr462+dfsg/src/maasserver/migrations/0006_increase_filestorage_filename_length.py 1970-01-01 00:00:00.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/migrations/0006_increase_filestorage_filename_length.py 2012-04-19 04:11:04.000000000 +0000
@@ -0,0 +1,136 @@
+# flake8: noqa
+# SKIP this file when reformatting.
+# The rest of this file was generated by South.
+
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'FileStorage.filename'
+ db.alter_column('maasserver_filestorage', 'filename', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200))
+
+
+ def backwards(self, orm):
+
+ # Changing field 'FileStorage.filename'
+ db.alter_column('maasserver_filestorage', 'filename', self.gf('django.db.models.fields.CharField')(max_length=100, unique=True))
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'maasserver.config': {
+ 'Meta': {'object_name': 'Config'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
+ },
+ 'maasserver.filestorage': {
+ 'Meta': {'object_name': 'FileStorage'},
+ 'data': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ 'maasserver.macaddress': {
+ 'Meta': {'object_name': 'MACAddress'},
+ 'created': ('django.db.models.fields.DateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['maasserver.Node']"}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
+ },
+ 'maasserver.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386'", 'max_length': '10'}),
+ 'created': ('django.db.models.fields.DateField', [], {}),
+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-1bda95d4-876c-11e1-b67e-002215205ce8'", 'unique': 'True', 'max_length': '41'}),
+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'null': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
+ },
+ 'maasserver.sshkey': {
+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
+ 'created': ('django.db.models.fields.DateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.TextField', [], {}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'maasserver.userprofile': {
+ 'Meta': {'object_name': 'UserProfile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+ },
+ 'piston.consumer': {
+ 'Meta': {'object_name': 'Consumer'},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': "orm['auth.User']"})
+ },
+ 'piston.token': {
+ 'Meta': {'object_name': 'Token'},
+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Consumer']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1334543418L'}),
+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
+ }
+ }
+
+ complete_apps = ['maasserver']
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/models.py maas-0.1+bzr482+dfsg/src/maasserver/models.py
--- maas-0.1+bzr462+dfsg/src/maasserver/models.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/models.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""MAAS model objects."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -386,7 +387,7 @@
if constraints.get('name'):
available_nodes = available_nodes.filter(
- system_id=constraints['name'])
+ hostname=constraints['name'])
available_nodes = list(available_nodes[:1])
if len(available_nodes) == 0:
@@ -1066,7 +1067,7 @@
# Unix filenames can be longer than this (e.g. 255 bytes), but leave
# some extra room for the full path, as well as a versioning suffix.
- filename = models.CharField(max_length=100, unique=True, editable=False)
+ filename = models.CharField(max_length=200, unique=True, editable=False)
data = models.FileField(
upload_to=upload_dir, storage=storage, max_length=255)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/provisioning.py maas-0.1+bzr482+dfsg/src/maasserver/provisioning.py
--- maas-0.1+bzr462+dfsg/src/maasserver/provisioning.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/provisioning.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,18 +4,22 @@
"""Interact with the Provisioning API."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
__metaclass__ = type
__all__ = [
+ 'check_profiles',
'get_provisioning_api_proxy',
+ 'get_all_profile_names',
'present_detailed_user_friendly_fault',
'ProvisioningProxy',
]
from functools import partial
+import itertools
from logging import getLogger
from textwrap import dedent
from urllib import urlencode
@@ -28,13 +32,15 @@
post_save,
)
from django.dispatch import receiver
+from django.utils.safestring import mark_safe
from maasserver.components import (
COMPONENT,
discard_persistent_error,
register_persistent_error,
)
-from maasserver.exceptions import MAASAPIException
+from maasserver.exceptions import ExternalComponentException
from maasserver.models import (
+ ARCHITECTURE_CHOICES,
Config,
MACAddress,
Node,
@@ -142,7 +148,7 @@
if user_friendly_text is None:
return None
else:
- return MAASAPIException(dedent(
+ return ExternalComponentException(dedent(
user_friendly_text.lstrip('\n') % params))
@@ -181,6 +187,7 @@
'add_node': [COMPONENT.PSERV, COMPONENT.COBBLER, COMPONENT.IMPORT_ISOS],
'modify_nodes': [COMPONENT.PSERV, COMPONENT.COBBLER],
'delete_nodes_by_name': [COMPONENT.PSERV, COMPONENT.COBBLER],
+ 'get_profiles_by_name': [COMPONENT.PSERV, COMPONENT.COBBLER],
}
# A mapping exception -> component.
@@ -415,16 +422,57 @@
return conversions.get(architecture, architecture)
-def select_profile_for_node(node):
- """Select which profile a node should be configured for."""
- assert node.architecture, "Node's architecture is not known."
- cobbler_arch = name_arch_in_cobbler_style(node.architecture)
+def check_profiles():
+ """Check that Cobbler has profiles defined for all the profiles used by
+ MAAS. If a profile is missing, display a persistent error with an invite
+ to run the maas-import-isos script.
+ """
+ all_profiles = get_all_profile_names()
+ papi = get_provisioning_api_proxy()
+ existing_profiles = set(papi.get_profiles_by_name(all_profiles))
+ missing_profiles = set(all_profiles) - existing_profiles
+ if len(missing_profiles) != 0:
+ # Some profiles are missing: display a persistent component
+ # error.
+ register_persistent_error(
+ COMPONENT.IMPORT_ISOS,
+ mark_safe(
+ """
+ Some of the required system profiles are missing.
+ Run the maas-import-isos script to import Ubuntu isos and
+ create the related profiles:
+ sudo maas-import-isos
+ """))
+
+
+def get_all_profile_names():
+ """Return all the names of the profiles used by MAAS."""
+ architectures = {arch[0] for arch in ARCHITECTURE_CHOICES}
+ commissioning = {True, False}
+ product = itertools.product(architectures, commissioning)
+ profiles = [
+ get_profile_name(architecture, commissioning)
+ for architecture, commissioning in product]
+ return profiles
+
+
+def get_profile_name(architecture, commissioning=False):
+ """Return the profile name for a given architecture and whether the node
+ is commissioning or not."""
+ cobbler_arch = name_arch_in_cobbler_style(architecture)
profile = "maas-%s-%s" % ("precise", cobbler_arch)
- if node.status == NODE_STATUS.COMMISSIONING:
+ if commissioning:
profile += "-commissioning"
return profile
+def select_profile_for_node(node):
+ """Select which profile a node should be configured for."""
+ assert node.architecture, "Node's architecture is not known."
+ commissioning = node.status == NODE_STATUS.COMMISSIONING
+ return get_profile_name(node.architecture, commissioning)
+
+
@receiver(post_save, sender=Node)
def provision_post_save_Node(sender, instance, created, **kwargs):
"""Create or update nodes in the provisioning server."""
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/rabbit.py maas-0.1+bzr482+dfsg/src/maasserver/rabbit.py
--- maas-0.1+bzr462+dfsg/src/maasserver/rabbit.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/rabbit.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Rabbit messaging."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/css/base.css maas-0.1+bzr482+dfsg/src/maasserver/static/css/base.css
--- maas-0.1+bzr462+dfsg/src/maasserver/static/css/base.css 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/css/base.css 2012-04-19 04:11:04.000000000 +0000
@@ -13,5 +13,9 @@
pre {
background-color: #f2f2f2;
padding: 5px 5px 5px 3px;
- display: inline-block;
+ display: block;
+ overflow: auto;
}
+#flash-messages .warning pre {
+ background-color: #ECA918;
+}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/css/components/data_list.css maas-0.1+bzr482+dfsg/src/maasserver/static/css/components/data_list.css
--- maas-0.1+bzr462+dfsg/src/maasserver/static/css/components/data_list.css 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/css/components/data_list.css 2012-04-19 04:11:04.000000000 +0000
@@ -9,3 +9,6 @@
.data-list span {
font-size: 16px;
}
+.data-list .block {
+ word-wrap: break-word;
+ }
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/css/components/table_list.css maas-0.1+bzr482+dfsg/src/maasserver/static/css/components/table_list.css
--- maas-0.1+bzr462+dfsg/src/maasserver/static/css/components/table_list.css 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/css/components/table_list.css 2012-04-19 04:11:04.000000000 +0000
@@ -40,5 +40,5 @@
padding: 2px 0;
}
ul.list .icon {
- padding: 6px 0 0 0;
+ padding: 2px 0 0 0;
}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/css/components/yui_node_add.css maas-0.1+bzr482+dfsg/src/maasserver/static/css/components/yui_node_add.css
--- maas-0.1+bzr462+dfsg/src/maasserver/static/css/components/yui_node_add.css 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/css/components/yui_node_add.css 2012-04-19 04:11:04.000000000 +0000
@@ -4,3 +4,6 @@
.yui3-node-add-widget .buttons {
margin-top: 30px;
}
+.yui3-node-add-widget .add-link img.icon {
+ margin-right: 6px;
+ }
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/css/layout.css maas-0.1+bzr482+dfsg/src/maasserver/static/css/layout.css
--- maas-0.1+bzr462+dfsg/src/maasserver/static/css/layout.css 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/css/layout.css 2012-04-19 04:11:04.000000000 +0000
@@ -189,6 +189,17 @@
#sidebar .button {
width: 100px;
}
+#sidebar h4 {
+ margin: 30px 0 0 0;
+ }
+#sidebar h4:first-child {
+ margin-top: 10px;
+ }
+#sidebar .button,
+#sidebar button,
+#sidebar input[type="submit"] {
+ margin-top: 10px;
+ }
/******************************************************************************
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/css/modifiers.css maas-0.1+bzr482+dfsg/src/maasserver/static/css/modifiers.css
--- maas-0.1+bzr462+dfsg/src/maasserver/static/css/modifiers.css 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/css/modifiers.css 2012-04-19 04:11:04.000000000 +0000
@@ -22,6 +22,9 @@
vertical-align: text-bottom;
margin-right: 3px;
}
+a.icon:hover {
+ text-decoration: none;
+ }
/* Spacing */
.space-top-small {
margin-top: 10px;
@@ -38,6 +41,9 @@
.space-right-small {
margin-right: 10px;
}
+.space-left-small {
+ margin-left: 10px;
+ }
.space-bottom-none {
margin-bottom: 0;
}
@@ -45,8 +51,8 @@
margin-bottom: 5px;
}
.pad-top {
- margin-top: 20px;
+ padding-top: 20px;
}
.pad-top-large {
- margin-top: 40px;
+ padding-top: 40px;
}
Binary files /tmp/L56NtgH4uS/maas-0.1+bzr462+dfsg/src/maasserver/static/img/inline_add.png and /tmp/ugMSGV0c6Q/maas-0.1+bzr482+dfsg/src/maasserver/static/img/inline_add.png differ
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/js/node_add.js maas-0.1+bzr482+dfsg/src/maasserver/static/js/node_add.js
--- maas-0.1+bzr462+dfsg/src/maasserver/static/js/node_add.js 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/js/node_add.js 2012-04-19 04:11:04.000000000 +0000
@@ -114,11 +114,16 @@
.set('href', '#')
.set('text', "Cancel")
.addClass('link-button');
+ var macaddress_add_icon = Y.Node.create('')
+ .set('src', MAAS_config.uris.statics + 'img/inline_add.png')
+ .set('alt', "+")
+ .addClass('icon');
var macaddress_add_link = Y.Node.create('')
.addClass('add-link')
.addClass('add-mac-form')
.set('href', '#')
- .set('text', "Add additional MAC address");
+ .set('text', "Add additional MAC address")
+ .prepend(macaddress_add_icon);
var operation = Y.Node.create('')
.set('type', 'hidden')
.set('name', 'op')
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/js/node_views.js maas-0.1+bzr482+dfsg/src/maasserver/static/js/node_views.js
--- maas-0.1+bzr462+dfsg/src/maasserver/static/js/node_views.js 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/js/node_views.js 2012-04-19 04:11:04.000000000 +0000
@@ -145,7 +145,13 @@
this.numberNode = Y.one(config.numberNode);
this.descriptionNode = Y.one(config.descriptionNode);
this.reservedNode = Y.one(config.reservedNode);
+ /* XXX: GavinPanella 2012-04-17 bug=984117:
+ * Hidden until we support reserved nodes. */
+ this.reservedNode.hide();
this.retiredNode = Y.one(config.retiredNode);
+ /* XXX: GavinPanella 2012-04-17 bug=984116:
+ * Hidden until we support retired nodes. */
+ this.retiredNode.hide();
this.deployed_nodes = 0;
this.commissioned_nodes = 0;
this.queued_nodes = 0;
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/js/prefs.js maas-0.1+bzr482+dfsg/src/maasserver/static/js/prefs.js
--- maas-0.1+bzr462+dfsg/src/maasserver/static/js/prefs.js 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/js/prefs.js 2012-04-19 04:11:04.000000000 +0000
@@ -42,7 +42,7 @@
.set('id','create_token')
.addClass('button')
.addClass('right')
- .set('text', "+ Add MAAS key");
+ .set('text', "+ Generate MAAS key");
this.status_node = Y.Node.create('')
.set('id','create_error');
this.spinnerNode = Y.Node.create('')
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/js/tests/test_node_views.js maas-0.1+bzr482+dfsg/src/maasserver/static/js/tests/test_node_views.js
--- maas-0.1+bzr462+dfsg/src/maasserver/static/js/tests/test_node_views.js 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/js/tests/test_node_views.js 2012-04-19 04:11:04.000000000 +0000
@@ -160,10 +160,16 @@
'3 nodes reserved for named deployment.',
Y.one('#reserved-nodes').get('text'),
'The reserved text should be set');
+ /* XXX: GavinPanella 2012-04-17 bug=984117:
+ * Hidden until we support reserved nodes. */
+ Y.Assert.areEqual("none", view.reservedNode.getStyle("display"));
Y.Assert.areEqual(
'1 retired node not represented.',
Y.one('#retired-nodes').get('text'),
'The retired text should be set');
+ /* XXX: GavinPanella 2012-04-17 bug=984116:
+ * Hidden until we support retired nodes. */
+ Y.Assert.areEqual("none", view.retiredNode.getStyle("display"));
},
testUpdateNodeCreation: function() {
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/static/js/tests/test_prefs.js maas-0.1+bzr482+dfsg/src/maasserver/static/js/tests/test_prefs.js
--- maas-0.1+bzr462+dfsg/src/maasserver/static/js/tests/test_prefs.js 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/static/js/tests/test_prefs.js 2012-04-19 04:11:04.000000000 +0000
@@ -40,7 +40,7 @@
var create_link = widget.get('srcNode').one('#create_token');
Y.Assert.isNotNull(create_link);
Y.Assert.areEqual(
- "+ Add MAAS key", create_link.get('text'));
+ "+ Generate MAAS key", create_link.get('text'));
// The placeholder node for errors has been created.
var status_node = widget.get('srcNode').one('#create_error');
Y.Assert.isNotNull(status_node);
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/base.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/base.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/base.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/base.html 2012-04-19 04:11:04.000000000 +0000
@@ -107,7 +107,8 @@
{% block footer-copyright %}
© 2012 Canonical Ltd. Ubuntu and Canonical are registered
- trademarks of Canonical Ltd.
+ trademarks of Canonical Ltd.
+ View Documentation
{% endblock %}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/index.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/index.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/index.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/index.html 2012-04-19 04:11:04.000000000 +0000
@@ -72,7 +72,7 @@
- Add node
+ + Add node
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/node_list.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/node_list.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/node_list.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/node_list.html 2012-04-19 04:11:04.000000000 +0000
@@ -18,6 +18,11 @@
e.preventDefault();
Y.maas.node_add.showAddNodeWidget({targetNode: '#nodes'});
});
+ // Reload the page when a new node gets added.
+ Y.maas.node_add.AddNodeDispatcher.on(
+ Y.maas.node_add.NODE_ADDED_EVENT, function(e) {
+ window.location.reload();
+ });
});
});
// -->
@@ -55,6 +60,6 @@
{% endfor %}
{% endif%}
- Add node
+ + Add node
{% endblock %}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/node_view.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/node_view.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/node_view.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/node_view.html 2012-04-19 04:11:04.000000000 +0000
@@ -7,41 +7,37 @@
{% block sidebar %}
{% if can_edit %}
-
+ Node details
+
+ Edit node
+
{% endif %}
{% if form.action_buttons or can_delete %}
-
Actions
- {% if can_delete %}
- {% if node.owner %}
-
- Delete node
-
- {% else %}
-
- Delete node
-
- {% endif %}
+ {% endif %}
+ {% if can_delete %}
+ {% if node.owner %}
+
+ Delete node
+
+ {% else %}
+
+ Delete node
+
{% endif %}
- {% for action in form.action_buttons %}
- {% if forloop.first %}
-
{% endif %}
- {% endfor %}
-
+ {% endif %}
+ {% if form.action_buttons %}
+
{% endif %}
{% endblock %}
@@ -65,12 +61,16 @@
{{ node.display_status }}
- {% if node.error %}
+ {% if error_text %}
Error output
-
- {{ node.error }}
-
+ {{ error_text }}
+
+ {% endif %}
+ {% if status_text %}
+
+ Console output
+ {{ status_text }}
{% endif %}
{% if node.owner %}
@@ -81,4 +81,3 @@
{% endif %}
{% endblock %}
-
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/prefs_add_sshkey.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/prefs_add_sshkey.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/prefs_add_sshkey.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/prefs_add_sshkey.html 2012-04-19 04:11:04.000000000 +0000
@@ -4,13 +4,13 @@
{% block page-title %}Add SSH key{% endblock %}
{% block content %}
-
{% endblock %}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/prefs.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/prefs.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/prefs.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/prefs.html 2012-04-19 04:11:04.000000000 +0000
@@ -56,7 +56,7 @@
+ Add SSH key
+ class="button right space-top">+ Add SSH key
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/settings_add_archive.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/settings_add_archive.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/settings_add_archive.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/settings_add_archive.html 2012-04-19 04:11:04.000000000 +0000
@@ -5,13 +5,13 @@
{% block page-title %}Add archive{% endblock %}
{% block content %}
-
{% endblock %}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/settings.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/settings.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/settings.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/settings.html 2012-04-19 04:11:04.000000000 +0000
@@ -40,14 +40,15 @@
+ title="Edit user {{ user_item.username }}"
+ class="icon">
{% if user != user_item %}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/user_view.html maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/user_view.html
--- maas-0.1+bzr462+dfsg/src/maasserver/templates/maasserver/user_view.html 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templates/maasserver/user_view.html 2012-04-19 04:11:04.000000000 +0000
@@ -6,18 +6,14 @@
{% block layout-modifiers %}sidebar{% endblock %}
{% block sidebar %}
-
-
+ User details
+
+ Edit user
+
+ Actions
+
+ Delete user
+
{% endblock %}
{% block content %}
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/templatetags/field_type.py maas-0.1+bzr482+dfsg/src/maasserver/templatetags/field_type.py
--- maas-0.1+bzr462+dfsg/src/maasserver/templatetags/field_type.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/templatetags/field_type.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Field type template tag."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/enum.py maas-0.1+bzr482+dfsg/src/maasserver/testing/enum.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/enum.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/enum.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Enumeration helpers."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/factory.py maas-0.1+bzr482+dfsg/src/maasserver/testing/factory.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/factory.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/factory.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test object factories."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/__init__.py maas-0.1+bzr482+dfsg/src/maasserver/testing/__init__.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/__init__.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/__init__.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/models.py maas-0.1+bzr482+dfsg/src/maasserver/testing/models.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/models.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/models.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test model for tests of testing module."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/oauthclient.py maas-0.1+bzr482+dfsg/src/maasserver/testing/oauthclient.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/oauthclient.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/oauthclient.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""OAuth client for API testing."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/testcase.py maas-0.1+bzr482+dfsg/src/maasserver/testing/testcase.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/testcase.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/testcase.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Custom test-case classes."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -16,6 +17,7 @@
'TestModelTestCase',
]
+from django.core.cache import cache
from maasserver.testing import reset_fake_provisioning_api_proxy
from maasserver.testing.factory import factory
import maastesting.testcase
@@ -25,6 +27,7 @@
def setUp(self):
super(TestCase, self).setUp()
+ self.addCleanup(cache.clear)
self.addCleanup(reset_fake_provisioning_api_proxy)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/tests/test_enum.py maas-0.1+bzr482+dfsg/src/maasserver/testing/tests/test_enum.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/tests/test_enum.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/tests/test_enum.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for enumeration helpers."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/tests/test_factory.py maas-0.1+bzr482+dfsg/src/maasserver/testing/tests/test_factory.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/tests/test_factory.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/tests/test_factory.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test the factory where appropriate. Don't overdo this."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/testing/tests/test_module.py maas-0.1+bzr482+dfsg/src/maasserver/testing/tests/test_module.py
--- maas-0.1+bzr462+dfsg/src/maasserver/testing/tests/test_module.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/testing/tests/test_module.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `maasserver.testing`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/models.py maas-0.1+bzr482+dfsg/src/maasserver/tests/models.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/models.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/models.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test related classes and functions for maas and its applications."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_api.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_api.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_api.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_api.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test maasserver API."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -1042,11 +1043,11 @@
desired_node = random.choice(available_nodes)
response = self.client.post(self.get_uri('nodes/'), {
'op': 'acquire',
- 'name': desired_node.system_id,
+ 'name': desired_node.hostname,
})
self.assertEqual(httplib.OK, response.status_code)
parsed_result = json.loads(response.content)
- self.assertEqual(desired_node.system_id, parsed_result['system_id'])
+ self.assertEqual(desired_node.hostname, parsed_result['hostname'])
def test_POST_acquire_would_rather_fail_than_disobey_constraint(self):
# If "acquire" is passed a constraint, it won't return a node
@@ -1076,13 +1077,13 @@
# If a name constraint is given, "acquire" attempts to allocate
# a node of that name.
node = factory.make_node(status=NODE_STATUS.READY, owner=None)
- system_id = node.system_id
response = self.client.post(self.get_uri('nodes/'), {
'op': 'acquire',
- 'name': system_id,
+ 'name': node.hostname,
})
self.assertEqual(httplib.OK, response.status_code)
- self.assertEqual(system_id, json.loads(response.content)['system_id'])
+ self.assertEqual(
+ node.hostname, json.loads(response.content)['hostname'])
def test_POST_acquire_constrains_by_name(self):
# Negative test for name constraint.
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_auth.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_auth.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_auth.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_auth.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_commands.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_commands.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_commands.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_commands.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test custom commands, as found in src/maasserver/management/commands."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -17,6 +18,7 @@
from django.conf import settings
from django.contrib.auth.models import User
+from django.core.cache import cache
from django.core.management import call_command
from maasserver.models import FileStorage
from maasserver.testing.factory import factory
@@ -101,3 +103,18 @@
self.assertTrue(users[0].check_password(password))
self.assertTrue(users[0].is_superuser)
self.assertEqual(email, users[0].email)
+
+ def test_clearcache_clears_entire_cache(self):
+ key = factory.getRandomString()
+ cache.set(key, factory.getRandomString())
+ call_command('clearcache')
+ self.assertIsNone(cache.get(key, None))
+
+ def test_clearcache_clears_specific_key(self):
+ key = factory.getRandomString()
+ cache.set(key, factory.getRandomString())
+ another_key = factory.getRandomString()
+ cache.set(another_key, factory.getRandomString())
+ call_command('clearcache', key=key)
+ self.assertIsNone(cache.get(key, None))
+ self.assertIsNotNone(cache.get(another_key, None))
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_components.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_components.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_components.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_components.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test maasserver components module."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_configuration.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_configuration.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_configuration.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_configuration.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests configuration."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_fields.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_fields.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_fields.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_fields.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test custom model fields."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_forms.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_forms.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_forms.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_forms.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test forms."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_js.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_js.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_js.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_js.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Run YUI3 unit tests with SST (http://testutils.org/sst/)."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_maasavahi.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_maasavahi.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_maasavahi.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_maasavahi.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for the Avahi export of MAAS."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_messages.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_messages.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_messages.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_messages.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test maasserver messages."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_middleware.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_middleware.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_middleware.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_middleware.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test maasserver middleware classes."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -16,31 +17,64 @@
import logging
from tempfile import NamedTemporaryFile
+from django.contrib.messages import constants
+from django.core.cache import cache
from django.core.exceptions import (
PermissionDenied,
ValidationError,
)
from django.test.client import RequestFactory
+from maasserver import (
+ components,
+ middleware as middleware_module,
+ provisioning,
+ )
from maasserver.exceptions import (
+ ExternalComponentException,
MAASAPIException,
MAASAPINotFound,
+ MAASException,
)
from maasserver.middleware import (
APIErrorsMiddleware,
+ check_profiles_cached,
+ clear_profiles_check_cache,
+ ErrorsMiddleware,
ExceptionLoggerMiddleware,
ExceptionMiddleware,
+ ExternalComponentsMiddleware,
+ PROFILES_CHECK_DONE_KEY,
)
from maasserver.testing.factory import factory
-from maasserver.testing.testcase import TestCase
+from maasserver.testing.testcase import (
+ LoggedInTestCase,
+ TestCase,
+ )
+
+
+class Messages:
+ """A class to record messages published by Django messaging
+ framework.
+ """
+
+ messages = []
+
+ def add(self, level, message, extras):
+ self.messages.append((level, message, extras))
-def fake_request(base_path):
+def fake_request(path, method='GET'):
"""Create a fake request.
- :param base_path: The base path to make the request to.
+ :param path: The path to make the request to.
+ :param method: The method to use for the reques
+ ('GET' or 'POST').
"""
rf = RequestFactory()
- return rf.get('%s/hello/' % base_path)
+ request = rf.get(path)
+ request.method = method
+ request._messages = Messages()
+ return request
class ExceptionMiddlewareTest(TestCase):
@@ -164,3 +198,109 @@
fake_request('/middleware/api/hello'),
ValueError(error_text))
self.assertIn(error_text, open(logfile.name).read())
+
+
+class ExternalComponentsMiddlewareTest(TestCase):
+
+ def patch_papi_get_profiles_by_name(self, method):
+ self.patch(components, '_PERSISTENT_ERRORS', {})
+ papi = provisioning.get_provisioning_api_proxy()
+ self.patch(papi.proxy, 'get_profiles_by_name', method)
+
+ def test_middleware_calls_check_profiles_cached(self):
+ calls = []
+ self.patch(
+ middleware_module, "check_profiles_cached",
+ lambda: calls.append(1))
+ middleware = ExternalComponentsMiddleware()
+ response = middleware.process_request(None)
+ self.assertIsNone(response)
+ self.assertEqual(1, len(calls))
+
+ def test_check_profiles_cached_sets_cache_key(self):
+ def return_all_profiles(profiles):
+ return profiles
+ self.patch_papi_get_profiles_by_name(return_all_profiles)
+
+ check_profiles_cached()
+ self.assertTrue(cache.get(PROFILES_CHECK_DONE_KEY, False))
+
+ def test_check_profiles_cached_sets_cache_key_if_exception_raised(self):
+ # The cache key PROFILES_CHECK_DONE_KEY is set to True even if
+ # the call to papi.get_profiles_by_name raises an exception.
+ def raise_exception(profiles):
+ raise Exception()
+ self.patch_papi_get_profiles_by_name(raise_exception)
+ try:
+ check_profiles_cached()
+ except Exception:
+ pass
+ self.assertTrue(cache.get(PROFILES_CHECK_DONE_KEY, False))
+
+ def test_check_profiles_cached_does_nothing_if_cache_key_set(self):
+ # If the cache key PROFILES_CHECK_DONE_KE is set to True
+ # the call to check_profiles_cached is silent.
+ def raise_exception(profiles):
+ raise Exception()
+ cache.set(PROFILES_CHECK_DONE_KEY, True)
+ self.patch_papi_get_profiles_by_name(raise_exception)
+ check_profiles_cached()
+ # No exception, get_profiles_by_name has not been called.
+
+ def test_clear_profiles_check_cache_deletes_PROFILES_CHECK_DONE_KEY(self):
+ cache.set(PROFILES_CHECK_DONE_KEY, factory.getRandomString())
+ self.assertTrue(cache.get(PROFILES_CHECK_DONE_KEY, False))
+ clear_profiles_check_cache()
+ self.assertFalse(cache.get(PROFILES_CHECK_DONE_KEY, False))
+
+ def test_middleware_returns_none_if_exception_raised(self):
+ def raise_exception(profiles):
+ raise Exception()
+
+ self.patch_papi_get_profiles_by_name(raise_exception)
+ middleware = ExternalComponentsMiddleware()
+ request = fake_request(factory.getRandomString())
+ response = middleware.process_request(request)
+ self.assertIsNone(response)
+
+ def test_middleware_does_not_catch_keyboardinterrupt_exception(self):
+ def raise_exception(profiles):
+ raise KeyboardInterrupt()
+
+ self.patch_papi_get_profiles_by_name(raise_exception)
+ middleware = ExternalComponentsMiddleware()
+ request = fake_request(factory.getRandomString())
+ self.assertRaises(
+ KeyboardInterrupt, middleware.process_request, request)
+
+
+class ErrorsMiddlewareTest(LoggedInTestCase):
+
+ def test_error_middleware_ignores_GET_requests(self):
+ request = fake_request(factory.getRandomString(), 'GET')
+ exception = MAASException()
+ middleware = ErrorsMiddleware()
+ response = middleware.process_exception(request, exception)
+ self.assertIsNone(response)
+
+ def test_error_middleware_ignores_non_ExternalComponentException(self):
+ request = fake_request(factory.getRandomString(), 'GET')
+ exception = ValueError()
+ middleware = ErrorsMiddleware()
+ response = middleware.process_exception(request, exception)
+ self.assertIsNone(response)
+
+ def test_error_middleware_handles_ExternalComponentException(self):
+ url = factory.getRandomString()
+ request = fake_request(url, 'POST')
+ error_message = factory.getRandomString()
+ exception = ExternalComponentException(error_message)
+ middleware = ErrorsMiddleware()
+ response = middleware.process_exception(request, exception)
+ # The response is a redirect.
+ self.assertEqual(
+ (httplib.FOUND, response['Location']),
+ (response.status_code, url))
+ # An error message has been published.
+ self.assertEqual(
+ [(constants.ERROR, error_message, '')], request._messages.messages)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_models.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_models.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_models.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_models.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test maasserver models."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -554,7 +555,7 @@
self.assertEqual(
nodes[1],
Node.objects.get_available_node_for_acquisition(
- user, {'name': nodes[1].system_id}))
+ user, {'name': nodes[1].hostname}))
def test_get_available_node_returns_None_if_name_is_unknown(self):
user = factory.make_user()
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_provisioning.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_provisioning.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_provisioning.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_provisioning.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `maasserver.provisioning`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -25,7 +26,10 @@
get_persistent_errors,
register_persistent_error,
)
-from maasserver.exceptions import MAASAPIException
+from maasserver.exceptions import (
+ ExternalComponentException,
+ MAASAPIException,
+ )
from maasserver.models import (
ARCHITECTURE,
Config,
@@ -35,11 +39,14 @@
NODE_STATUS_CHOICES,
)
from maasserver.provisioning import (
+ check_profiles,
compose_cloud_init_preseed,
compose_commissioning_preseed,
compose_preseed,
DETAILED_PRESENTATIONS,
+ get_all_profile_names,
get_metadata_server_url,
+ get_profile_name,
name_arch_in_cobbler_style,
present_detailed_user_friendly_fault,
present_user_friendly_fault,
@@ -272,6 +279,21 @@
def test_name_arch_in_cobbler_returns_unicode(self):
self.assertIsInstance(name_arch_in_cobbler_style(b'amd64'), unicode)
+ def test_get_profile_name_selects_Precise_and_right_arch(self):
+ architectures = map_enum(ARCHITECTURE).values()
+ self.assertItemsEqual(
+ [
+ 'maas-precise-%s' % name_arch_in_cobbler_style(arch)
+ for arch in architectures],
+ [
+ get_profile_name(arch)
+ for arch in architectures])
+
+ def test_get_profile_name_converts_architecture_name(self):
+ profile = get_profile_name(architecture='amd64')
+ self.assertNotIn('amd64', profile)
+ self.assertIn('x86_64', profile)
+
@inlineCallbacks
def test_select_profile_for_node_ignores_previously_chosen_profile(self):
node = factory.make_node(architecture='i386')
@@ -280,23 +302,6 @@
self.assertEqual(
'maas-precise-i386', select_profile_for_node(node))
- def test_select_profile_for_node_selects_Precise_and_right_arch(self):
- nodes = {
- arch: self.make_node_without_saving(arch=arch)
- for arch in map_enum(ARCHITECTURE).values()}
- self.assertItemsEqual([
- 'maas-precise-%s' % name_arch_in_cobbler_style(arch)
- for arch in nodes.keys()],
- [
- select_profile_for_node(node)
- for node in nodes.values()])
-
- def test_select_profile_for_node_converts_architecture_name(self):
- node = factory.make_node(architecture='amd64')
- profile = select_profile_for_node(node)
- self.assertNotIn('amd64', profile)
- self.assertIn('x86_64', profile)
-
def test_select_profile_for_node_works_for_commissioning(self):
# A special profile is chosen for nodes in the commissioning
# state.
@@ -316,6 +321,14 @@
pserv_node = self.papi.get_nodes_by_name([system_id])[system_id]
self.assertEqual("maas-precise-i386", pserv_node["profile"])
+ def test_get_all_profile_names(self):
+ expected_profiles = []
+ for arch in map_enum(ARCHITECTURE).values():
+ for commissioning in (False, True):
+ expected_profiles.append(
+ get_profile_name(arch, commissioning))
+ self.assertItemsEqual(expected_profiles, get_all_profile_names())
+
def test_provision_post_save_Node_checks_for_missing_profile(self):
# If the required profile for a node is missing, MAAS reports
# that the maas-import-isos script may need running.
@@ -324,7 +337,7 @@
raise Fault(PSERV_FAULT.NO_SUCH_PROFILE, "Unknown profile.")
self.patch(self.papi.proxy, 'add_node', raise_missing_profile)
- with ExpectedException(MAASAPIException):
+ with ExpectedException(ExternalComponentException):
node = factory.make_node(architecture='amd32k')
provisioning.provision_post_save_Node(
sender=Node, instance=node, created=True)
@@ -335,7 +348,7 @@
raise Fault(PSERV_FAULT.NO_COBBLER, factory.getRandomString())
self.patch(self.papi.proxy, 'add_node', raise_fault)
- with ExpectedException(MAASAPIException):
+ with ExpectedException(ExternalComponentException):
node = factory.make_node(architecture='amd32k')
provisioning.provision_post_save_Node(
sender=Node, instance=node, created=True)
@@ -416,7 +429,8 @@
self.patch(self.papi.proxy, 'add_node', raise_fault)
- with ExpectedException(MAASAPIException, ".*provisioning server.*"):
+ with ExpectedException(
+ ExternalComponentException, ".*provisioning server.*"):
self.papi.add_node('node', 'profile', 'power', '')
def test_provisioning_errors_are_reported_helpfully(self):
@@ -426,7 +440,7 @@
self.patch(self.papi.proxy, 'add_node', raise_provisioning_error)
- with ExpectedException(MAASAPIException, ".*Cobbler.*"):
+ with ExpectedException(ExternalComponentException, ".*Cobbler.*"):
self.papi.add_node('node', 'profile', 'power', '')
def patch_and_call_papi_method(self, fault_code, papi_method='add_node'):
@@ -459,6 +473,27 @@
errors = get_persistent_errors()
self.assertEqual(1, len(errors))
+ def patch_get_profiles_by_name(self, method):
+ self.patch(components, '_PERSISTENT_ERRORS', {})
+ self.patch(
+ self.papi.proxy, 'get_profiles_by_name', method)
+
+ def test_check_profiles_no_error_registered_if_all_profiles_found(self):
+ def return_all_profiles(profiles):
+ return profiles
+ self.patch_get_profiles_by_name(return_all_profiles)
+ check_profiles()
+ self.assertEqual([], get_persistent_errors())
+
+ def test_check_profiles_error_registered_if_not_all_profiles_found(self):
+ def return_some_profiles(profiles):
+ return profiles[1:]
+ self.patch_get_profiles_by_name(return_some_profiles)
+
+ check_profiles()
+ errors = get_persistent_errors()
+ self.assertIn("sudo maas-import-isos ", errors[0])
+
def test_failing_components_cleared_if_add_node_works(self):
self.patch(components, '_PERSISTENT_ERRORS', {})
register_persistent_error(COMPONENT.PSERV, factory.getRandomString())
@@ -478,22 +513,30 @@
self.papi.modify_nodes({})
self.assertEqual([other_error], get_persistent_errors())
- def test_failing_components_cleared_if_modify_nodes_works(self):
+ def register_random_errors(self, failed_components):
self.patch(components, '_PERSISTENT_ERRORS', {})
- register_persistent_error(COMPONENT.PSERV, factory.getRandomString())
- register_persistent_error(COMPONENT.COBBLER, factory.getRandomString())
+ for component in failed_components:
+ register_persistent_error(component, factory.getRandomString())
+
+ def test_failing_components_cleared_if_modify_nodes_works(self):
+ self.register_random_errors((COMPONENT.PSERV, COMPONENT.COBBLER))
self.papi.modify_nodes({})
self.assertEqual([], get_persistent_errors())
def test_failing_components_cleared_if_delete_nodes_by_name_works(self):
- self.patch(components, '_PERSISTENT_ERRORS', {})
- register_persistent_error(COMPONENT.PSERV, factory.getRandomString())
- register_persistent_error(COMPONENT.COBBLER, factory.getRandomString())
+ self.register_random_errors((COMPONENT.PSERV, COMPONENT.COBBLER))
other_error = factory.getRandomString()
register_persistent_error(factory.getRandomString(), other_error)
self.papi.delete_nodes_by_name([])
self.assertEqual([other_error], get_persistent_errors())
+ def test_failing_components_cleared_if_get_profiles_by_name_works(self):
+ self.register_random_errors((COMPONENT.PSERV, COMPONENT.COBBLER))
+ other_error = factory.getRandomString()
+ register_persistent_error(factory.getRandomString(), other_error)
+ self.papi.get_profiles_by_name([])
+ self.assertEqual([other_error], get_persistent_errors())
+
class TestProvisioningWithFake(ProvisioningTests, ProvisioningFakeFactory,
TestCase):
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_rabbit.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_rabbit.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_rabbit.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_rabbit.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Rabbit messaging tests."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_runserver.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_runserver.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_runserver.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_runserver.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for the "runserver" command module."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_views.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_views.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_views.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_views.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test maasserver API."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -15,6 +16,7 @@
import httplib
import os
import urllib2
+from urlparse import urlparse
from xmlrpclib import Fault
from django.conf import settings
@@ -29,7 +31,10 @@
views,
)
from maasserver.components import register_persistent_error
-from maasserver.exceptions import NoRabbit
+from maasserver.exceptions import (
+ ExternalComponentException,
+ NoRabbit,
+ )
from maasserver.forms import NodeActionForm
from maasserver.models import (
Config,
@@ -57,6 +62,7 @@
from maasserver.views import (
get_longpoll_context,
get_yui_location,
+ NodeEdit,
proxy_to_longpoll,
)
from maastesting.rabbit import uses_rabbit_fixture
@@ -685,15 +691,30 @@
self.assertEqual(0, len(doc.cssselect('form#node_actions input')))
- def test_view_node_shows_error_if_set(self):
+ def test_view_node_shows_console_output_if_error_set(self):
+ # When node.error is set but the node's status does not indicate an
+ # error condition, the contents of node.error are displayed as console
+ # output.
node = factory.make_node(
- owner=self.logged_in_user, error=factory.getRandomString())
+ owner=self.logged_in_user, error=factory.getRandomString(),
+ status=NODE_STATUS.READY)
node_link = reverse('node-view', args=[node.system_id])
response = self.client.get(node_link)
- doc = fromstring(response.content)
- content_text = doc.cssselect('#content')[0].text_content()
- self.assertIn("Error output", content_text)
- self.assertIn(node.error, content_text)
+ console_output = fromstring(response.content).xpath(
+ '//h4[text()="Console output"]/following-sibling::span/text()')
+ self.assertEqual([node.error], console_output)
+
+ def test_view_node_shows_error_output_if_error_set(self):
+ # When node.error is set and the node's status indicates an error
+ # condition, the contents of node.error are displayed as error output.
+ node = factory.make_node(
+ owner=self.logged_in_user, error=factory.getRandomString(),
+ status=NODE_STATUS.FAILED_TESTS)
+ node_link = reverse('node-view', args=[node.system_id])
+ response = self.client.get(node_link)
+ error_output = fromstring(response.content).xpath(
+ '//h4[text()="Error output"]/following-sibling::span/text()')
+ self.assertEqual([node.error], error_output)
def test_view_node_shows_no_error_if_no_error_set(self):
node = factory.make_node(owner=self.logged_in_user)
@@ -754,6 +775,43 @@
[message.message for message in response.context['messages']])
+class MAASExceptionHandledInView(LoggedInTestCase):
+
+ def test_raised_MAASException_redirects(self):
+ # When a ExternalComponentException is raised in a POST request, the
+ # response is a redirect to the same page.
+
+ # Patch NodeEdit to error on post.
+ def post(self, request, *args, **kwargs):
+ raise ExternalComponentException()
+ self.patch(NodeEdit, 'post', post)
+ node = factory.make_node(owner=self.logged_in_user)
+ node_edit_link = reverse('node-edit', args=[node.system_id])
+ response = self.client.post(node_edit_link, {})
+ redirect_url = urlparse(response['Location']).path
+ self.assertEqual(
+ (httplib.FOUND, redirect_url),
+ (response.status_code, node_edit_link))
+
+ def test_raised_ExternalComponentException_publishes_message(self):
+ # When a ExternalComponentException is raised in a POST request, a
+ # message is published with the error message.
+ error_message = factory.getRandomString()
+
+ # Patch NodeEdit to error on post.
+ def post(self, request, *args, **kwargs):
+ raise ExternalComponentException(error_message)
+ self.patch(NodeEdit, 'post', post)
+ node = factory.make_node(owner=self.logged_in_user)
+ node_edit_link = reverse('node-edit', args=[node.system_id])
+ self.client.post(node_edit_link, {})
+ # Manually perform the redirect: i.e. get the same page.
+ response = self.client.get(node_edit_link, {})
+ self.assertEqual(
+ [error_message],
+ [message.message for message in response.context['messages']])
+
+
class AdminNodeViewsTest(AdminLoggedInTestCase):
def test_admin_can_edit_nodes(self):
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/tests/test_zeroconfservice.py maas-0.1+bzr482+dfsg/src/maasserver/tests/test_zeroconfservice.py
--- maas-0.1+bzr462+dfsg/src/maasserver/tests/test_zeroconfservice.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/tests/test_zeroconfservice.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `zeroconfservice`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/urls_api.py maas-0.1+bzr482+dfsg/src/maasserver/urls_api.py
--- maas-0.1+bzr462+dfsg/src/maasserver/urls_api.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/urls_api.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""URL API routing configuration."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/urls.py maas-0.1+bzr482+dfsg/src/maasserver/urls.py
--- maas-0.1+bzr462+dfsg/src/maasserver/urls.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/urls.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""URL routing configuration."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/views.py maas-0.1+bzr482+dfsg/src/maasserver/views.py
--- maas-0.1+bzr462+dfsg/src/maasserver/views.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/views.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Views."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -31,7 +32,6 @@
import mimetypes
import os
import urllib2
-from django.utils.safestring import mark_safe
from convoy.combo import (
combine_files,
@@ -61,6 +61,7 @@
render_to_response,
)
from django.template import RequestContext
+from django.utils.safestring import mark_safe
from django.views.generic import (
CreateView,
DeleteView,
@@ -147,6 +148,10 @@
NODE_PERMISSION.ADMIN, node)
if node.status in (NODE_STATUS.COMMISSIONING, NODE_STATUS.READY):
messages.info(self.request, NODE_BOOT_INFO)
+ context['error_text'] = (
+ node.error if node.status == NODE_STATUS.FAILED_TESTS else None)
+ context['status_text'] = (
+ node.error if node.status != NODE_STATUS.FAILED_TESTS else None)
return context
def get_success_url(self):
diff -Nru maas-0.1+bzr462+dfsg/src/maasserver/zeroconfservice.py maas-0.1+bzr482+dfsg/src/maasserver/zeroconfservice.py
--- maas-0.1+bzr462+dfsg/src/maasserver/zeroconfservice.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maasserver/zeroconfservice.py 2012-04-19 04:11:04.000000000 +0000
@@ -7,6 +7,7 @@
"""Work with Zeroconf."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/factory.py maas-0.1+bzr482+dfsg/src/maastesting/factory.py
--- maas-0.1+bzr462+dfsg/src/maastesting/factory.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/factory.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test object factories."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/management/commands/reconcile.py maas-0.1+bzr482+dfsg/src/maastesting/management/commands/reconcile.py
--- maas-0.1+bzr462+dfsg/src/maastesting/management/commands/reconcile.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/management/commands/reconcile.py 2012-04-19 04:11:04.000000000 +0000
@@ -18,6 +18,7 @@
"""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/management/commands/tests/test_reconcile.py maas-0.1+bzr482+dfsg/src/maastesting/management/commands/tests/test_reconcile.py
--- maas-0.1+bzr462+dfsg/src/maastesting/management/commands/tests/test_reconcile.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/management/commands/tests/test_reconcile.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `maastesting.management.commands.reconcile`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -11,10 +12,10 @@
__metaclass__ = type
__all__ = []
-from maastesting.testcase import TestCase
from maastesting.management.commands.reconcile import (
guess_architecture_from_profile,
)
+from maastesting.testcase import TestCase
class TestFunctions(TestCase):
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/rabbit.py maas-0.1+bzr482+dfsg/src/maastesting/rabbit.py
--- maas-0.1+bzr462+dfsg/src/maastesting/rabbit.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/rabbit.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Helpers for testing with RabbitMQ."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/runner.py maas-0.1+bzr482+dfsg/src/maastesting/runner.py
--- maas-0.1+bzr462+dfsg/src/maastesting/runner.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/runner.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test runner for maas and its applications."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/testcase.py maas-0.1+bzr482+dfsg/src/maastesting/testcase.py
--- maas-0.1+bzr462+dfsg/src/maastesting/testcase.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/testcase.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test related classes and functions for maas and its applications."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/tests/__init__.py maas-0.1+bzr482+dfsg/src/maastesting/tests/__init__.py
--- maas-0.1+bzr462+dfsg/src/maastesting/tests/__init__.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/tests/__init__.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/tests/test_factory.py maas-0.1+bzr482+dfsg/src/maastesting/tests/test_factory.py
--- maas-0.1+bzr462+dfsg/src/maastesting/tests/test_factory.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/tests/test_factory.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test the factory where appropriate. Don't overdo this."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/maastesting/tests/test_rabbit.py maas-0.1+bzr482+dfsg/src/maastesting/tests/test_rabbit.py
--- maas-0.1+bzr462+dfsg/src/maastesting/tests/test_rabbit.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/maastesting/tests/test_rabbit.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `maastesting.rabbit`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/address.py maas-0.1+bzr482+dfsg/src/metadataserver/address.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/address.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/address.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Figure out server address for the maas_url setting."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/api.py maas-0.1+bzr482+dfsg/src/metadataserver/api.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/api.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/api.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Metadata API."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -176,6 +177,9 @@
return rc.ALL_OK
node.status = target_status
+ # When moving to a terminal state, remove the allocation.
+ if target_status is not None:
+ node.owner = None
node.error = request.POST.get('error', '')
node.save()
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/fields.py maas-0.1+bzr482+dfsg/src/metadataserver/fields.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/fields.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/fields.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Custom field types for the metadata server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/middleware.py maas-0.1+bzr482+dfsg/src/metadataserver/middleware.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/middleware.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/middleware.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Django "middlewares" for the metadata API."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/migrations/0001_initial.py maas-0.1+bzr482+dfsg/src/metadataserver/migrations/0001_initial.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/migrations/0001_initial.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/migrations/0001_initial.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Initial metadataserver migration."""
from __future__ import (
+ absolute_import,
print_function,
# This breaks South.
#unicode_literals,
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/models.py maas-0.1+bzr482+dfsg/src/metadataserver/models.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/models.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/models.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Model for the metadata server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/nodeinituser.py maas-0.1+bzr482+dfsg/src/metadataserver/nodeinituser.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/nodeinituser.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/nodeinituser.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""User management for nodes' access to the metadata service."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/tests/__init__.py maas-0.1+bzr482+dfsg/src/metadataserver/tests/__init__.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/tests/__init__.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/tests/__init__.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `metadataserver`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/tests/models.py maas-0.1+bzr482+dfsg/src/metadataserver/tests/models.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/tests/models.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/tests/models.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test model for testing BinaryField."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_address.py maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_address.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_address.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_address.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test server-address-guessing logic."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_api.py maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_api.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_api.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_api.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for the metadata API."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -313,6 +314,16 @@
self.assertEqual(
NODE_STATUS.COMMISSIONING, reload_object(node).status)
+ def test_signaling_WORKING_keeps_owner(self):
+ user = factory.make_user()
+ node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ node.owner = user
+ node.save()
+ client = self.make_node_client(node=node)
+ response = self.call_signal(client, status='WORKING')
+ self.assertEqual(httplib.OK, response.status_code)
+ self.assertEqual(user, reload_object(node).owner)
+
def test_signaling_commissioning_success_makes_node_Ready(self):
node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
client = self.make_node_client(node=node)
@@ -343,6 +354,15 @@
self.assertEqual(httplib.OK, response.status_code)
self.assertEqual(NODE_STATUS.READY, reload_object(node).status)
+ def test_signaling_commissioning_success_clears_owner(self):
+ node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ node.owner = factory.make_user()
+ node.save()
+ client = self.make_node_client(node=node)
+ response = self.call_signal(client, status='OK')
+ self.assertEqual(httplib.OK, response.status_code)
+ self.assertEqual(None, reload_object(node).owner)
+
def test_signaling_commissioning_failure_makes_node_Failed_Tests(self):
node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
client = self.make_node_client(node=node)
@@ -366,6 +386,15 @@
self.assertEqual(httplib.OK, response.status_code)
self.assertEqual(error_text, reload_object(node).error)
+ def test_signaling_commissioning_failure_clears_owner(self):
+ node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ node.owner = factory.make_user()
+ node.save()
+ client = self.make_node_client(node=node)
+ response = self.call_signal(client, status='FAILED')
+ self.assertEqual(httplib.OK, response.status_code)
+ self.assertEqual(None, reload_object(node).owner)
+
def test_signaling_no_error_clears_existing_error(self):
node = factory.make_node(
status=NODE_STATUS.COMMISSIONING, error=factory.getRandomString())
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_fields.py maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_fields.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_fields.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_fields.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Test custom field types."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_models.py maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_models.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_models.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_models.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Model tests for metadata server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_nodeinituser.py maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_nodeinituser.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/tests/test_nodeinituser.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/tests/test_nodeinituser.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Model tests for metadata server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/metadataserver/urls.py maas-0.1+bzr482+dfsg/src/metadataserver/urls.py
--- maas-0.1+bzr462+dfsg/src/metadataserver/urls.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/metadataserver/urls.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Metadata API URLs."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/amqpclient.py maas-0.1+bzr482+dfsg/src/provisioningserver/amqpclient.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/amqpclient.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/amqpclient.py 2012-04-19 04:11:04.000000000 +0000
@@ -8,6 +8,7 @@
"""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/api.py maas-0.1+bzr482+dfsg/src/provisioningserver/api.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/api.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/api.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Provisioning API for external use."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -170,6 +171,11 @@
super(ProvisioningAPI, self).__init__()
self.session = session
+ def sync(self):
+ """Request Cobbler to sync and return when it's finished."""
+ return self.session.call(
+ "sync", self.session.token_placeholder)
+
@inlineCallbacks
def add_distro(self, name, initrd, kernel):
assert isinstance(name, basestring)
@@ -180,6 +186,7 @@
"initrd": initrd,
"kernel": kernel,
})
+ yield self.sync()
returnValue(distro.name)
@inlineCallbacks
@@ -188,6 +195,7 @@
assert isinstance(distro, basestring)
profile = yield CobblerProfile.new(
self.session, name, {"distro": distro})
+ yield self.sync()
returnValue(profile.name)
@inlineCallbacks
@@ -204,17 +212,20 @@
"power_type": power_type,
}
system = yield CobblerSystem.new(self.session, name, attributes)
+ yield self.sync()
returnValue(system.name)
@inlineCallbacks
def modify_distros(self, deltas):
for name, delta in deltas.items():
yield CobblerDistro(self.session, name).modify(delta)
+ yield self.sync()
@inlineCallbacks
def modify_profiles(self, deltas):
for name, delta in deltas.items():
yield CobblerProfile(self.session, name).modify(delta)
+ yield self.sync()
@inlineCallbacks
def modify_nodes(self, deltas):
@@ -231,6 +242,7 @@
for interface_modification in interface_modifications:
yield system.modify(interface_modification)
yield system.modify(delta)
+ yield self.sync()
@inlineCallbacks
def get_objects_by_name(self, object_type, names):
@@ -278,6 +290,7 @@
assert all(isinstance(name, basestring) for name in names)
for name in names:
yield object_type(self.session, name).delete()
+ yield self.sync()
@deferred
def delete_distros_by_name(self, names):
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/cobblercatcher.py maas-0.1+bzr482+dfsg/src/provisioningserver/cobblercatcher.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/cobblercatcher.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/cobblercatcher.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Helping hands for dealing with Cobbler exceptions."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/cobblerclient.py maas-0.1+bzr482+dfsg/src/provisioningserver/cobblerclient.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/cobblerclient.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/cobblerclient.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/enum.py maas-0.1+bzr482+dfsg/src/provisioningserver/enum.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/enum.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/enum.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Enumerations meaningful to the provisioning server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/interfaces.py maas-0.1+bzr482+dfsg/src/provisioningserver/interfaces.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/interfaces.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/interfaces.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Provisioning API interfaces."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/plugin.py maas-0.1+bzr482+dfsg/src/provisioningserver/plugin.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/plugin.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/plugin.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Twisted Application Plugin code for the MAAS provisioning server"""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -13,7 +14,6 @@
from getpass import getuser
-from amqpclient import AMQFactory
from formencode import Schema
from formencode.validators import (
Int,
@@ -21,6 +21,7 @@
String,
URL,
)
+from provisioningserver.amqpclient import AMQFactory
from provisioningserver.cobblerclient import CobblerSession
from provisioningserver.remote import ProvisioningAPI_XMLRPC
from provisioningserver.services import (
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/remote.py maas-0.1+bzr482+dfsg/src/provisioningserver/remote.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/remote.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/remote.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Provisioning API over XML-RPC."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/services.py maas-0.1+bzr482+dfsg/src/provisioningserver/services.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/services.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/services.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Additional services that compose the MAAS Provisioning Server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/testing/amqpclient.py maas-0.1+bzr482+dfsg/src/provisioningserver/testing/amqpclient.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/testing/amqpclient.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/testing/amqpclient.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `provisioningserver.amqpclient`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/testing/factory.py maas-0.1+bzr482+dfsg/src/provisioningserver/testing/factory.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/testing/factory.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/testing/factory.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Provisioning test-objects factory."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/testing/fakeapi.py maas-0.1+bzr482+dfsg/src/provisioningserver/testing/fakeapi.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/testing/fakeapi.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/testing/fakeapi.py 2012-04-19 04:11:04.000000000 +0000
@@ -12,6 +12,7 @@
"""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/testing/fakecobbler.py maas-0.1+bzr482+dfsg/src/provisioningserver/testing/fakecobbler.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/testing/fakecobbler.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/testing/fakecobbler.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/testing/realcobbler.py maas-0.1+bzr482+dfsg/src/provisioningserver/testing/realcobbler.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/testing/realcobbler.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/testing/realcobbler.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Set up test sessions against real Cobblers."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -16,6 +17,7 @@
from os import environ
from textwrap import dedent
from urlparse import urlparse
+from unittest import skipIf
from provisioningserver.cobblerclient import CobblerSession
@@ -33,11 +35,17 @@
env_var = 'PSERV_TEST_COBBLER_URL'
- help_text = dedent("""
+ help_text_available = dedent("""\
Set %s to the URL for a Cobbler instance to test against,
+ e.g. http://username:password@example.com/cobbler_api.
+ WARNING: this will modify your Cobbler database.
+ """ % env_var)
+
+ help_text_local = dedent("""\
+ Set %s to the URL for a *local* Cobbler instance to test against,
e.g. http://username:password@localhost/cobbler_api.
WARNING: this will modify your Cobbler database.
- """.lstrip('\n') % env_var)
+ """ % env_var)
def __init__(self):
self.url = environ.get(self.env_var)
@@ -46,23 +54,52 @@
self.username = urlparts.username or 'cobbler'
self.password = urlparts.password or ''
+ @property
def is_available(self):
- """Is a real Cobbler available for tests?
+ """Is a real Cobbler available for tests?"""
+ return self.url is not None
- Use this to disable real-Cobbler tests if no real Cobbler is
- available: annotate them with
+ @property
+ def skip_unless_available(self):
+ """Decorator to disable tests if no real Cobbler is available.
+
+ Annotate tests like so::
+
+ @real_cobbler.skip_unless_available
+ def test_something_that_requires_a_real_cobbler(self):
+ ...
- @testtools.skipIf(
- not real_cobbler.is_available(), RealCobbler.help_text)
"""
- return self.url is not None
+ return skipIf(not self.is_available, self.help_text_available)
+
+ @property
+ def is_local(self):
+ """Is a real Cobbler installed locally available for tests?"""
+ if self.is_available:
+ hostname = urlparse(self.url).hostname
+ return hostname == "localhost" or hostname.startswith("127.")
+ else:
+ return False
+
+ @property
+ def skip_unless_local(self):
+ """Decorator to disable tests if no real *local* Cobbler is available.
+
+ Annotate tests like so::
+
+ @real_cobbler.skip_unless_local
+ def test_something_that_requires_a_real_local_cobbler(self):
+ ...
+
+ """
+ return skipIf(not self.is_local, self.help_text_local)
def get_session(self):
"""Obtain a session on the real Cobbler.
Returns None if no real Cobbler is available.
"""
- if self.is_available():
+ if self.is_available:
return CobblerSession(self.url, self.username, self.password)
else:
return None
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/__init__.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/__init__.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/__init__.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/__init__.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `provisioningserver`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_amqaclient.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_amqaclient.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_amqaclient.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_amqaclient.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for C{AMQFactory}."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_api.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_api.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_api.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_api.py 2012-04-19 04:11:04.000000000 +0000
@@ -7,6 +7,7 @@
"""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -19,7 +20,7 @@
abstractmethod,
)
from base64 import b64decode
-from unittest import skipIf
+from contextlib import contextmanager
from maasserver.testing.enum import map_enum
from maastesting.factory import factory
@@ -40,6 +41,11 @@
from provisioningserver.testing.realcobbler import RealCobbler
from testtools import TestCase
from testtools.deferredruntest import AsynchronousDeferredRunTest
+from testtools.matchers import (
+ FileExists,
+ Not,
+ )
+from testtools.monkey import patch
from twisted.internet.defer import inlineCallbacks
from zope.interface.verify import verifyObject
@@ -607,6 +613,84 @@
self.assertEqual(
preseed_data, b64decode(attrs['ks_meta']['MAAS_PRESEED']))
+ @contextmanager
+ def expected_sync(self, papi, times=1):
+ """Context where # calls to `papi.sync` must match `times`."""
+ sync_calls = []
+ orig_sync = papi.sync
+ fake_sync = lambda: orig_sync().addCallback(sync_calls.append)
+ unpatch = patch(papi, "sync", fake_sync)
+ try:
+ yield
+ finally:
+ unpatch()
+ self.assertEqual(times, len(sync_calls))
+
+ @inlineCallbacks
+ def test_add_distro_syncs(self):
+ # add_distro ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ with self.expected_sync(papi):
+ yield self.add_distro(papi)
+
+ @inlineCallbacks
+ def test_add_profile_syncs(self):
+ # add_profile ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ distro_name = yield self.add_distro(papi)
+ with self.expected_sync(papi):
+ yield self.add_profile(papi, distro_name=distro_name)
+
+ @inlineCallbacks
+ def test_add_node_syncs(self):
+ # add_node ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ profile_name = yield self.add_profile(papi)
+ with self.expected_sync(papi):
+ yield self.add_node(papi, profile_name=profile_name)
+
+ @inlineCallbacks
+ def test_modify_distros_syncs(self):
+ # modify_distros ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ with self.expected_sync(papi):
+ yield papi.modify_distros({})
+
+ @inlineCallbacks
+ def test_modify_profiles_syncs(self):
+ # modify_profiles ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ with self.expected_sync(papi):
+ yield papi.modify_profiles({})
+
+ @inlineCallbacks
+ def test_modify_nodes_syncs(self):
+ # modify_nodes ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ with self.expected_sync(papi):
+ yield papi.modify_nodes({})
+
+ @inlineCallbacks
+ def test_delete_distros_by_name_syncs(self):
+ # delete_distros_by_name ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ with self.expected_sync(papi):
+ yield papi.delete_distros_by_name([])
+
+ @inlineCallbacks
+ def test_delete_profiles_by_name_syncs(self):
+ # delete_profiles_by_name ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ with self.expected_sync(papi):
+ yield papi.delete_profiles_by_name([])
+
+ @inlineCallbacks
+ def test_delete_nodes_by_name_syncs(self):
+ # delete_nodes_by_name ensures that Cobbler syncs.
+ papi = self.get_provisioning_api()
+ with self.expected_sync(papi):
+ yield papi.delete_nodes_by_name([])
+
class TestFakeProvisioningAPI(ProvisioningAPITests, TestCase):
"""Test :class:`FakeAsynchronousProvisioningAPI`.
@@ -631,6 +715,22 @@
"""Return a real ProvisioningAPI, but using a fake Cobbler session."""
return ProvisioningAPI(make_fake_cobbler_session())
+ def test_sync(self):
+ """`ProvisioningAPI.sync` issues an authenticated ``sync`` call.
+
+ It is not exported - i.e. it is not part of :class:`IProvisioningAPI`
+ - but is used heavily by other methods in `IProvisioningAPI`.
+ """
+ papi = self.get_provisioning_api()
+ calls = []
+ self.patch(
+ papi.session, "call",
+ lambda *args: calls.append(args))
+ papi.sync()
+ self.assertEqual(
+ [("sync", papi.session.token_placeholder)],
+ calls)
+
class TestProvisioningAPIWithRealCobbler(ProvisioningAPITests,
ProvisioningAPITestsWithCobbler,
@@ -640,12 +740,34 @@
The URL for the Cobbler instance must be provided in the
`PSERV_TEST_COBBLER_URL` environment variable.
- Includes by inheritance all the tests in :class:`ProvisioningAPITests`.
+ Includes by inheritance all the tests in :class:`ProvisioningAPITests` and
+ :class:`ProvisioningAPITestsWithCobbler`.
"""
real_cobbler = RealCobbler()
- @skipIf(not real_cobbler.is_available(), RealCobbler.help_text)
+ @real_cobbler.skip_unless_available
def get_provisioning_api(self):
"""Return a connected :class:`ProvisioningAPI`."""
return ProvisioningAPI(self.real_cobbler.get_session())
+
+ @real_cobbler.skip_unless_local
+ @inlineCallbacks
+ def test_sync_after_modify(self):
+ # When MAAS modifies the MAC addresses of a node it triggers a sync of
+ # Cobbler. This is to ensure that netboot files are up-to-date, or
+ # removed as appropriate.
+ papi = self.get_provisioning_api()
+ node_name = yield self.add_node(papi)
+ mac_address = factory.getRandomMACAddress()
+ yield papi.modify_nodes(
+ {node_name: {"mac_addresses": [mac_address]}})
+ # The PXE file corresponding to the node's MAC address is present.
+ pxe_filename = "/var/lib/tftpboot/pxelinux.cfg/01-%s" % (
+ mac_address.replace(":", "-"),)
+ self.assertThat(pxe_filename, FileExists())
+ # Remove the MAC address again.
+ yield papi.modify_nodes(
+ {node_name: {"mac_addresses": []}})
+ # The PXE file has been removed too.
+ self.assertThat(pxe_filename, Not(FileExists()))
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_cobblercatcher.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_cobblercatcher.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_cobblercatcher.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_cobblercatcher.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for conversion of Cobbler exceptions to `ProvisioningError`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
@@ -15,7 +16,6 @@
ABCMeta,
abstractmethod,
)
-from unittest import skipIf
from xmlrpclib import Fault
from maastesting.factory import factory
@@ -180,7 +180,7 @@
real_cobbler = RealCobbler()
- @skipIf(not real_cobbler.is_available(), RealCobbler.help_text)
+ @real_cobbler.skip_unless_available
@deferred
def get_cobbler_session(self):
return self.real_cobbler.get_session()
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_cobblerclient.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_cobblerclient.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_cobblerclient.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_cobblerclient.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `provisioningserver.cobblerclient`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_cobblersession.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_cobblersession.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_cobblersession.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_cobblersession.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_fakecobbler.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_fakecobbler.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_fakecobbler.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_fakecobbler.py 2012-04-19 04:11:04.000000000 +0000
@@ -2,6 +2,7 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_plugin.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_plugin.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_plugin.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_plugin.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for the psmaas TAP."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_remote.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_remote.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_remote.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_remote.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `provisioningserver.remote`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_services.py maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_services.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/tests/test_services.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/tests/test_services.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Tests for `provisioningserver.services`."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/src/provisioningserver/utils.py maas-0.1+bzr482+dfsg/src/provisioningserver/utils.py
--- maas-0.1+bzr462+dfsg/src/provisioningserver/utils.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/src/provisioningserver/utils.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Utilities for the provisioning server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/templates/module.py maas-0.1+bzr482+dfsg/templates/module.py
--- maas-0.1+bzr462+dfsg/templates/module.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/templates/module.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""..."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/templates/script.py maas-0.1+bzr482+dfsg/templates/script.py
--- maas-0.1+bzr462+dfsg/templates/script.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/templates/script.py 2012-04-19 04:11:04.000000000 +0000
@@ -5,6 +5,7 @@
"""..."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/templates/test_module.py maas-0.1+bzr482+dfsg/templates/test_module.py
--- maas-0.1+bzr462+dfsg/templates/test_module.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/templates/test_module.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""..."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/twisted/plugins/maasps.py maas-0.1+bzr482+dfsg/twisted/plugins/maasps.py
--- maas-0.1+bzr462+dfsg/twisted/plugins/maasps.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/twisted/plugins/maasps.py 2012-04-19 04:11:04.000000000 +0000
@@ -4,6 +4,7 @@
"""Twisted Application Plugin for the MAAS provisioning server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/utilities/rewrite-future-imports maas-0.1+bzr482+dfsg/utilities/rewrite-future-imports
--- maas-0.1+bzr462+dfsg/utilities/rewrite-future-imports 1970-01-01 00:00:00.000000000 +0000
+++ maas-0.1+bzr482+dfsg/utilities/rewrite-future-imports 2012-04-19 04:11:04.000000000 +0000
@@ -0,0 +1,42 @@
+#!/usr/bin/env python2.7
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Ensure that __future__ import lines are populated correctly."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+
+import re
+import sys
+
+
+re_futures = re.compile(
+ r"^(from __future__ import) [(](.*?)[)]", re.DOTALL | re.MULTILINE)
+
+
+mandatory_future_imports = frozenset(
+ ("absolute_import", "print_function", "unicode_literals"))
+
+
+def replace(match):
+ imports = set().union(
+ (name.strip() for name in match.group(2).split(",")),
+ mandatory_future_imports)
+ imports.discard("")
+ imports = "".join(" %s,\n" % name for name in sorted(imports))
+ return "%s (\n%s )" % (match.group(1), imports)
+
+
+if __name__ == '__main__':
+ for filename in sys.argv[1:]:
+ with open(filename, "rb") as fd:
+ source = fd.read()
+ source = re_futures.sub(replace, source)
+ with open(filename, "wb") as fd:
+ fd.write(source)
diff -Nru maas-0.1+bzr462+dfsg/vdenv/api-list.py maas-0.1+bzr482+dfsg/vdenv/api-list.py
--- maas-0.1+bzr462+dfsg/vdenv/api-list.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/vdenv/api-list.py 2012-04-19 04:11:04.000000000 +0000
@@ -5,6 +5,7 @@
"""Print information from the Cobbler server."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
diff -Nru maas-0.1+bzr462+dfsg/vdenv/setup.py maas-0.1+bzr482+dfsg/vdenv/setup.py
--- maas-0.1+bzr462+dfsg/vdenv/setup.py 2012-04-12 20:11:10.000000000 +0000
+++ maas-0.1+bzr482+dfsg/vdenv/setup.py 2012-04-19 04:11:04.000000000 +0000
@@ -5,6 +5,7 @@
"""Setup a Virtual Data-center Environment."""
from __future__ import (
+ absolute_import,
print_function,
unicode_literals,
)
|