#!/bin/bash

# Copyright 2009-2012 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; 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 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., 6755 Hollister Ave., Goleta
# CA 93117, 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
#   All rights reserved.
#
#   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.

#set -o nounset
set -e
unset UP_DB UP_KEYS UP_CONF UP_FORCE UP_LOG UP_SAMEVERSION
unset NEW_EUCA OLD_EUCA NEW_EUCA_VERSION OLD_EUCA_VERSION
LEGAL_LONG_OPTS="help old new conf keys db force copyonly sameversion user"
KEYDIR="/var/lib/eucalyptus/keys"
DBDIR="/var/lib/eucalyptus/db"
NEW_EUCA="/"

usage() {
	echo "$0 [options]"
	echo
	echo "This script attempts to upgrade an old installation of eucalyptus"
	echo
	echo "   -h|--help                   Ibid."
	echo "   -n|--new <euca_dir>         The value of \${EUCALYPTUS} for the NEW installation."
	echo "   -o|--old <euca_dir>         The value of \${EUCALYPTUS} for the OLD installation."
#	echo "   -b|--backup <euca_dir>      create a backup of an existing eucalyptus installation"
	echo "   -f|--force                  Attempt the upgrade even if a database is detected"
	echo "   -c|--conf                   Upgrade the conf file"
	echo "   -d|--db                     Upgrade the database"
	echo "   -k|--keys                   Upgrade the keys"
	echo "   -s|--sameversion            Versions unchanged or no db changes."
	echo "   -u|--user                   The user who owns this eucalyptus installation."
	echo
}

log() {
  FSTR="${1}\n"
  shift
  printf "${FSTR}" ${@} | tee -a ${UP_LOG}
}

boole() {
  if [ -z "$1" ]; then echo "false"; else echo "true"; fi
}

get_euca_version() {
  NAME=$1
  FLAG=$(echo ${NAME//_EUCA/} | tr A-Z a-z)
  DIR=$(eval echo \$$NAME)
  if [ -z "${DIR}" ]; then 
    log "Use --${FLAG} to specify the location of the new installation" 1>&2; exit 1
  elif [ ! -e ${DIR}/etc/eucalyptus/eucalyptus-version ]; then 
    log "cannot locate ${DIR}/etc/eucalyptus/eucalyptus-version, --${FLAG} must point to a valid eucalyptus installation" 1>&2; exit 1
  else
    head -n1 ${DIR}/etc/eucalyptus/eucalyptus-version
  fi  
}

sameversion() {
  SAME_VERSION=1
}

upgrade_conf() {
  if [ -n "${SAME_VERSION}" ]; then 
    cp --preserve -a ${OLD_EUCA}/etc/eucalyptus/eucalyptus.conf ${NEW_EUCA}/etc/eucalyptus/eucalyptus.conf
    return
  fi
  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
  ${EUCALYPTUS}/usr/sbin/euca_conf -d ${EUCALYPTUS} --upgrade-conf ${OLD_EUCA}/etc/eucalyptus/eucalyptus.conf 
}

upgrade_keys() {
  for x in $(\ls -ad ${OLD_EUCA}/${KEYDIR}/* 2>&1 | grep -v policy\.xml) ; do
    cp -a --preserve $x ${NEW_EUCA}/${KEYDIR}/
  done
}

upgrade_db() {
  # Ensure that this system had CLC enabled in the previous version
  if [ -f "${OLD_EUCA}/var/lib/eucalyptus/services" ]; then
      if ! grep -q cloud ${OLD_EUCA}/var/lib/eucalyptus/services; then
          log "CLC is disabled.  Skipping DB upgrade."
          return
      fi
  elif ! (ls ${OLD_EUCA}/${DBDIR}/* >/dev/null 2>&1); then 
      if [ "${OLD_EUCA_VERSION:0:3}" == "3.0" ]; then
          log "This is not a CLC.  Skipping DB upgrade."
          return
      else 
          log "Cannot locate previous database files in ${OLD_EUCA}/${DBDIR}! Skipping DB upgrade."
          return
      fi
  fi
  
  if [ -n "${SAME_VERSION}" ]; then
    for x in $(\ls -ad ${OLD_EUCA}/${DBDIR}/* 2>&1 | grep eucalyptus) ; do
      cp -a --preserve $x ${NEW_EUCA}/${DBDIR}/
    done
    return
  fi

  # clean out the target DB directory
  # rm -rfv ${NEW_EUCA}/${DBDIR}/* > ${UP_LOG} 2>&1
  
  # Arbitrary string of properly formatted opts passed direct to jvm"
  UPGRADE_OPTS="-Deuca.log.level=DEBUG -Djava.security.egd=file:/dev/./urandom -XX:MaxPermSize=512m"
  # Upgrade version and directory info
  UPGRADE_OLD_ARGS="--upgrade-old-version=${OLD_EUCA_VERSION} --upgrade-old-dir=${OLD_EUCA}"
  UPGRADE_ARGS="--upgrade  ${UPGRADE_OLD_ARGS}"
  UPGRADE_CMD="${EUCALYPTUS}/usr/sbin/eucalyptus-cloud -u ${EUCA_USER} -h ${EUCALYPTUS} ${UPGRADE_ARGS}"
  echo "${UPGRADE_CMD}" >> ${UP_LOG} 
  
  echo "Upgrading your database..." | tee -a ${UP_LOG} 2>&1
  chown $EUCA_USER $UP_LOG
  
  # This is to deal with possible database connection leaks in the upgrade script
  ulimit -n 65536

  if ! ${UPGRADE_CMD} | tee -a ${UP_LOG} 2>&1; then
    echo "Database upgrade failed! Please ${UP_LOG} for details and contact Eucalyptus support for assistance."
    exit 1
  fi
}

## NOTE: Start reading here.
## Parse cmd line options.
NEW_ARGS=$(echo "${@}" | sed 's/--\(.\)\w*/-\1/g')
for f in $(echo "${@}" | sed 's/ /\n/g' | awk ' $1 ~ /--\w*/' | sed 's/--//g'); do if ! echo ${LEGAL_LONG_OPTS} | grep $f >/dev/null 2>&1; then echo "Illegal option: --$f"; FAIL="true"; fi; done
if [ -n "${FAIL}" ]; then exit 1;fi
while getopts ":hn:fo:ckdpsu:" flag ${NEW_ARGS} ; do
  case $flag in
    h) usage; exit 0;;
    n) 
      NEW_EUCA=${OPTARG}
      NEW_EUCA_VERSION=$(get_euca_version NEW_EUCA)
    ;;
    o) 
      OLD_EUCA=${OPTARG}
      OLD_EUCA_VERSION=$(get_euca_version OLD_EUCA)
    ;;
    f) UP_FORCE="force";;
    d) UP_DB="upgrade_db";;
    k) UP_KEYS="upgrade_keys";;
    c) UP_CONF="upgrade_conf";;
    s) UP_SAMEVERSION="sameversion";;
    u) EUCA_USER=${OPTARG};;
  esac
