#!/bin/sh -e

##########################################################################
#   Script description:
#       Discover new DHCP leases and add to /etc/hosts and dhcp.conf.
#       
#   History:
#   Date        Name        Modification
#   2014-11-18  Jason Bacon Begin
#                           Based on Jim Wagner's node-discover
##########################################################################

pause()
{
    local junk
    
    printf 'Press return to continue...'
    read junk
}

usage()
{
    cat << EOM

Usage: $0 base-name digits domain

Example:

    $0 compute- 3 local ==>
	compute-001.meadows.local
	compute-002.meadows.local
	...
	
    $0 raid- 2 bigcluster.party.edu ==>
	raid-02.bigcluster.party.edu
	raid-03.bigcluster.party.edu
	...

EOM
    exit 1
}


##########################################################################
#   Function description:
#       Detect new leases in dhcp.leases that are not in hosts
#       
#   History:
#   Date        Name        Modification
#   2014-11-21  Charlie &   Begin
##########################################################################

get_new_leases()
{
    local leases lease_file ip state
    
    new_leases=''
    
    if [ $# != 1 ]; then
	printf "Usage: get_new_leases lease-file\n"
	exit 1
    fi
    lease_file=$1

    leases=$(awk '$1 == "lease" { print $2 }' $lease_file | sort -n -t . -k 4)
    
    # Detect unconfigured servers
    for ip in $leases; do
	# Already recorded?
	# Escape periods so 192.168.1.3 doesn't match 192.168.193
	search_ip=$(echo $ip | sed -e 's|\.|\\\.|g')
	if ! egrep -qw "^#? *$search_ip" /etc/hosts; then
	    printf "\n$ip not found in /etc/hosts.\n"
	    
	    # Get lease state from dhcpd.leases
	    state=`awk -v ip=$ip '$2 == ip {
		do
		{
		    getline
		}   while ( $1 != "binding" || $2 != "state" );
		print $3;
		exit;
	    }'  $lease_file`
	    
	    # Active leases not already in /etc/hosts must be added
	    if [ 0$state = 0'active;' ]; then
		new_leases="$new_leases $ip"
	    fi
	fi
    done
}


##########################################################################
#   Main
##########################################################################

if [ $# != 3 ]; then
    usage
fi

base_name="$1"
digits=$2
domain=$3

for lease_file in /var/db/dhcpd/dhcpd.leases /var/lib/dhcpd/dhcpd.leases; do
    if [ -e "$lease_file" ]; then
	break
    fi
done
if [ -z "$lease_file" ]; then
    printf 'Error: No dhcpd.leases found!\n'
    exit 1
fi

for dhcp_conf in /usr/local/etc/dhcpd.conf /etc/dhcp/dhcpd.conf; do
    if [ -e "$dhcp_conf" ]; then
	break
    fi
done
if [ -z "$dhcp_conf" ]; then
    printf 'Error: No dhcpd.conf found!\n'
    exit 1
fi

printf "Using $lease_file and $dhcp_conf...\n"

if ! fgrep $base_name /etc/hosts | grep $domain; then
    printf "No existing hosts beginning with $base_name.\n"
fi

while [ 0$serial_number = 0 ]; do
    printf "Next serial number? "
    read serial_number
done

cat << EOM

Now specify the bits in a prefix covering ALL local network segments.

E.g. If you use the following segments:

Primary     192.168.0.0/17  # Primary and management share one larger block
Management  192.168.64.0/17
HPC         192.168.128.0/18
Aux         192.168.192.0/18

then specify 16 here.

EOM
default_bits=$(auto-get-netmask-bits)
default_bits=$(( default_bits - 1 ))
printf "Bits in prefix covering all local network segments? [$default_bits] "
read bits
if [ 0$bits = 0 ]; then
    bits=$default_bits
fi

case $(auto-ostype) in
FreeBSD)
    dhcp_service=isc-dhcpd
    if ! which gsed > /dev/null 2>&1; then
	pkg install -y gsed
    fi
    sed=gsed
    ;;

RHEL)
    dhcp_service=dhcpd
    sed=sed
    ;;

*)
    auto-unsupported-os $0
    exit 1
    ;;

esac

# Restart the service to clean up outdated leases
service $dhcp_service restart

