Commit 09ac08db authored by Nikos Skalkotos's avatar Nikos Skalkotos
Browse files

Merge branch 'v0.2-dev'

parents d0362bb3 de7269cd
......@@ -7,7 +7,7 @@ SUBDIRS = tasks
dist_doc_DATA = COPYING AUTHORS ChangeLog
dist_bin_SCRIPTS = snf-image-helper
dist_scripts_SCRIPTS= snf-passtohash.py
dist_scripts_SCRIPTS= snf-passtohash.py inject-files.py decode-properties.py
dist_common_DATA = common.sh unattend.xml
edit = sed \
......
......@@ -29,12 +29,14 @@
RESULT=/dev/ttyS1
FLOPPY_DEV=/dev/fd0
PROGNAME=$(basename $0)
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
# Programs
XMLSTARLET=xmlstarlet
RESIZE2FS=resize2fs
PARTED=parted
CLEANUP=( )
......@@ -49,6 +51,10 @@ log_error() {
exit 1
}
warn() {
echo "Warning: $@" >&2
}
get_base_distro() {
local root_dir=$1
......@@ -92,6 +98,35 @@ get_distro() {
fi
}
get_last_partition() {
local dev="$1"
"$PARTED" -s -m "$dev" unit s print | tail -1
}
get_partition() {
local dev="$1"
local id="$2"
"$PARTED" -s -m "$dev" unit s print | grep "^$id"
}
get_partition_count() {
local dev="$1"
expr $("$PARTED" -s -m "$dev" unit s print | wc -l) - 2
}
get_last_free_sector() {
local dev="$1"
local last_line=$("$PARTED" -s -m "$dev" unit s print free | tail -1)
local type=$(echo "$last_line" | cut -d: -f 5)
if [ "$type" = "free;" ]; then
echo "$last_line" | cut -d: -f 3
fi
}
cleanup() {
# if something fails here, it shouldn't call cleanup again...
trap - EXIT
......@@ -124,6 +159,23 @@ cleanup() {
fi
}
check_if_excluded() {
test "$PROGNAME" = "snf-image-helper" && return 0
eval local do_exclude=\$SNF_IMAGE_EXCLUDE_${PROGNAME:2}_TASK
if [ -n "$do_exclude" ]; then
warn "Task $PROGNAME was excluded and will not run."
exit 0
fi
return 0
}
trap cleanup EXIT
# Check if the execution of a task should be ommited
check_if_excluded
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
......@@ -23,6 +23,11 @@ if test -z "$RESIZE2FS" ; then
AC_MSG_ERROR([resize2fs not found in $PATH])
fi
AC_PATH_PROG(PARTED, [parted], [], [$PATH:/usr/sbin:/sbin])
if test -z "$PARTED" ; then
AC_MSG_ERROR([parted not found in $PATH])
fi
AC_CONFIG_FILES([
Makefile
tasks/Makefile
......
#!/usr/bin/env python
"""Decode a json encoded string with properties
This program decodes a json encoded properties string and outputs it in a
bash sourcable way. The properties are passed to the program through a JSON
string either read from a file or from standard input and are outputed to a
target file.
"""
import sys
import os
import subprocess
import json
from StringIO import StringIO
from optparse import OptionParser
def parse_arguments(input_args):
usage = "Usage: %prog [options] <output_file>"
parser = OptionParser(usage=usage)
parser.add_option("-i", "--input",
action="store",type='string', dest="input_file",
help="get input from FILE instead of stdin",
metavar="FILE")
opts, args = parser.parse_args(input_args)
if len(args) != 1:
parser.error('output file is missing')
output_file = args[0]
if opts.input_file is not None:
if not os.path.isfile(opts.input_file):
parser.error('input file does not exist')
return (opts.input_file, output_file)
def main():
(input_file, output_file) = parse_arguments(sys.argv[1:])
infh = sys.stdin if input_file is None else open(input_file, 'r')
outfh = open(output_file, 'w')
properties = json.load(infh)
for key, value in properties.items():
os.environ['SNF_IMAGE_PROPERTY_' + key] = value
p = subprocess.Popen(['bash', '-c', 'set'], stdout=subprocess.PIPE)
output = StringIO(p.communicate()[0]);
for line in iter(output):
if line.startswith('SNF_IMAGE_PROPERTY_'):
outfh.write('export ' + line)
infh.close()
outfh.close()
return 0
if __name__ == "__main__":
sys.exit(main())
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
#!/usr/bin/env python
"""Inject files into a directory
This program injects files into a target directory.
The files are passed to the program through a JSON string either read from a
file or from standard input.
"""
import sys
import os
import json
import datetime
import base64
from optparse import OptionParser
def timestamp():
now = datetime.datetime.now()
current_time = now.strftime("%Y%m%d.%H%M%S")
return current_time
def parse_arguments(input_args):
usage = "Usage: %prog [options] <target>"
parser = OptionParser(usage=usage)
parser.add_option("-i", "--input",
action="store",type='string', dest="input_file",
help="get input from FILE instead of stdin",
metavar="FILE")
opts, args = parser.parse_args(input_args)
if len(args) != 1:
parser.error('target is missing')
target = args[0]
if not os.path.isdir(target):
parser.error('target is not a directory')
input_file = opts.input_file
if input_file is None:
input_file = sys.stdin
else:
if not os.path.isfile(input_file):
parser.error('input file does not exist')
input_file = open(input_file,'r')
return (input_file, target)
def main():
(input_file, target) = parse_arguments(sys.argv[1:])
files = json.load(input_file)
for f in files:
real_path = target + '/' + f['path']
if os.path.lexists(real_path):
backup_file = real_path + '.bak.' + timestamp()
os.rename(real_path, backup_file)
parentdir = os.path.dirname(real_path)
if not os.path.exists(parentdir):
os.makedirs(parentdir)
newfile = open(real_path, 'w')
newfile.write(base64.b64decode(f['contents']))
newfile.close()
os.chmod(real_path, 0440)
sys.stderr.write('Successful personalization of Image\n')
input_file.close()
return 0
if __name__ == "__main__":
sys.exit(main())
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
......@@ -59,11 +59,22 @@ else
log_error "Floppy does not contain \`rules\' file"
fi
if [ -n "$SNF_IMAGE_PROPERTIES" ]; then
properties=$(mktemp --tmpdir properties.XXXXXX)
add_cleanup rm "$properties"
echo "$SNF_IMAGE_PROPERTIES" |
"@scriptsdir@/decode-properties.py" "$properties"
source "$properties"
else
log_error "SNF_IMAGE_PROPERTIES variable is missing"
fi
# Image mount point...
target=$(mktemp -d --tmpdir target.XXXXXX)
add_cleanup rmdir "$target"
export SNF_IMAGE_TARGET="$target"
export SNF_IMAGE_ROOTDEV="${SNF_IMAGE_DEV}${SNF_IMAGE_ROOT}"
if [ ! -d "@tasksdir@" ]; then
log_error "snf-image/tasks directory is missing"
......@@ -78,13 +89,15 @@ fi
# in case it is left mounted...
trap '{ umount "$target"; }' ERR
# Redirect standard error to standard output,
# prepend a timestamp before each line of output.
echo "Execute all snf-image tasks...."
$RUN_PARTS -v --exit-on-error "@tasksdir@" 2>&1|
while IFS= read -r line; do
echo $(date +%Y:%m:%d-%H:%M:%S.%N) "$line"
done
if [ -z "$SNF_IMAGE_EXCLUDE_ALL_TASKS" ]; then
# Redirect standard error to standard output,
# prepend a timestamp before each line of output.
echo "Execute all snf-image tasks...."
$RUN_PARTS -v --exit-on-error "@tasksdir@" 2>&1|
while IFS= read -r line; do
echo $(date +%Y:%m:%d-%H:%M:%S.%N) "$line"
done
fi
# Disable the trap. If code reaches here, the filesystem is unmounted.
trap - ERR
......
#! /bin/bash
### BEGIN TASK INFO
# Provides: FixPartitionTable
# RunBefore: FilesystemResizeUnmounted
# Short-Description: Resize filesystem to use all the available space
### END TASK INFO
set -e
. "@commondir@/common.sh"
if [ ! -b "$SNF_IMAGE_DEV" ]; then
log_error "Device file:\`${SNF_IMAGE_DEV}' is not a block device"
fi
if [ $(get_partition_count "$SNF_IMAGE_DEV") -eq 0 ]; then
log_error "Device: \`${SNF_IMAGE_DEV}' does not contain any partition"
fi
retval=$(get_last_partition "$SNF_IMAGE_DEV")
id=$(echo $retval | cut -d: -f1)
pstart=$(echo $retval | cut -d: -f2)
pend=$(echo $retval | cut -d: -f3)
ptype=$(echo $retval | cut -d: -f5)
if [ $id -gt 4 ]; then
log_error "We don't support logical volumes"
fi
if [ x"$ptype" = "x" ]; then
# Don't know how to handle this
warn "Last partition with id: \`$id' is empty or has unknown filesystem"
warn "I won't resize the partition"
exit 0
fi
new_pend=$(get_last_free_sector "$SNF_IMAGE_DEV")
if [ -z "$new_pend" ] ; then
# Nothing to do
exit 0
fi
# Extend the partition
$PARTED -s -m "$SNF_IMAGE_DEV" rm "$id"
$PARTED -s -m "$SNF_IMAGE_DEV" mkpart primary "$ptype" "$pstart" "$new_pend"
# Inform the kernel about the changes
partprobe "$SNF_IMAGE_DEV"
exit 0
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
#! /bin/bash
### BEGIN TASK INFO
# Provides: ResizeUnmounted
# Provides: FilesystemResizeUnmounted
# RunBefore: MountImage
# RunAfter: FixPartitionTable
# Short-Description: Resize filesystem to use all the available space
### END TASK INFO
......@@ -11,15 +12,16 @@ set -e
if [ ! -b "$SNF_IMAGE_DEV" ]; then
log_error "Device file:\`${SNF_IMAGE_DEV}' is not a block device"
fi
if [ -z "$SNF_IMAGE_TYPE" ]; then
log_error "Image type does not exist"
fi
if [ "$SNF_IMAGE_TYPE" = "extdump" ]; then
"$RESIZE2FS" "$SNF_IMAGE_DEV"
fi
last_partition=$(get_last_partition "$SNF_IMAGE_DEV")
id=$(echo "$last_partition" | cut -d: -f1)
ptype=$(echo "$last_partition" | cut -d: -f5)
if [[ "$ptype" == ext[234] ]]; then
device="${SNF_IMAGE_DEV}${id}"
"$RESIZE2FS" "$device"
fi
exit 0
......
......@@ -13,11 +13,11 @@ if [ ! -d "$SNF_IMAGE_TARGET" ]; then
log_error "Target dir:\`$SNF_IMAGE_TARGET' is missing"
fi
if [ ! -b "$SNF_IMAGE_DEV" ]; then
log_error "Device file:\`$SNF_IMAGE_DEV' is not a block device"
if [ ! -b "$SNF_IMAGE_ROOTDEV" ]; then
log_error "Device file:\`$SNF_IMAGE_ROOTDEV' is not a block device"
fi
mount "$SNF_IMAGE_DEV" "$SNF_IMAGE_TARGET"
mount "$SNF_IMAGE_ROOTDEV" "$SNF_IMAGE_TARGET" -o rw
exit 0
......
......@@ -14,7 +14,7 @@ if [ ! -d "$SNF_IMAGE_TARGET" ]; then
log_error "Target dir: \`$SNF_IMAGE_TARGET' is missing"
fi
if [ "$SNF_IMAGE_TYPE" = "ntfsdump" ]; then
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" = "windows" ]; then
# Make sure Unattend.xml is removed after setup has finished
mkdir -p "$SNF_IMAGE_TARGET/Windows/Setup/Scripts"
echo "del /Q /F C:\Unattend.xml" > "$SNF_IMAGE_TARGET/Windows/Setup/Scripts/SetupComplete.cmd"
......
......@@ -14,11 +14,7 @@ if [ ! -d "$SNF_IMAGE_TARGET" ]; then
log_error "Target dir: \`$SNF_IMAGE_TARGET' is missing."
fi
target="$SNF_IMAGE_TARGET"
if [ "$SNF_IMAGE_TYPE" != "extdump" ]; then
cleanup
trap - EXIT
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" != "linux" ]; then
exit 0
fi
......@@ -29,6 +25,7 @@ RSA_KEY="/etc/ssh/ssh_host_rsa_key"
DSA_KEY="/etc/ssh/ssh_host_dsa_key"
ECDSA_KEY="/etc/ssh/ssh_host_ecdsa_key"
target="$SNF_IMAGE_TARGET"
#Remove the default keys
for pair in "$HOST_KEY@rsa1" "$RSA_KEY@rsa" "$DSA_KEY@dsa" "$ECDSA_KEY@ecdsa"; do
......@@ -48,8 +45,6 @@ config="$target/etc/ssh/sshd_config"
if [ ! -e "$config" ]; then
echo "Warning: Config file: \`$config' is missing."
echo "Warning: Can't check for non-default keys."
cleanup
trap - EXIT
exit 0
fi
......@@ -93,9 +88,6 @@ grep ^HostKey "$config" | while read key_line; do
fi
done
cleanup
trap - EXIT
exit 0
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
#!/usr/bin/env python
### BEGIN TASK INFO
# Provides: EnforcePersonality
# RunBefore: UmountImage
# RunAfter: MountImage
# Short-Description: Inject files to the instance
### END TASK INFO
"""Personalize an Image by injecting files
This hook injects files into the filesystem of an Image.
The files are passed to the hook through the Ganeti
OS interface and found in the variable OSP_IMG_PERSONALITY.
"""
import sys
import os
import json
import datetime
import base64
def timestamp():
now = datetime.datetime.now()
current_time = now.strftime("%Y%m%d.%H%M%S")
return current_time
def main():
if not os.environ.has_key('SNF_IMAGE_TARGET'):
sys.stderr.write('Error: SNF_IMAGE_TARGET variable is missing\n')
return 1
target = os.environ['SNF_IMAGE_TARGET']
if not os.path.isdir(target):
sys.stderr.write('Error: Target: `'+target+'\' is not a directory.\n')
return 2
if os.environ.has_key('SNF_IMAGE_PERSONALITY'):
osp_img_personality = os.environ['SNF_IMAGE_PERSONALITY']
files = json.loads(osp_img_personality)
for f in files:
real_path = target + '/' + f['path']
if os.path.lexists(real_path):
backup_file = real_path + '.bak.' + timestamp()
os.rename(real_path, backup_file)
newfile = open(real_path, 'w')
newfile.write(base64.b64decode(f['contents']))
newfile.close()
os.chmod(real_path, 0440)
sys.stderr.write('Successful personalization of Image\n')
else:
sys.stderr.write('This Image has no personality (0 files to inject)\n')
return 0
if __name__ == "__main__":
sys.exit(main())
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
#! /bin/bash
### BEGIN TASK INFO
# Provides: ResizeMounted
# Provides: FilesystemResizeMounted
# RunBefore: UmountImage
# RunAfter: MountImage
# Short-Description: Resize filesystem to use all the available space
......@@ -14,21 +14,26 @@ if [ ! -d "$SNF_IMAGE_TARGET" ]; then
log_error "Target directory \`$SNF_IMAGE_TARGET' is missing"
fi
if [ "$SNF_IMAGE_TYPE" = "ntfsdump" ]; then
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" != "windows" ]; then
exit 0
fi
last_partition=$(get_last_partition "$SNF_IMAGE_DEV")
id=$(echo "$last_partition" | cut -d: -f1)
ptype=$(echo "$last_partition" | cut -d: -f5)
if [ "$ptype" = "ntfs" ]; then
# Write a diskpart script to %SystemDrive%\Windows\SnfScripts. Sysprep will
# try to execute this script during the specialize pass.
mkdir -p "$SNF_IMAGE_TARGET/Windows/SnfScripts"
cat > "$SNF_IMAGE_TARGET/Windows/SnfScripts/ExtendFilesystem" <<EOF
select disk 0
select volume 1
select volume $id
extend filesystem
exit
EOF
fi
cleanup
trap - EXIT
exit 0
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
......@@ -11,10 +11,10 @@ set -e
. "@commondir@/common.sh"
if [ -z "$SNF_IMAGE_TARGET" ]; then
log_error "Target dir: \`$SNF_IMAGE_TARGET' is missing"
log_error "Target dir: \`$SNF_IMAGE_TARGET' is missing"
fi
if [ "$SNF_IMAGE_TYPE" != "ntfsdump" ]; then
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" != "windows" ]; then
exit 0
fi
......
......@@ -14,7 +14,7 @@ if [ ! -d "$SNF_IMAGE_TARGET" ]; then
log_error "Target dir: \`$SNF_IMAGE_TARGET' is missing"
fi
if [ "$SNF_IMAGE_TYPE" = "extdump" ]; then
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" = "linux" ]; then
distro=$(get_base_distro $SNF_IMAGE_TARGET)
if [ "$distro" = "redhat" ]; then
......
......@@ -62,15 +62,12 @@ if [ -z "$SNF_IMAGE_HOSTNAME" ]; then
log_error "Hostname is missing"
fi
if [ "$SNF_IMAGE_TYPE" = "ntfsdump" ]; then
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" = "windows" ]; then
windows_hostname "$SNF_IMAGE_TARGET" "$SNF_IMAGE_HOSTNAME"
elif [ "$SNF_IMAGE_TYPE" = "extdump" ]; then
elif [ "$SNF_IMAGE_PROPERTY_OSFAMILY" = "linux" ]; then
linux_hostname "$SNF_IMAGE_TARGET" "$SNF_IMAGE_HOSTNAME"
fi
cleanup
trap - EXIT
exit 0
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
......
......@@ -36,13 +36,21 @@ linux_password() {
log_error "No /etc/shadow found!"
fi
declare -a users=("root")
local distro=$(get_distro $target)
if [ "x$distro" = "xubuntu" -o \
"x$distro" = "xfedora" ] ; then
users+=("user")
declare -a users