done
## After this point guaranteed that NEW_EUCA and OLD_EUCA are set.
export EUCALYPTUS="${NEW_EUCA}"
UP_LOG=${NEW_EUCA}/var/log/eucalyptus/upgrade.log
if [ "${NEW_EUCA_VERSION}" == "${OLD_EUCA_VERSION}" ]; then SAME_VERSION="true"; fi
if [ "${NEW_EUCA_VERSION}" == "eee-2.0.1" -a "${OLD_EUCA_VERSION}" == "eee-2.0.0" ]; then SAME_VERSION="true"; fi

if [ ! -d "${NEW_EUCA}/var/lib/eucalyptus" ]; then echo "${NEW_EUCA} does not point to a valid Eucalyptus installation. Aborting." ; exit 1 ; fi
## NOTE: Add an externalized v.old -> v.new upgrade-path check.
log "#                           UPGRADE INFORMATION\n#================================================================================" 
log "# Old Version:              %-12.12s" "${OLD_EUCA_VERSION}"
log "# New Version:              %-12.12s" "${NEW_EUCA_VERSION}"
log "# Upgrade keys:             %-12.12s       using: %-15.15s" "$(boole ${UP_KEYS})" ${UP_KEYS}
log "# Upgrade configuration:    %-12.12s       using: %-15.15s" "$(boole ${UP_CONF})" ${UP_CONF}
log "# Upgrade database:         %-12.12s       using: %-15.15s" "$(boole ${UP_DB})" ${UP_DB}
log "# Same version:             %-12.12s       using: %-15.15s" "$(boole ${UP_SAMEVERSION})" ${UP_SAMEVERSION}

for f in $(echo ${UP_SAMEVERSION} ${UP_KEYS} ${UP_DB} ${UP_CONF}); do
  log "$(echo "# Start upgrading: ${f}" | sed 's/upgrade_//g')"
  if ! $f; then
    log "ERROR:  Upgrading failed for ${f//upgrade_/}.  See ${UP_LOG} for details."
  else
    log "$(echo "# Done upgrading:  ${f}" | sed 's/upgrade_//g;s/_//g')"
  fi
done
## NOTE: Don't bother reading past here.
























## ONEDAY: Restore --backup
#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/${KEYDIR}/ $EUCADIR/${DBDIR}/ $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
