#! /bin/bash
#
# This script need root rights. Before run it, make sure you have root
# right.
#
# It used to configure slapos node, it could be run at anytime to
# check the configure of slapos node. The main functions:
#
#     * Install msloop network adapter, named to re6stnet-lo
#
#     * ipv6: Ipv6 configure
#
#     * re6stnet: Install re6stnet and register to nexedi re6stnet if it hasn't
#
#     * node: Create node configure file by parameters ca/key
#
#     * client: Create client configure file by parameters ca/key
#
#     * cron: create cron configure file
#
# Usage:
#
#    ./slapos-configure
#
export PATH=/usr/local/bin:/usr/bin:$PATH

for myprofile in ~/.bash_profile ~/.profile ; do
    grep -q "export CYGWIN=server" $myprofile || echo "export CYGWIN=server" >> $myprofile
done

#-------------------------------------------------
# Common functions
#-------------------------------------------------

#
# Return connection name by line, and replace space with '%'
#
function get_all_connections()
{
    netsh interface ipv6 show interface | \
    grep "^[ 0-9]\+ " | \
    sed -e "s/^[ 0-9]\+[a-zA-Z]\+//" -e "s/^\s*//" -e "s/ /%/g"
}

#
# Check all the connection names, and compare the original connection
# list, return the new connection name
#
# If nothing found, return empty
# If more than one, return the first one
#
function get_new_connection()
{
    original_connections=" $* "
    current_connections=$(get_all_connections)

    for name in $current_connections ; do
        [[ ! "$original_connections" == *[\ ]$name[\ ]* ]] && \
        echo ${name//%/ } && return 0
    done
}

#
# Remove all ipv4/ipv6 addresses in the connection re6stnet-lo
#
function reset_connection()
{
    ifname=${1-re6stnet-lo}
    for addr in $(netsh interface ipv6 show address $ifname level=normal | \
                grep "^Manual" | \
                sed -e "s/^\(\w\+\s\+\)\{4\}//") ; do
        netsh interface ipv6 del address $ifname $addr
    done
    netsh interface ip set address $ifname source=dhcp
    # for addr in $(netsh interface ip show address $ifname | \
    #             grep "IP Address:" | \
    #             sed -e "s/IP Address://") ; do
    #     netsh interface del address $ifname $addr
    # done
}

#
# Transfer connection name to GUID
#
function connection2guid()
{
    ifname=${1-re6stnet-lo}
    #
    # This command doesn't work in the Windows 7, Window 8, maybe
    # Vista. Because no guid information in these platforms.
    #
    # netsh interface ipv6 show interface $ifname | \
    #     grep "^GUID\s*:" | \
    #     sed -e "s/^GUID\s*:\s*//"
    #
    # So we use getmac to repleace it:
    getmac /fo list /v | grep -A3 "^Connection Name: *$ifname\$" \
        | grep "^Transport Name:" | sed -e "s/^.*Tcpip_//g"
}

#
# Show error message and waiting for user to press any key quit
#
function show_error_exit()
{
    msg=${1-Failed to configure Slapos Node in this computer.}
    echo $msg
    read -n 1 -p "Press any key to exit..."
    exit 1
}

#
# Query the parameter, usage:
#
#   query_parameter ACTUAL EXCPETED MESSAGE
#
function query_parameter()
{
    if [[ X$1 == X || $1 == "*" || $1 == "all" ]] ; then
        return 1
    fi
    if [[ $1 == "?" || $1 == "query" ]] ; then
        read -n 1 -p $3 user_ack
        if [[ X$user_ack == X[Yy] ]] ; then
            return 1
        else
            return 0
        fi
    fi
    if [[ $1 == $2 ]] ; then
        return 1
    fi
    return 0
}

#-------------------------------------------------
# Constants
#-------------------------------------------------
slapos_client_home=~/.slapos
client_configure_file=$slapos_client_home/slapos.cfg
client_certificate_file=$slapos_client_home/certificate
client_key_file=$slapos_client_home/key
client_template_file=/etc/slapos/slapos-client.cfg.example
url_client_template_file=http://git.erp5.org/gitweb/slapos.core.git/blob_plain/HEAD:/slapos-client.cfg.example

node_certificate_file=/etc/opt/slapos/ssl/computer.crt
node_key_file=/etc/opt/slapos/ssl/computer.key
node_config_file=/etc/opt/slapos/slapos.cfg
node_template_file=/etc/slapos/slapos.cfg.example
url_node_template_file=http://git.erp5.org/gitweb/slapos.core.git/blob_plain/HEAD:/slapos.cfg.example

slapos_ifname=re6stnet-lo
# Hope it will not confilct with original network in the local machine
ipv4_local_network=10.201.67.0/24

slapos_runner_file=/etc/slapos/scripts/slap-runner.html
slaprunner_cfg='http://git.erp5.org/gitweb/slapos.git/blob_plain/refs/heads/cygwin-0:/software/slaprunner/software.cfg'

#-------------------------------------------------
# Create paths
#-------------------------------------------------

mkdir -p /etc/opt/slapos/ssl/partition_pki
mkdir -p $slapos_client_home

#-------------------------------------------------
# Configure cygwin server services
#-------------------------------------------------

echo Checking cygserver service ...
cygrunsrv --query cygserver > /dev/null 2>&1
if (( $? )) ; then
    echo Run cygserver-config ...
    /usr/bin/cygserver-config --yes || \
        show_error_exit "Failed to run cygserver-config"
else
    echo The cygserver service has been installed.
fi

echo Checking syslog-ng service ...
cygrunsrv --query syslog-ng > /dev/null 2>&1
if (( $? )) ; then
    echo Run syslog-ng-config ...
    /usr/bin/syslog-ng-config --yes || \
        show_error_exit "Failed to run syslog-ng-config"
else
    echo The syslog-ng service has been installed.
fi

echo Checking cron job ...
ps -ef | grep -q "/usr/sbin/cron"
if (( $? )) ; then
    echo Starting cron job ...
    /usr/sbin/cron &
    (( $? )) && show_error_exit "Failed to run cron-config"
    disown -h
    echo The cron job started.
else
    echo The cron job is running.
fi

#-------------------------------------------------
# Configure slapos network
#-------------------------------------------------

#
# Add msloop network adapter, ane name it as "re6stnet-lo"
#
echo Checking slapos network adapter: $slapos_ifname ...
original_connections=$(echo $(get_all_connections))
if [[ ! " $original_connections " == *[\ ]$slapos_ifname[\ ]* ]] ; then
    echo Installing slapos network adapter ...
    devcon install $WINDIR\\inf\\netloop.inf *MSLOOP
    connection_name=$(get_new_connection $original_connections)
    [[ "X$connection_name" == "X" ]] && \
        show_error_exit "Add msloop network adapter failed."
    echo
    netsh interface set interface name="$connection_name" newname="$slapos_ifname"
fi
#ip -4 addr add $ipv4_local_network dev $slapos_ifname
# reset_connection $slapos_ifname
echo SlapOS network adapter OK.
echo Slapos ipv4_local_network is $ipv4_local_network

#-------------------------------------------------
# Generate slapos node configure file
#-------------------------------------------------

echo Checking computer certificate file ...
if [[ ! -f $node_certificate_file ]] ; then
    read -p "Where is computer certificate file (/computer.crt): " certificate_file
    [[ X$certificate_file == X ]] && certificate_file=/computer.crt
    [[ ! -f "$certificate_file" ]] && \
        show_error_exit "Certificate file $certificate_file doesn't exists."
    echo "Copy certificate from $certificate_file to $node_certificate_file"
    certificate_file=$(cygpath -u $certificate_file)
    cp $certificate_file $node_certificate_file
else
    echo Found computer certificate file: $node_certificate_file
fi
openssl x509 -noout -in $node_certificate_file || \
    show_error_exit "Invalid computer certificate: $node_certificate_file."
echo Check computer certificate OK.

echo Checking computer guid ...
computer_id=$(grep  CN=COMP $node_certificate_file | sed -e "s/^.*, CN=//g" | sed -e "s%/emailAddress.*\$%%g")
[[ "$computer_id" == COMP-+([0-9]) ]] || \
    show_error_exit "Invalid computer id specified."
echo Computer GUID is: $computer_id

echo Checking computer key file ...
if [[ ! -f $node_key_file ]] ; then
    read -p "Where is computer key file (/computer.key): " key_file
    [[ X$key_file == X ]] && key_file=/computer.key
    [[ ! -f "$key_file" ]] && \
        show_error_exit "Key file $key_file doesn't exists."
    echo "Copy key from $key_file to $node_key_file"
    key_file=$(cygpath -u $key_file)
    cp $key_file $node_key_file
else
    echo Found computer key file: $node_key_file
fi
openssl rsa -noout -in $node_key_file -check || \
    show_error_exit "Invalid computer key: $node_key_file."
echo Check computer key OK.

# Create node configure file, replace interface_name with guid of
# re6stnet-lo
echo Checking computer configure file ...
if [[ ! -f $node_config_file ]] ; then
    [[ -f $node_template_file ]] || \
        (cd /etc/slapos; wget $url_node_template_file -O $node_template_file) || \
        show_error_exit "Download slapos.cfg.example failed."
    echo "Copy computer configure file from $node_template_file to $node_config_file"
    cp $node_template_file $node_config_file
fi

interface_guid=$(connection2guid $slapos_ifname) || \
    show_error_exit "Failed to get guid of interface: $slapos_ifname."

echo "Computer configuration information:"
echo "  interface name:     $slapos_ifname"
echo "  GUID:               $interface_guid"
echo "  ipv4_local_network: $ipv4_local_network"
echo "  computer_id:        $computer_id"
# generate /etc/slapos/slapos.cfg
sed -i  -e "s%^\\s*interface_name.*$%interface_name = $interface_guid%" \
        -e "s%^#\?\\s*ipv6_interface.*$%# ipv6_interface =%g" \
        -e "s%^ipv4_local_network.*$%ipv4_local_network = $ipv4_local_network%" \
        -e "s%^computer_id.*$%computer_id = $computer_id%" \
        $node_config_file
echo Check computer configure file OK.

#-------------------------------------------------
# Generate slapos client configure file
#-------------------------------------------------

echo Checking client certificate file ...
if [[ ! -f $client_certificate_file ]] ; then
    read -p "Where is client certificate file (/certificate): " certificate_file
    [[ X$certificate_file == X ]] && certificate_file=/certificate
    [[ ! -f "$certificate_file" ]] && \
        show_error_exit "Certificate file $certificate_file doesn't exists."
    echo "Copy client certificate from $certificate_file to $client_certificate_file"
    certificate_file=$(cygpath -u $certificate_file)
    cp $certificate_file $client_certificate_file
fi
openssl x509 -noout -in $client_certificate_file || \
    show_error_exit "Invalid client certificate: $client_certificate_file."
echo Check client certificate Ok.

echo Checking client key file ...
if [[ ! -f $client_key_file ]] ; then
    read -p "Where is client key file (/key): " key_file
    [[ X$key_file == X ]] && key_file=/key
    [[ ! -f "$key_file" ]] && \
        show_error_exit "Key file $key_file doesn't exists."
    echo "Copy client key from $key_file to $client_key_file"
    key_file=$(cygpath -u $key_file)
    cp $key_file $client_key_file
fi
openssl rsa -noout -in $client_key_file -check || \
    show_error_exit "Invalid client key: $client_key_file."
echo Checking computer key OK.

echo Checking client configure file ...
if [[ ! -f $client_configure_file ]] ; then
    [[ -f $client_template_file ]] || \
    (cd /etc/slapos; wget $url_client_template_file -O $client_template_file) || \
    show_error_exit "Download slapos-client.cfg.example failed."
    echo "Copy client configure file from $client_template_file to $client_config_file"
    cp $client_template_file $client_configure_file
fi

echo Client configuration information:
echo     client certificate file: $client_certificate_file
echo     client key file:         $client_key_file
echo
sed -i -e "s%^cert_file.*$%cert_file = $client_certificate_file%" \
       -e "s%^key_file.*$%key_file = $client_key_file%" \
       $client_configure_file
echo Check client configure file OK.

#-------------------------------------------------
# Re6stnet
#-------------------------------------------------

# Check ipv6, install it if it isn't installed.
echo Checking ipv6 protocol ...
netsh interface ipv6 show interface > /dev/null || netsh interface ipv6 install || \
    show_error_exit "Failed to install ipv6 protocol."
echo IPv6 protocol has been installed.

# miniupnpc is required by re6stnet
echo Checking miniupnpc ...
if [[ ! -d /opt/miniupnpc ]] ; then
    [[ -f /miniupnpc.tar.gz ]] || show_error_exit "No package found: /miniupnpc.tar.gz"
    echo "Installing miniupnpc ..."
    cd /opt
    tar xzf /miniupnpc.tar.gz --no-same-owner
    mv $(ls -d miniupnpc-*) miniupnpc
    cd miniupnpc
    make
    python setup.py install || show_error_exit "Failed to install miniupnpc."
    echo "Install miniupnpc OK."
else
    echo Check miniupnpc OK.
fi

# pyOpenSSL is required by re6stnet
echo Checking pyOpenSSL ...
if [[ ! -d /opt/pyOpenSSL ]] ; then
    [[ -f /pyOpenSSL.tar.gz ]] || show_error_exit "No package found: /pyOpenSSL.tar.gz"
    echo "Installing pyOpenSSL ..."
    cd /opt
    tar xzf /pyOpenSSL.tar.gz --no-same-owner
    mv $(ls -d pyOpenSSL-*) pyOpenSSL
    cd pyOpenSSL
    python setup.py install ||  show_error_exit "Failed ot install pyOpenSSL."
    echo "Install pyOpenSSL OK."
else
    echo Check pyOpenSSL OK.
fi

# Install re6stnet
echo Checking re6stnet ...
if [[ ! -d /opt/re6stnet ]] ; then
    echo "Installing re6stnet ..."
    cd /opt
    if [[ -f /re6stnet.tar.gz ]] ; then
        tar xzf /re6stnet.tar.gz --no-same-owner
        mv $(ls -d re6stnet-*) re6stnet
    else
        echo "Clone re6stnet from http://git.erp5.org/repos/re6stnet.git"
		git clone -b cygwin http://git.erp5.org/repos/re6stnet.git
    fi
    cd re6stnet
    python setup.py install || show_error_exit "Failed to install re6stnet."
    echo "Install re6stnet OK."
else
    echo Check re6stnet OK.
fi

echo Checking re6stent configuration ...
mkdir -p /etc/re6stnet
cd /etc/re6stnet
if [[ ! -f re6stnet.conf ]] ; then
    echo Register to http://re6stnet.nexedi.com ...
    # Your subnet: 2001:67c:1254:e:19::/80 (CN=917529/32)
    mysubnet=$(re6st-conf --registry http://re6stnet.nexedi.com/ --anonymous | grep "^Your subnet:") \
        || show_error_exit "Register to nexedi re6stnet failed"
    echo Register OK.
    echo
    echo $mysubnet
    echo
    echo Write subnet information to re6stnet.conf
    echo "# $mysubnet" >> re6stnet.conf
    echo Write "table 0" to re6stnet.conf
    echo "table 0" >> re6stnet.conf

fi
[[ ! -f re6stnet.conf ]] && \
    show_error_exit "Failed to register to nexedi re6stnet: no /etc/re6stnet/re6stnet.conf found."
grep -q "^table " re6stnet.conf || \
    show_error_exit "Error: no parameter 'table 0' found in the /etc/re6stnet/re6stnet.conf"
grep -q "^# Your subnet: " re6stnet.conf || \
    show_error_exit "Error: no subnet found in the /etc/re6stnet/re6stnet.conf"
echo Check re6stnet configuration OK.
echo

#-------------------------------------------------
# Create openvpn tap-windows drivers used by re6stnet
#-------------------------------------------------

# Adding tap-windows driver will break others, so we add all drivers
# here. Get re6stnet client count, then remove extra drivers and add
# required drivers.
#
echo
echo Installing OpenVPN Tap-Windows Driver ...
echo
original_connections=$(echo $(get_all_connections))
client_count=$(sed -n -e "s/^client-count *//p" /etc/re6stnet/re6stnet.conf)
[[ -z $client_count ]] && client_count=10
echo Re6stnet client count = $client_count
re6stnet_name_list="re6stnet-tcp re6stnet-udp"
for (( i=1; i<=client_count; i=i+1 )) ; do
    re6stnet_name_list="$re6stnet_name_list re6stnet$i"
done
for re6stnet_ifname in $re6stnet_name_list ; do
    echo Checking interface $re6stnet_ifname ...
    if [[ ! " $original_connections " == *[\ ]$re6stnet_ifname[\ ]* ]] ; then
        echo Installing  interface $re6stnet_ifname ...
        ip vpntap add dev $re6stnet_ifname || show_error_exit "Failed to install openvpn tap-windows driver."
        echo Interface $re6stnet_ifname installed.
    else
        echo $re6stnet_ifname has been installed.
    fi
done
#
# Remove OpenVPN Tap-Windows Driver
#
# ip vpntap del dev re6stnet-x
#

#-------------------------------------------------
# Create instance of Web Runner
#-------------------------------------------------

slaprunner_title="SlapOS-Node-Runner-In-$computer_id"

grep -q "window.location.href" $slapos_runner_file
if (( $? )) ; then
    echo
    echo Installing Web Runner ...
    echo

    re6stnet_ipv6=$(cat /etc/re6stnet/re6stnet.conf | grep "Your subnet" | \
        sed -e "s/^.*subnet: //g" -e "s/\/80 (CN.*\$/1/g")
    echo "Re6stnet address in this computer: $re6stnet_ipv6"
    netsh interface ipv6 show addr $slapos_ifname level=normal | grep -q $re6stnet_ipv6 || \
        netsh interface ipv6 add addr $slapos_ifname $re6stnet_ipv6
    echo Run slapformat ...
    /opt/slapos/bin/slapos node format -cv --now ||
        show_error_exit "Failed to run slapos format."
    echo

    echo "Supply $slaprunner_cfg in the computer $computer_id"
    /opt/slapos/bin/slapos supply  $slaprunner_cfg $computer_id
    echo "Request an instance 'Node Runner' ..."
    while true ; do
        /opt/slapos/bin/slapos node software --verbose
        /opt/slapos/bin/slapos node instance --verbose
        /opt/slapos/bin/slapos node report --verbose
        /opt/slapos/bin/slapos request $client_config_file $slaprunner_title $slaprunner_cfg --node computer_guid=$computer_id && break
        sleep 5
    done
    # Connection parameters of instance are:
    #  {'backend_url': 'http://[2001:67c:1254:45::c5d5]:50000',
    #  'cloud9-url': 'http://localhost:9999',
    #  'password_recovery_code': 'e2d01c14',
    #  'ssh_command': 'ssh 2001:67c:1254:45::c5d5 -p 2222',
    #  'url': 'http://softinst39090.host.vifib.net/'}
    slaprunner_url=$(/opt/slapos/bin/slapos request $client_config_file $slaprunner_title $slaprunner_cfg --node computer_guid=$computer_id | \
        grep backend_url | sed -e "s/^.*': '//g" -e "s/',.*$//g")
    echo Got node runner url: $slaprunner_url
    [[ -z $slaprunner_url ]] && show_error_exit "Failed to create instance of SlapOS Web Runner."

    cp $slapos_runner_file{.html, .html.orig}
    cat <<EOF > $slapos_runner_file
<html>
<head><title>SlapOS Web Runner</title>
<script LANGUAGE="JavaScript">
<!--
function openwin() {
  window.location.href = "$slaprunner_url"
}
//-->
</script>
</head>
<body onload="openwin()"/>
</html>
EOF
    echo Generate file: $slapos_runner_file

    # Apply patches to slapos.cookbook for inotifix
    patch_file=/etc/slapos/patches/slapos-cookbook-inotifyx.patch
    if [[ -f $patch_file ]] ; then
        echo "Apply patch: $patch_file"
        for x in $(find /opt/slapgrid/ -name slapos.cookbook-*.egg) ; do
            echo Apply to $x
            cd $x
            patch --dry-run -p1 < $patch_file && patch -p1 < $patch_file
        done
    fi
    echo
    echo Install Web Runner OK.
    echo
fi

#-------------------------------------------------
# Configure crontab
#-------------------------------------------------
crontab_file=/var/cron/tabs/$(whoami)
if [[ ! -f $crontab_file ]] ; then
    cat <<EOF  > $crontab_file
SHELL=/bin/sh
PATH=/usr/bin:/usr/sbin:/sbin:/bin
MAILTO=""

# Run "Installation/Destruction of Software Releases" and "Deploy/Start/Stop Partitions" once per minute
* * * * * Administrator /opt/slapos/bin/slapos node software --verbose --logfile=/opt/slapos/log/slapos-node-software.log > /dev/null 2>&1
* * * * * Administrator /opt/slapos/bin/slapos node instance --verbose --logfile=/opt/slapos/log/slapos-node-instance.log > /dev/null 2>&1

# Run "Destroy Partitions to be destroyed" once per hour
0 * * * * Administrator /opt/slapos/bin/slapos node report --maximal_delay=3600 --verbose --logfile=/opt/slapos/log/slapos-node-report.log > /dev/null 2>&1

# Run "Check/add IPs and so on" once per hour
0 * * * * Administrator /opt/slapos/bin/slapos node format >> /opt/slapos/log/slapos-node-format.log 2>&1


# Make sure we have only good network routes if we use VPN
# * * * * * root if [ -f /etc/opt/slapos/openvpn-needed  ]; then ifconfig tapVPN | grep "Scope:Global" > /dev/null ;if [ $? = 0 ]; then ROUTES=$(ip -6 r l | grep default | awk '{print $5}'); for GW in $ROUTES ; do if [ ! $GW = tapVPN ]; then /sbin/ip -6 route del default dev $GW > /dev/null 2>&1;fi ;done ;fi ;fi
EOF
    echo Cron file $crontab_file created.
fi

#-------------------------------------------------
# Add slapos-configure to windows startup item
#-------------------------------------------------
slapos_run_key='\HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
slapos_run_entry=SlapOS-Node
slapos_run_script=/etc/slapos/scripts/slapos-configure.sh
echo Checking startup item ...
regtool -q get "$slapos_run_key\\$slapos_run_entry" || \
    regtool -q set "$slapos_run_key\\$slapos_run_entry" \
    "\"$(cygpath -w /usr/bin/bash)\" --login -i $slapos_run_script" || \
    show_error_exit "Failed to add slapos-configure.sh as windows startup item."
echo Startup item "$slapos_run_key\\$slapos_run_entry": $(regtool get "$slapos_run_key\\$slapos_run_entry")
echo 

echo SlapOS Node configure successfully.
read -n 1 -t 60 -p "Press any key to exit..."
exit 0