#!/bin/bash

##
# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
# This file is part of openvim
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# For those usages not covered by the Apache License, Version 2.0 please
# contact with: nfvlabs@tid.es
##

#Get configuration of a host for using it as a compute node

function usage(){

    echo -e "\nUsage: $0 --user-name <user> --ip=<X.X.X.X> --cores=<core_number> --huge-pages-1G=<huga_pages_number> --nb-10GB-interfaces==<interface_number>"
    echo -e "Generate a develop host yaml to be used for openvim host-add\n"
    echo -e "   --user-name -u  <user>*       Server OAM Ip"
    echo -e "   --ip        -i  <ip>*         Server hostname"
    echo -e "   --cores     -c  <cores>       Numa Cores available must be an odd number and bigger or equal to 4."
    echo -e "   --huge-pages-1G      -hp  <huge_pages_number>   Must be an odd number and bigger or equal to 16. 4GiB of memory will be reserved for the host OS, the rest will be used by VM."
    echo -e "   --nb-10GB-interfaces -ni  <nb-10GB-interfaces>  Dataplane interfaces must be an odd number and bigger or equal to 4."
    echo -e "   * Mandatory arguments"
    echo -e "\n"
    echo -e "The output will be a server descriptor with two numas and resources (memory, cores and interfaces) equally distributed between them."
    echo -e "Each interface (physical funtion) will have defined 8 SR-IOV (virtual functions)."

    exit 1
}

function get_hash_value() {   echo `eval  echo $\{\`echo $USER[$IP]\`\}`; }

function get_mac(){
  seed=$1
  b1=$((seed%16)); seed=$((seed/16))
  b2=$((seed%16)); seed=$((seed/16))
  b3=$((seed%16)); seed=$((seed/16))
  b4=$((seed%16)); seed=$((seed/16))
  b5=$((seed%16)); seed=$((seed/16))
  mac=`printf "%02X:%02X:%02X:%02X:%02X:%02X" 2 $b5 $b4 $b3 $b2 $b1`
  echo $mac
}

function _parse_opts(){

    #help argument
    if [ -n "$option_help" ];
    then
        usage
    fi

    #User argument
    [ -z "$option_user_name" ] && echo -e "ERROR: User argument is mandatory, --user-name=<user>\n" && usage
    USER_NAME=${option_user_name}

    [ -z "$option_ip" ] && echo -e "ERROR: OAM IP argument is mandatory, --ip=<X.X.X.X>\n" && usage
    IP=${option_ip}

    #TODO to be checl diference between real cores an numa cores
    #cores argument
    REAL_CORES=$(grep -c "^processor" "/proc/cpuinfo")
    if [ -z "$option_cores" ] ; then
        CORES=REAL_CORES
    else
        CORES=${option_cores}
    fi

    #Ensure the core user input is big enough
    ([ $((CORES%2)) -ne 0 ]) && echo -e "ERROR: Wrong number of cores\n" && usage

    MEMORY=$(($(grep MemTotal /proc/meminfo | awk '{print $2}') /1024/1024))
    if [ -z "$option_huge_pages_1G" ] ; then
        HUGE_PAGES_MEMORY=0
    else
        HUGE_PAGES_MEMORY=${option_huge_pages_1G}
    fi
    #Ensure the memory user input is big enough
    #([ $((MEMORY%2)) -ne 0 ] || [ $MEMORY -lt 16 ] ) && echo -e "ERROR: Wrong number of memory\n" && usage

    #nb_10GB_interfaces argument
    if [ -z "$nb_10GB_interfaces" ] ; then
        INTERFACES=8
    else
        INTERFACES=${nb_10GB_interfaces}
    fi
    ([ $((INTERFACES%2)) -ne 0 ] || [ $INTERFACES -lt 4 ] ) && echo -e "ERROR: Wrong number of interfaces\n" && usage

    # Parameter by default
    NUMAS=1
    ([ $((NUMAS%2)) -ne 0 ]) && NUMAS=1
}

