#!/bin/bash
#
#Copyright (c) 2009  Eucalyptus Systems, Inc.   
#
#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, only version 3 of the License.  
# 
#This file 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 <http://www.gnu.org/licenses/>.
# 
#Please contact Eucalyptus Systems, Inc., 130 Castilian
#Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/> 
#if you need additional information or have any questions.
#
#This file may incorporate work covered under the following copyright and
#permission notice:
#
#  Software License Agreement (BSD License)
#
#  Copyright (c) 2008, Regents of the University of California
#  
#
#  Redistribution and use of this software in source and binary forms, with
#  or without modification, are permitted provided that the following
#  conditions are met:
#
#    Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#
#    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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER
#  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. USERS OF
#  THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
#  LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
#  SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
#  IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
#  BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
#  THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
#  OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
#  WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
#  ANY SUCH LICENSES OR RIGHTS.
#

NEW_EUCA=""
OLD_EUCA=""
FORCE=""
OPENSSL="`which openssl 2> /dev/null`"
DB="N"
CONF="N"
KEYS="N"

usage() {
	echo "$0 [options]"
	echo
	echo "This script attempts to upgrade an old installation of eucalyptus"
	echo
	echo "   --help                   this page"
	echo "   --new <euca_dir>         where the new eucalyptus is installed"
	echo "   --old <euca_dir>         where the old eucalyptus is installed"
	echo "   --backup <euca_dir>      create a backup of an existing eucalyptus installation"
	echo "   --force                  attempt the upgrade even if a database is detected"
	echo "   --conf                   upgrade the conf file"
	echo "   --db                     upgrade the database"
	echo "   --keys                   upgrade the keys"
	echo
}