again='y'
while [ 0$again != 0n ]; do

    get_new_leases $lease_file
    printf "New leases: $new_leases\n"
    
    if [ -z "$new_leases" ]; then
	printf 'No new leases detected.  Wait for new servers? ([y]/n) '
	read wait
	if [ 0$wait != 0n ]; then
	    while [ -z "$new_leases" ]; do
		sleep 2
		printf '.'
		get_new_leases $lease_file
	    done
	fi
    fi
    
    for new_ip in $new_leases; do
	hwaddr=`awk -v new_ip=$new_ip '$1 == "lease" && $2 == new_ip {
	    do
	    {
		getline
	    }   while ( $1 != "hardware" );
	    print $3
	    exit
	}' $lease_file | cut -d ';' -f 1`
    
	printf '\nNew lease detected: %s %s.\n' "$new_ip" "$hwaddr"
	
	default_host_name=`printf "$base_name%0${digits}d.%s" $serial_number $domain`
	printf "Host name? [$default_host_name] "
	read host_name
	if [ 0$host_name = 0 ]; then
	    host_name=$default_host_name
	fi
	short_name=${host_name%%.*}
	printf "$short_name\n"
	
	if ! fgrep -w $host_name $dhcp_conf; then
	    $sed -i '/option domain-name-servers/ a\\n    host '"$host_name"' {\n\thardware ethernet '"$hwaddr"';\n\toption host-name \"'"$host_name"'\";\n\tfixed-address '"$new_ip"';\n    }' $dhcp_conf
	fi
	# vi $dhcp_conf
	service $dhcp_service restart
	
	# Disable prompt for first ssh
	auto-disable-host-key-prompt $short_name $host_name $new_ip
	
	# Add to /etc/hosts.
	if ! fgrep -q "$new_ip" /etc/hosts; then
	    printf '\n' >> /etc/hosts
	fi
	
	# Escape periods so 192.168.1.3 doesn't match 192.168.193
	search_ip=$(echo $new_ip | sed -e 's|\.|\\\.|g')
	# printf "$new_ip\t$host_name\t\t${host_name%%.*}\n"
	auto-append-line "^$search_ip" "$new_ip\t$host_name\t\t${host_name%%.*}" \
	    /etc/hosts nocomment
	f3=$(echo $new_ip | awk -F . '{ print $3 }')

	new_ip_hex=$(auto-octet-to-hex $new_ip)
	addresses_per_segment=$(( (1 << (32 - $bits)) / 4 ))
	
	mgmt_hex=$(( $new_ip_hex + $addresses_per_segment ))
	mgmt_ip=$(auto-hex-to-octet $mgmt_hex)
	mgmt_name=${host_name%.*}-mgmt.${host_name##*.}
	# Escape periods so 192.168.1.3 doesn't match 192.168.193
	search_ip=$(echo $mgmt_ip | sed -e 's|\.|\\\.|g')
	# printf "$mgmt_ip\t$mgmt_name\t${mgmt_name%%.*}\n"
	auto-append-line "^$search_ip" "$mgmt_ip\t$mgmt_name\t${mgmt_name%%.*}" \
	    /etc/hosts nocomment
	
	hpc_hex=$(( $new_ip_hex + $addresses_per_segment * 2 ))
	hpc_ip=$(auto-hex-to-octet $hpc_hex)
	hpc_name=${host_name%.*}-hpc.${host_name##*.}
	# Escape periods so 192.168.1.3 doesn't match 192.168.193
	search_ip=$(echo $hpc_ip | sed -e 's|\.|\\\.|g')
	# printf "$hpc_ip\t$hpc_name\t${hpc_name%%.*}\n"
	auto-append-line "^$search_ip" "$hpc_ip\t$hpc_name\t${hpc_name%%.*}" \
	    /etc/hosts nocomment
	
	aux_hex=$(( $new_ip_hex + $addresses_per_segment * 3 ))
	aux_ip=$(auto-hex-to-octet $aux_hex)
	aux_name=${host_name%.*}-aux.${host_name##*.}
	# Escape periods so 192.168.1.3 doesn't match 192.168.193
	search_ip=$(echo $aux_ip | sed -e 's|\.|\\\.|g')
	# printf "$aux_ip\t$aux_name\t${aux_name%%.*}\n"
	auto-append-line "^$search_ip" "$aux_ip\t$aux_name\t${aux_name%%.*}" \
	    /etc/hosts nocomment
	
	vi -c '$' /etc/hosts
	
	serial_number=$(($serial_number + 1))
    done
    
    printf 'Image another server? [y] '
    read again
done