function _generate_compute_develope_yaml(){

    _yaml_init

    FEATURES_LIST="lps,dioc,hwsv,tlbps,ht,lps,64b,iommu"

    #Generate a cpu topology for 4 numas with hyperthreading
    #in this developing/fake server all cores can be used
    #TODO check if this calculation is correct
    echo2file "#This file was created by $0"
    echo2file "#for adding this compute node to openvim"
    echo2file "#copy this file to openvim controller and run"
    echo2file "#openvim host-add <this>"
    echo2file
    echo2file "host:"
    echo2file "  name:    $HOST_NAME"
    echo2file "  user:    $USER_NAME"
    echo2file "  ip_name: $IP"
    echo2file "host-data:"
    echo2file "  name:        $HOST_NAME"
    echo2file "  user:        $USER_NAME"
    echo2file "  ip_name:     $IP"
    echo2file "  ranking:     100"
    echo2file "  description: $HOST_NAME"
    echo2file "  features:    $FEATURES_LIST"
    echo2file "  numas:"

    numa=0
    last_iface=0
    iface_counter=0

    while [ $numa -lt $NUMAS ]
    do

      HUGE_PAGES=$((HUGE_PAGES_MEMORY/2-2))
      ([ ${HUGE_PAGES} -lt -1 ]) && HUGE_PAGES=0

      echo2file "  - numa_socket:  $numa"
    #MEMORY
      echo2file "    hugepages: $((HUGE_PAGES))"
      echo2file "    memory:    $((MEMORY/$NUMAS))"
    #CORES
      echo2file "    cores:"

      for((n_core=0;n_core<$REAL_CORES;n_core++))
        do
            THREAD_ID=$(($n_core+1))
            CORE_ID=$(($((${n_core}+${numa}))/2))
            echo2file "    - core_id:   ${CORE_ID}"
            echo2file "      thread_id: ${THREAD_ID}"
            [ $CORE_ID -eq 0 ] && echo2file "      status:    noteligible"

            thread_counter=$((thread_counter+1))
      done
      # GENERATE INTERFACES INFORMATION AND PRINT IT
      seed=$RANDOM
      echo2file "    interfaces:"
      for ((iface=0;iface<$INTERFACES;iface+=2))
      do
        name="iface$iface_counter"
        bus=$((iface+last_iface))
        bus=$((iface))
        pci=`printf "0000:%02X:00.0" $bus`
        mac=`get_mac $seed`
        seed=$((seed+1))

        echo2file "    - source_name: $name"
        echo2file "      Mbps: 10000"
        echo2file "      pci: \"$pci\""
        echo2file "      mac: \"$mac\""
        echo2file "      sriovs:"

        PCI_COUNTER=0
        for((nb_sriov=0;nb_sriov<8;nb_sriov++))
        do
          #PCI_COUNTER=$((PCI_COUNTER+2))
          #echo2file "nb_sriov "$nb_sriov
          #echo2file "PCI_COUNTER "$PCI_COUNTER

          pci=`printf "0000:%02X:10.%i" $bus $nb_sriov`
          mac=`get_mac $seed`
          seed=$((seed+1))
          echo2file "      - source_name: eth$nb_sriov"
          echo2file "        mac: \"$mac\""
          echo2file "        pci: \"$pci\""
        done
        iface_counter=$((iface_counter+1))
      done
      last_iface=$(((numa+1)*127/NUMAS+5)) #made-up formula for more realistic pci numbers
      numa=$((numa+1))
    done
}

function _yaml_init(){
    echo -n > host-develope.yaml
}

function echo2file(){
    echo "${1}"
    echo "${1}" >> host-develope.yaml
}

