Add initial helper tasks
authorNikos Skalkotos <skalkoto@grnet.gr>
Wed, 28 Sep 2011 07:58:42 +0000 (10:58 +0300)
committerNikos Skalkotos <skalkoto@grnet.gr>
Wed, 28 Sep 2011 07:58:42 +0000 (10:58 +0300)
Use SNF_IMAGE_ as prefix for the floppy exporting variables

helper/common.sh [new file with mode: 0644]
helper/rc.local [new file with mode: 0644]
helper/snf-passtohash.py [new file with mode: 0755]
helper/tasks/00InstallUnattend.sh [new file with mode: 0644]
helper/tasks/40AssignHostname.sh [new file with mode: 0644]
helper/tasks/40ChangePassword.sh [new file with mode: 0644]
helper/tasks/40DeleteSSHKeys.sh [new file with mode: 0644]
helper/tasks/90SELinuxAutorelabel.sh [new file with mode: 0644]
host/common.sh.in
host/create

diff --git a/helper/common.sh b/helper/common.sh
new file mode 100644 (file)
index 0000000..cca0630
--- /dev/null
@@ -0,0 +1,122 @@
+# Copyright 2011 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#   1. Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are
+# those of the authors and should not be interpreted as representing official
+# policies, either expressed or implied, of GRNET S.A.
+
+OUTPUT=/dev/ttyS0
+RESULT=/dev/ttyS2
+
+# Programs
+XMLSTARLET=xmlstarlet
+INSTALL_MBR=install-mbr
+
+CLEANUP=( )
+
+log_error() {
+    echo "$@" >&2
+    exit 1
+}
+
+get_base_distro() {
+    local root_dir=$1
+
+    if [ -e "$root_dir/etc/debian_version" ]; then
+        echo "debian"
+    elif [ -e "$root_dir/etc/redhat-release" ]; then
+        echo "redhat"
+    elif [ -e "$root_dir/etc/slackware-version" ]; then
+        echo "slackware"
+    elif [ -e "$root_dir/SuSE-release" ]; then
+        echo "suse"
+    elif [ -e "$root_dir/gentoo-release" ]; then
+        echo "gentoo"
+    fi
+}
+
+get_distro() {
+    local root_dir=$1
+
+    if [ -e "$root_dir/etc/debian_version" ]; then
+        distro="debian"
+        if [ -e ${root_dir}/etc/lsb-release ]; then
+            ID=$(grep $DISTRIB_ID=${root_dir}/etc/lsb-release | cut -d= -f2)
+            if [ "x$ID" = "xUbuntu" ]; then
+                distro="ubuntu"
+            fi
+        fi
+        echo "$distro"
+    elif [ -e "$root_dir/etc/fedora-release" ]; then
+        echo "fedora"
+    elif [ -e "$root_dir/etc/centos-release" ]; thne
+        echo "centos"
+    elif [ -e "$root_dir/etc/redhat-release" ]; then
+        echo "redhat"
+    elif [ -e "$root_dir/etc/slackware-version" ]; then
+        echo "slackware"
+    elif [ -e "$root_dir/SuSE-release" ]; then
+        echo "suse"
+    elif [ -e "$root_dir/gentoo-release" ]; then
+        echo "gentoo"
+    fi
+}
+
+cleanup() {
+# if something fails here, it souldn't call cleanup again...
+    trap - EXIT
+
+    if [ ${#CLEANUP[*]} -gt 0 ]; then
+        LAST_ELEMENT=$((${#CLEANUP[*]}-1))
+        REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
+        for i in $REVERSE_INDEXES; do
+            # If something fails here, it's better to retry it for a few times
+            # before we give up with an error. This is needed for kpartx when
+            # dealing with ntfs partitions mounted through fuse. umount is not
+            # synchronous and may return while the partition is still busy. A
+            # premature attempt to delete partition mappings through kpartx on a
+            # device that hosts previously mounted ntfs partition may fail with
+            # an  `device-mapper: remove ioctl failed: Device or resource busy'
+            # error. A sensible workaround for this is to wait for a while and
+            # then try again.
+            local cmd=${CLEANUP[$i]}
+            $cmd || for interval in 0.25 0.5 1 2 4; do
+            echo "Command $cmd failed!"
+            echo "I'll wait for $interval secs and will retry..."
+            sleep $interval
+            $cmd && break
+        done
+        test $? -eq 1 && { echo "Giving Up..."; exit 1; }
+    done
+  fi
+  echo "Clean UP executed"
+}
+
+trap cleanup EXIT
+
+# Redirect stdout and stderr
+exec &> $OUTPUT
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
diff --git a/helper/rc.local b/helper/rc.local
new file mode 100644 (file)
index 0000000..f0e760d
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+# Copyright 2011 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#   1. Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are
+# those of the authors and should not be interpreted as representing official
+# policies, either expressed or implied, of GRNET S.A.
+
+. /usr/share/snf-image/common.sh
+
+# terminate helper vm when the script exits
+CLEANUP+=("telinit 0")
+
+floppy=$(mktemp -d floppy.XXXXXXXX) || exit 1
+CLEANUP+=("rm -f $floppy")
+
+if [ -e /dev/fd0 ]; then
+    mount /dev/fd0 $floppy
+    source $floppy
+    umount $floppy
+else
+    log_error "Floppy Device is not present!"
+fi
+
+# Mount the instance's partition
+target=$(mktemp -d target.XXXXXXXX) || exit 1
+CLEANUP+=("rm -f $target")
+mount /dev/sdb $target
+CLEANUP+=("umount $target")
+
+SNF_IMAGE_TARGET=$target
+
+RUN_PARTS=`which run-parts`
+if [ -n "$RUN_PARTS" -a -n "/usr/lib/snf-image/tasks" ]; then
+    set | egrep ^SNF_IMAGE_\\w+= | cut -d= -f1 | while read line; do
+        export $line
+    done
+    $RUN_PARTS "/usr/lib/snf-image/tasks"
+fi
+
+cleanup
+trap - EXIT
+
+# never called...
+exit 0
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
diff --git a/helper/snf-passtohash.py b/helper/snf-passtohash.py
new file mode 100755 (executable)
index 0000000..a4c6f0f
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2011 Greek Research and Technology Network
+#
+"""Generate a Hash from a given Password 
+
+This program takes a Password as an argument and
+returns to standard output a hash followed by a newline.
+To do this, it generates internally a random salt.
+
+"""
+
+import sys
+import crypt 
+
+from string import ascii_letters, digits
+from random import choice
+from os.path import basename
+from optparse import OptionParser
+
+
+# This dictionary maps the hashing algorithm method
+# with its <ID> as documented in:
+# http://www.akkadia.org/drepper/SHA-crypt.txt
+HASH_ID_FROM_METHOD = {
+    'md5': '1',
+    'blowfish': '2a',
+    'sun-md5': 'md5',
+    'sha256': '5',
+    'sha512': '6'
+}
+
+def random_salt(length=8):
+    pool = ascii_letters + digits + "/" + "."
+    return ''.join(choice(pool) for i in range(length))
+
+
+def parse_arguments(input_args):
+    usage = "usage: %prog [-h] [-m encrypt-method] <password>"
+    parser = OptionParser(usage=usage)
+    parser.add_option("-m", "--encrypt-method",
+                       dest="encrypt_method",
+                       type='choice',
+                       default="sha512",
+                       choices=HASH_ID_FROM_METHOD.keys(),
+                       help="encrypt password with ENCRYPT_METHOD [%default] \
+                       (supported: " + ", ".join(HASH_ID_FROM_METHOD.keys()) +")",
+    )
+
+    (opts, args) = parser.parse_args(input_args)
+
+    if len(args) != 1:
+       parser.error('password is missing')
+
+    return (args[0], opts.encrypt_method)
+
+
+def main():
+    (password, method) = parse_arguments(sys.argv[1:])
+    salt = random_salt()
+    hash = crypt.crypt(password, "$"+HASH_ID_FROM_METHOD[method]+"$"+salt)
+    sys.stdout.write("%s\n" % (hash))
+    return 0
+
+if __name__ == "__main__":
+        sys.exit(main())
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
diff --git a/helper/tasks/00InstallUnattend.sh b/helper/tasks/00InstallUnattend.sh
new file mode 100644 (file)
index 0000000..a1d712e
--- /dev/null
@@ -0,0 +1,24 @@
+#! /bin/sh
+
+### BEGIN TASK INFO
+# Provides:            InstallUnattend
+# Requires:
+# Short-Description:   Installs Unattend.xml for unattended windows setup
+### END TAST INFO
+
+set -e
+. /usr/share/snf-image/common.sh
+
+if [ -z "$SNF_IMAGE_TARGET" ]; then
+       log_error "Target dir is missing"       
+fi
+
+if [ "$SNF_IMAGE_TYPE" != "ntfsdump" ]; then
+       exit 0
+fi
+
+if [ -e /usr/share/snf-image/unattend.xml ]; then
+       cat /usr/share/snf-image/unattend.xml > $SNF_IMAGE_TARGET/Unattend.xml
+fi
+
+exit 0
diff --git a/helper/tasks/40AssignHostname.sh b/helper/tasks/40AssignHostname.sh
new file mode 100644 (file)
index 0000000..a66ed42
--- /dev/null
@@ -0,0 +1,78 @@
+#! /bin/sh
+
+### BEGIN TASK INFO
+# Provides:            AssignHostname
+# Requires:            InstallUnattend
+# Short-Description:   Assign the Hostname of Computer Name in the instance
+### END TAST INFO
+
+set -e
+. /usr/share/snf-image/common.sh
+
+windows_hostname() {
+    local target=$1
+    local password=$2
+
+    local tmp_unattend=`mktemp` || exit 1
+    CLEANUP+=("rm $tmp_unattend")
+
+    echo -n "Assigning new computer name..."
+
+    local namespace="urn:schemas-microsoft-com:unattend"
+    
+    $XMLSTARLET ed -N x=$namespace -u "/x:unattend/x:settings/x:component/x:ComputerName" -v $password "$target/Unattend.xml" > $tmp_unattend
+
+    cat $tmp_unattend > "$target/Unattend.xml"
+    echo done
+}
+
+linux_hostname() {
+    local target=$1
+    local hostname=$2
+
+    local distro=$(get_base_distro $target)
+
+    case "$distro" in
+        debian)
+            echo "$hostname" > $target/etc/hostname;;
+        redhat)
+            sed -ie "s/HOSTNAME=.*$/HOSTNAME=$hostname/g" $target/etc/sysconfig/network;;
+        slackware|suse)
+        #local domain=$(sed -e 's/^[^\.]*//g' < /etc/HOSTNAME)
+        
+        # In slackware hostname and domain name are joined together. For now I
+        # will not retain the domain name.
+        
+        echo $hostname > ${target}/etc/HOSTNAME;;
+    gentoo)
+        sed -ie "s/\(\(HOSTNAME\)\|\(hostname\)\)=.*$/\1=\"$hostname\"/" $target/etc/conf.d/hostname;;
+    esac
+
+    # Some Linux distributions assign the hostname to 127.0.1.1 in order to be
+    # resolvable to an IP address. Lets replace this if found in /etc/hosts
+    sed -ie "s/^[[:blank:]]*127\.0\.1\.1[[:blank:]].\+$/127.0.1.1\t$hostname/" $target/etc/hosts
+}
+
+if [ -z "$SNF_IMAGE_TARGET" -o ! -d "$SNF_IMAGE_TARGET" ]; then
+    log_error "Missing target directory"       
+fi
+
+if [ -z "$SNF_IMAGE_HOSTNAME" ]; then
+    log_error "Hostname is missing"
+fi
+
+if [ "$SNF_IMAGE_TYPE" = "ntfsdump" ]; then
+    windows_hostname $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD
+elif [ "$SNF_IMAGE_TYPE" = "extdump" ]; then
+    linux_hostname $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD
+fi
+
+echo "done"
+
+cleanup
+trap - EXIT
+
+exit 0
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
+
diff --git a/helper/tasks/40ChangePassword.sh b/helper/tasks/40ChangePassword.sh
new file mode 100644 (file)
index 0000000..f71c35b
--- /dev/null
@@ -0,0 +1,82 @@
+#! /bin/sh
+
+### BEGIN TASK INFO
+# Provides:            ChangePassword
+# Requires:            InstallUnattend
+# Short-Description:   Changes Password for specified users
+### END TAST INFO
+
+set -e
+. /usr/share/snf-image/common.sh
+
+windows_password() {
+    local target=$1
+    local password=$2
+
+    local tmp_unattend=`mktemp` || exit 1
+    CLEANUP+=("rm $tmp_unattend")
+
+    echo -n "Installing new admin password..."
+
+    local namespace="urn:schemas-microsoft-com:unattend"
+    
+    $XMLSTARLET ed -N x=$namespace -u "/x:unattend/x:settings/x:component/x:UserAccounts/x:AdministratorPassword/x:Value" -v $password "$target/Unattend.xml" > $tmp_unattend
+
+    cat $tmp_unattend > "$target/Unattend.xml"
+    echo done
+}
+
+linux_password() {
+    local target=$1
+    local password=$2
+
+    local hash=$(/usr/share/snf-image/snf-passtohash.py $password)
+    if [ ! -e ${target}/etc/shadow ]; then
+       log_error "No /etc/shadow found!" 
+    fi
+    
+    declare -a users=("root")
+
+    local distro=$(get_distro $target)
+
+    if [ "x$disto" = "xubuntu" -o \
+         "x$disto" + "xfedora" ] ; then
+        users+=("user")
+    fi
+
+    for i in $(seq 0 1 $((${#users[@]}-1))); do
+        local tmp_shadow=$(mktemp)
+        CLEANUP+=("rm $tmp_shadow")
+
+        echo -n "Setting ${users[$i]} password..."
+    
+        echo "${users[$i]}:$hash:15103:0:99999:7:::" > $tmp_shadow
+        grep -v "${users[$i]}" ${TARGET}/etc/shadow >> $tmp_shadow
+        cat $tmp_shadow > ${target}/etc/shadow
+        echo "done"
+    done
+}
+
+if [ -z "$SNF_IMAGE_TARGET" ]; then
+    log_error "Target dir is missing"  
+fi
+
+if [ -z "$SNF_IMAGE_PASSWORD" ]; then
+    log_error "Password is missing"
+fi
+
+if [ "$SNF_IMAGE_TYPE" = "ntfsdump" ]; then
+    windows_password $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD
+elif [ "$SNF_IMAGE_TYPE" = "extdump" ]; then
+    linux_password $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD
+fi
+
+echo "done"
+
+cleanup
+trap - EXIT
+
+exit 0
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
+
diff --git a/helper/tasks/40DeleteSSHKeys.sh b/helper/tasks/40DeleteSSHKeys.sh
new file mode 100644 (file)
index 0000000..d8abbbd
--- /dev/null
@@ -0,0 +1,33 @@
+#! /bin/sh
+
+### BEGIN TASK INFO
+# Provides:            DeleteSSHKeys
+# Requires:
+# Short-Description:   Remove ssh keys if present.
+### END TAST INFO
+
+set -e
+. /usr/share/snf-image/common.sh
+
+
+if [ "$SNF_IMAGE_TYPE" != "extdump" ]; then
+    exit 0
+fi
+
+HOST_KEY="/etc/ssh/ssh_host_key"
+RSA_KEY="/etc/ssh/ssh_host_rsa_key"
+DSA_KEY="/etc/ssh/ssh_host_dsa_key"
+
+for key in $HOST_KEY $RSA_KEY $DSA_KEY ; do
+    if [ -f "${SNF_IMAGE_TARGET}/${key}" ] ; then
+        rm -f ${SNF_IMAGE_TARGET}/${key}*
+    fi
+done
+
+cleanup
+trap - EXIT
+
+exit 0
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
+
diff --git a/helper/tasks/90SELinuxAutorelabel.sh b/helper/tasks/90SELinuxAutorelabel.sh
new file mode 100644 (file)
index 0000000..c0d7bd6
--- /dev/null
@@ -0,0 +1,31 @@
+#! /bin/sh
+
+### BEGIN TASK INFO
+# Provides:            SELinuxAutorelabel
+# Requires:
+# Short-Description:   Force the system to relabel at next boot
+### END TAST INFO
+
+set -e
+. /usr/share/snf-image/common.sh
+
+if [ -z "$SNF_IMAGE_TARGET" ]; then
+       log_error "Target dir is missing"       
+fi
+
+if [ "$SNF_IMAGE_TYPE" != "extdump" ]; then
+    exit 0
+fi
+
+distro=$(get_base_distro $SNF_IMAGE_TARGET)
+
+if [ "$distro" = "redhat" ]; then
+    # we have to force a filesystem relabeling for SELinux after messing
+    # around with the filesystem in redhat derived OSs
+    echo "Enforce an automatic relabeling in the initial boot process..."
+    touch $SNF_IMAGE_TARGET/.autorelabel
+fi
+
+exit 0
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index 06a56df..0542f7e 100644 (file)
@@ -171,7 +171,7 @@ create_floppy() {
     dd bs=512 count=2880 if=/dev/zero of=$img
     mkfs.ext2 -F $img > /dev/null
     mount $img $target -o loop
-    set|egrep ^snf_export_\\w+|sed -e 's/^snf_export_/GANETI_/' > $target/rules
+    set|egrep ^snf_export_\\w+=|sed -e 's/^snf_export_/SNF_IMAGE_/' > $target/rules
     umount $target
 }
 
index 5bcbeaf..ec6a114 100755 (executable)
@@ -79,7 +79,7 @@ $MONITOR dd bs=4M if=$IMAGE_FILE of=$root_dev oflag=direct
 floppy=$(mktemp --tmpdir floppy.XXXXXXXX) || exit 1
 CLEANUP+=("rm -f $floppy")
 
-snf_export_FORMAT=${IMG_FORMAT}
+snf_export_TYPE=${IMG_FORMAT}
 snf_export_PASSWORD=${IMG_PASSWD}
 snf_export_HOSTNAME=${instance}