while [ $# -gt 0 ]; do
	if [ "$1" = "-h" -o "$1" = "-help" -o "$1" = "?" -o "$1" = "--help" ]; then
		usage
		exit 1
	fi
	if [ "$1" = "--db" ]; then
		DB="Y"
		shift
		continue
	fi
	if [ "$1" = "--conf" ]; then
		CONF="Y"
		shift
		continue
	fi
	if [ "$1" = "--keys" ]; then
		KEYS="Y"
		shift
		continue
	fi
	if [ "$1" = "--backup" ]; then
		if [ -z "$2" ]; then
			echo "--backup requires eucalyptus directory"
			exit
		fi
		BACKUP="$2"
		shift; shift;
		continue
	fi
	if [ "$1" = "--new" ]; then
		if [ -z "$2" ]; then
			echo "--new requires eucalyptus directory"
			exit
		fi
		NEW_EUCA="$2"
		shift; shift;
		continue
	fi
	if [ "$1" = "--old" ]; then
		if [ -z "$2" ]; then
			echo "--old requires eucalyptus directory"
			exit
		fi
		OLD_EUCA="$2"
		shift; shift;
		continue
	fi
	if [ "$1" == "--force" ]; then
		FORCE="Y"
		shift
		continue
	fi
	usage
	exit 1
done

if [ -d "$BACKUP" ]; then
    # backup useful bits of existing eucalyptus installation
    echo "Backing up $BACKUP..."
    CWD=`pwd` 
    DATESTR=`date +%s`
    mkdir -p /root/eucalyptus.backup.$DATESTR
    cd /root/eucalyptus.backup.$DATESTR
    echo "Backing up to /root/eucalyptus.backup.$DATESTR"
    EUCABACKUPS=""
    for i in $EUCADIR/var/lib/eucalyptus/keys/ $EUCADIR/var/lib/eucalyptus/db/ $EUCADIR/etc/eucalyptus/eucalyptus.conf $EUCADIR/etc/eucalyptus/eucalyptus-version
    do
	if [ -e $i ]; then
	    echo "Adding $i"
	    EUCABACKUPS="$EUCABACKUPS $i"
	fi
    done
    tar cf - $EUCABACKUPS 2>/dev/null | tar xf - 2>/dev/null
    cd $CWD
    echo "Done."
    exit 0
fi

# default NEW install and some checks
if [ -z "$NEW_EUCA" ]; then
	echo "Use --new to specify the location of the new installation"
	exit 1
fi 
if [ -z "$OLD_EUCA" ]; then
	echo "Use --old to specify the old installation from which we upgrade"
	exit 1
fi
if [ ! -e $NEW_EUCA/etc/eucalyptus/eucalyptus-version ]; then
	echo "cannot locate $NEW_EUCA/etc/eucalyptus/eucalyptus-version, --new must point to a valid eucalyptus installation"
	exit 1;
else
    new_euca_version="`cat $NEW_EUCA/etc/eucalyptus/eucalyptus-version 2> /dev/null`"
fi
if [ ! -e $OLD_EUCA/etc/eucalyptus/eucalyptus-version ]; then
	echo "Cannot locate $OLD_EUCA/etc/eucalyptus/eucalyptus-version, --old must point to a valid eucalyptus installation/backup.  If this is a valid installation and the version file is missing, please create one with the version of eucalyptus you are upgrading from (i.e. '1.6.1')"
	exit 1;
else
    old_euca_version="`cat $OLD_EUCA/etc/eucalyptus/eucalyptus-version 2> /dev/null`"
fi

NEW_EUCA_VER_NUM=`echo $new_euca_version | sed "s/\.//g"`
OLD_EUCA_VER_NUM=`echo $old_euca_version | sed "s/\.//g"`

# valid version upgrade check
if [ $new_euca_version = $old_euca_version ]; then
    SAMEVER="Y"
else 
    SAMEVER="N"
fi

if [ $new_euca_version = "1.6.2" ]; then
    if [ $old_euca_version != "1.6.1" ]; then
	echo "Cannot upgrade from $old_euca_version to $new_euca_version, skipping automatic upgrade"
	exit 0
    fi
fi

echo "Upgrading from version $old_euca_version ($OLD_EUCA_VER_NUM) to $new_euca_version ($NEW_EUCA_VER_NUM)"
#if [ `expr $euca_version "<" 1.6` -eq 1 ]; then
#	echo "$NEW_EUCA is not a 1.6.x Eucalyptus installation"
#	exit 2
#fi

# upgrade conf file
if [ "$CONF" = "Y" ]; then
    echo "Upgrading configuration file..."
    if [ "$SAMEVER" = "Y" ]; then
	cp --preserve -a $OLD_EUCA/etc/eucalyptus/eucalyptus.conf $NEW_EUCA/etc/eucalyptus/eucalyptus.conf
	echo "Done."
    else
	
    # we need euca_conf later (the new version)
	if [ ! -x $NEW_EUCA/usr/sbin/euca_conf ];
	then
	    echo "Cannot find new $NEW_EUCA/usr/sbin/euca_conf! Skipping automatic upgrade."
	    exit 1
	fi
	
    # upgrade the conf file
	export EUCALYPTUS="$NEW_EUCA"
	$EUCALYPTUS/usr/sbin/euca_conf -d $EUCALYPTUS --upgrade-conf $OLD_EUCA/etc/eucalyptus/eucalyptus.conf $EUCALYPTUS/etc/eucalyptus/eucalyptus.conf 
    fi
    echo "Done."
fi

if [ "$KEYS" = "Y" ]; then
    echo "Upgrading keys..."
    for x in `\ls -ad $OLD_EUCA/var/lib/eucalyptus/keys/* 2>&1 | grep -v policy\.xml` ; do
	cp -a --preserve $x $NEW_EUCA/var/lib/eucalyptus/keys/
    done
    echo "Done."
fi

# upgrade the database (and backup)
if [ "$DB" = "Y" ]; then
    echo "Upgrading database..."
    if [ $SAMEVER = "Y" ]; then
    	cp --preserve -a $OLD_EUCA/var/lib/eucalyptus/db/* $NEW_EUCA/var/lib/eucalyptus/db/
    else
        # check we have the right binaries
  	if [ -z "$OPENSSL" ]; then
	    echo "Missing openssl, skipping automatic upgrade."
	    exit 1
  	fi
	# make sure there are files to upgrade from
	if ( ls $OLD_EUCA/var/lib/eucalyptus/db/* >/dev/null 2>&1 ); then
	
        # upgrade the database
	    export EUCALYPTUS="$NEW_EUCA"
	    FILES=`\ls -1 ${EUCALYPTUS}/usr/share/eucalyptus/*.jar | egrep -v ".*eucalyptus-.*1\.6\.1.*"`
	    for FILE in $FILES; do
		  export CLASSPATH=${FILE}:${CLASSPATH}
	    done
	    $OPENSSL pkcs12 -in ${EUCALYPTUS}/var/lib/eucalyptus/keys/euca.p12 -name eucalyptus -name "eucalyptus" -password pass:eucalyptus  -passin pass:eucalyptus -passout pass:eucalyptus -nodes 2>/dev/null | grep -A30 "friendlyName: eucalyptus" | grep -A26 "BEGIN RSA" > ${EUCALYPTUS}/var/lib/eucalyptus/keys/cloud-pk.pem
	    
	    export EUCALYPTUS_DB="`echo -n eucalyptus | $OPENSSL dgst -sha256 -sign ${EUCALYPTUS}/var/lib/eucalyptus/keys/cloud-pk.pem -hex 2>/dev/null`"
	    export EUCALYPTUS_CLUSTER_CERT="`openssl pkcs12 -in ${EUCALYPTUS}/var/lib/eucalyptus/keys/clusters.p12 -name eucalyptus -name "eucalyptus" -password pass:eucalyptus  -passin pass:eucalyptus -passout pass:eucalyptus -nodes -nokeys 2>/dev/null | sed '1,/friendlyName: cc-/ d ; /friendlyName/,$ d ; /-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' -n | openssl base64 | sed '{:q;N;s/\n//g;s/=/./g;s/+/-/g;s/\//_/g;t q}'`"
	    export EUCALYPTUS_NODE_CERT="`openssl pkcs12 -in ${EUCALYPTUS}/var/lib/eucalyptus/keys/clusters.p12 -name eucalyptus -name "eucalyptus" -password pass:eucalyptus  -passin pass:eucalyptus -passout pass:eucalyptus -nodes -nokeys 2>/dev/null | sed '1,/friendlyName: nc-/ d ; /friendlyName/,$ d ; /-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' -n | openssl base64 | sed '{:q;N;s/\n//g;s/=/./g;s/+/-/g;s/\//_/g;t q}'`"
	    
	    echo "Upgrading your database..."
	    if [ -f ${EUCALYPTUS}/etc/eucalyptus/cloud.d/scripts/Import_${OLD_EUCA_VER_NUM}.groovy ]; then
                rm -f $NEW_EUCA/var/lib/eucalyptus/db/*.log $NEW_EUCA/var/lib/eucalyptus/db/*.script $NEW_EUCA/var/lib/eucalyptus/db/*.properties
		if ( ! java org.codehaus.groovy.tools.GroovyStarter --main groovy.ui.GroovyMain --classpath ${EUCALYPTUS}/etc/eucalyptus/cloud.d/scripts:$CLASSPATH \
                    -Deuca.old=${OLD_EUCA} -Deuca.new=${NEW_EUCA} -Deuca.home=${NEW_EUCA} \
                    -Deuca.var.dir=${NEW_EUCA}/var/lib/eucalyptus -Deuca.conf.dir=${NEW_EUCA} -Deuca.log.dir=${NEW_EUCA}/var/log/eucalyptus \
                    ${EUCALYPTUS}/etc/eucalyptus/cloud.d/scripts/Import_${OLD_EUCA_VER_NUM}.groovy 2>&1 | egrep -v "(log4j)|(ehcache)" > $EUCALYPTUS/var/log/eucalyptus/upgrade.log ); then
    		    echo "Database upgrade failed! Please check your database before using Eucalyptus."
    		    exit 1
		fi
	    else
		echo "Cannot find DB upgrade script from $old_euca_version to $new_euca_version! Skipping DB upgrade."
	    fi
	fi
    fi
    echo "Done."
fi