function _get_opts()
{
    options="$1"
    shift

    get_argument=""
    #reset variables
    params=""
    for option_group in $options
    do
        _name=${option_group%%:*}
        _name=${_name%=}
        _name=${_name//-/_}
        eval option_${_name}='""'
    done

    while [[ $# -gt 0 ]]
    do
        argument="$1"
        shift
        if [[ -n $get_argument ]]
        then
            [[ ${argument:0:1} == "-" ]] && echo "option '-$option' requires an argument"  >&2 && return 1
            eval ${get_argument}='"$argument"'
            #echo option $get_argument with argument
            get_argument=""
            continue
        fi


        #short options
        if [[ ${argument:0:1} == "-" ]] && [[ ${argument:1:1} != "-" ]] && [[ ${#argument} -ge 2 ]]
        then
            index=0
            while index=$((index+1)) && [[ $index -lt ${#argument} ]]
            do
                option=${argument:$index:1}
                bad_option=y
                for option_group in $options
                do
                    _name=""
                    for o in $(echo $option_group | tr ":=" " ")
                    do
                        [[ -z "$_name" ]] && _name=${o//-/_}
                        #echo option $option versus $o
                        if [[ "$option" == "${o}" ]]
                        then
                            eval option_${_name}='${option_'${_name}'}-'
                            bad_option=n
                            if [[ ${option_group:${#option_group}-1} != "=" ]]
                            then
                                continue
                            fi
                            if [[ ${#argument} -gt $((index+1)) ]]
                            then
                                eval option_${_name}='"${argument:$((index+1))}"'
                                index=${#argument}
                            else
                                get_argument=option_${_name}
                                #echo next should be argument $argument
                            fi

                            break
                        fi
                    done
                done
                [[ $bad_option == y ]] && echo "invalid argument '-$option'?  Type -h for help" >&2 && return 1
            done
        elif [[ ${argument:0:2} == "--" ]] && [[ ${#argument} -ge 3 ]]
        then
            option=${argument:2}
            option_argument=${option#*=}
            option_name=${option%%=*}
            [[ "$option_name" == "$option" ]] && option_argument=""
            bad_option=y
            for option_group in $options
            do
                _name=""
                for o in $(echo $option_group | tr ":=" " ")
                do
                    [[ -z "$_name" ]] && _name=${o//-/_}
                    #echo option $option versus $o
                    if [[ "$option_name" == "${o}" ]]
                    then
                        bad_option=n
                        if [[ ${option_group:${#option_group}-1} != "=" ]]
                        then #not an argument
                            [[ -n "${option_argument}" ]] && echo "option '--${option%%=*}' do not accept an argument " >&2 && return 1
                            eval option_${_name}='"${option_'${_name}'}-"'
                        elif [[ -n "${option_argument}" ]]
                        then
                            eval option_${_name}='"${option_argument}"'
                        else
                            get_argument=option_${_name}
                            #echo next should be argument $argument
                        fi
                        break
                    fi
                done
            done
            [[ $bad_option == y ]] && echo "invalid argument '-$option'?  Type -h for help" >&2 && return 1
        elif [[ ${argument:0:2} == "--" ]]
        then
            option__="$*"
            bad_option=y
            for o in $options
            do
                if [[ "$o" == "--" ]]
                then
                    bad_option=n
                    option__=" $*"
                    break
                fi
            done
            [[ $bad_option == y ]] && echo "invalid argument '--'?  Type -h for help" >&2 && return 1
            break
        else
            params="$params ${argument}"
        fi

    done

    [[ -n "$get_argument" ]] && echo "option '-$option' requires an argument"  >&2 && return  1
    return 0
}

#process options
DIRNAME=$(readlink -f ${BASH_SOURCE[0]})
DIRNAME=$(dirname $DIRNAME)

#source ${DIRNAME}/get-options.sh "help:h user:u= ip:i= cores:c= huge-pages-1G:hp= nb-10GB-interfaces:ni="  $*
_get_opts "help:h user-name:u= ip:i= cores:c= huge-pages-1G:hp= nb-10GB-interfaces:ni="  $* || exit 1
_parse_opts

#check root privileges
[ "${USER}" != "root" ] && echo "Needed root privileges" >&2 && exit 2
#check root privileges and non a root user behind


HOST_NAME=`cat /etc/hostname`

_generate_compute_develope_yaml

