#!/bin/sh
#
# OCF resource agent to move an IP address within a VPC in the Aliyun
# Based on code of Markus Guertler (GitHub AWS-VPC-move-IP)
# Based on code of Adam Gandelman (GitHub ec2-resource-agents/elasticip)
#

#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs

# Parameter defaults

OCF_RESKEY_address_default=""
OCF_RESKEY_routing_table_default=""
OCF_RESKEY_interface_default="eth0"
OCF_RESKEY_profile_default="default"

: ${OCF_RESKEY_address=${OCF_RESKEY_address_default}}
: ${OCF_RESKEY_routing_table=${OCF_RESKEY_routing_table_default}}
: ${OCF_RESKEY_interface=${OCF_RESKEY_interface_default}}
: ${OCF_RESKEY_profile=${OCF_RESKEY_profile_default}}

#######################################################################

# aliyuncli doesnt work without HOME parameter
export HOME="/root"

USAGE="usage: $0 {start|stop|status|meta-data}";
###############################################################################


###############################################################################
#
# Functions
#
###############################################################################



ip_get_and_configure() {
	ocf_log debug "function: ip_get_and_configure"

	ROUTE_TO_INSTANCE="$($cmd |grep $OCF_RESKEY_address | awk '{ print $3 }')"

	if [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; then
		if [ -n "$ROUTE_TO_INSTANCE" ]; then
			ip_drop
		fi

		cmd="aliyuncli vpc CreateRouteEntry --RouteTableId $OCF_RESKEY_routing_table --DestinationCidrBlock ${OCF_RESKEY_address}/32 --NextHopId $ECS_INSTANCE_ID --NextHopType Instance --output text"
		ocf_log debug "executing command: $cmd"
		$cmd
		rc=$?
		while [ $rc -ne 0 ]; do
			sleep 1
			cmd="aliyuncli vpc CreateRouteEntry --RouteTableId $OCF_RESKEY_routing_table --DestinationCidrBlock ${OCF_RESKEY_address}/32 --NextHopId $ECS_INSTANCE_ID --NextHopType Instance --output text"
			ocf_log debug "executing command: $cmd"
			$cmd
			rc=$?
		done
		wait_for_started
	fi


	# Reconfigure the local ip address
	ip addr add "${OCF_RESKEY_address}/32" dev $OCF_RESKEY_interface
	rc=$?
	if [ $rc -ne 0 ]; then
		ocf_log err "command failed, rc: $rc"
		return $OCF_ERR_GENERIC
	fi

	ocf_log debug "IP added"

	return $OCF_SUCCESS
}

ip_drop() {
	ocf_log debug "function: ip_drop"
	cmd="ip addr delete ${OCF_RESKEY_address}/32 dev $OCF_RESKEY_interface"
	ocf_log debug "executing command: $cmd"
	$cmd
	rc=$?
	if [ $rc -ne 0 ] && [ $rc -ne 2 ]; then
		ocf_log err "command failed, rc $rc"
		return $OCF_ERR_GENERIC
	fi

	cmd="aliyuncli vpc DeleteRouteEntry --RouteTableId $OCF_RESKEY_routing_table --DestinationCidrBlock ${OCF_RESKEY_address}/32 --NextHopId $ROUTE_TO_INSTANCE --output text"
	ocf_log debug "executing command: $cmd"
	$cmd
	if [ $? -ne 0 ]; then
		ocf_log err "command failed, rc: $rc"
		return $OCF_ERR_GENERIC
	fi
	wait_for_deleted

	ocf_log debug "IP dropped"

	return $OCF_SUCCESS
}

wait_for_started() {
	cmd="aliyuncli vpc DescribeRouteTables --RouteTableId $OCF_RESKEY_routing_table --output text"
	ocf_log debug "executing command: $cmd"
	ROUTE_TO_INSTANCE="$($cmd | grep $OCF_RESKEY_address | awk '{ print $3 }')"

	while [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; do
		sleep 3
		cmd="aliyuncli vpc DescribeRouteTables --RouteTableId $OCF_RESKEY_routing_table --output text"
		ocf_log debug "executing command: $cmd"
		ROUTE_TO_INSTANCE="$($cmd | grep $OCF_RESKEY_address | awk '{ print $3 }')"
	done
}

wait_for_deleted() {
	ROUTE_TO_INSTANCE="$($cmd |grep $OCF_RESKEY_address | awk '{ print $3 }')"

	 while [ ! -z "$ROUTE_TO_INSTANCE" ]; do
		sleep 1
		cmd="aliyuncli vpc DescribeRouteTables --RouteTableId $OCF_RESKEY_routing_table --output text"
		ocf_log debug "executing command: $cmd"
		ROUTE_TO_INSTANCE="$($cmd |grep $OCF_RESKEY_address | awk '{ print $3 }')"
	 done
}

ecs_ip_metadata() {
	cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="aliyun-vpc-move-ip">
<version>2.0</version>
<longdesc lang="en">
Resource Agent to move IP addresses within a VPC of the Aliyun Webservices ECS
by changing an entry in an specific routing table
</longdesc>
<shortdesc lang="en">Move IP within a VPC of the Aliyun ECS</shortdesc>
<parameters>
<parameter name="address" required="1">
<longdesc lang="en">
VPC private IP address
</longdesc>
<shortdesc lang="en">vpc ip</shortdesc>
<content type="string" default="${OCF_RESKEY_address_default}" />
</parameter>
<parameter name="routing_table" required="1">
<longdesc lang="en">
Name of the routing table, where the route for the IP address should be changed, i.e. vtb-...
</longdesc>
<shortdesc lang="en">routing table name</shortdesc>
<content type="string" default="${OCF_RESKEY_routing_table_default}" />
</parameter>
<parameter name="interface" required="1">
<longdesc lang="en">
Name of the network interface, i.e. eth0
</longdesc>
<shortdesc lang="en">network interface name</shortdesc>
<content type="string" default="${OCF_RESKEY_interface_default}" />
</parameter>
<parameter name="profile" required="0">
<longdesc lang="en">
Valid Aliyun CLI profile name (see 'aliyuncli configure').

See https://www.alibabacloud.com/help/doc-detail/43039.htm?spm=a2c63.p38356.b99.16.38a914abRZtOU3 for more information about aliyuncli.
</longdesc>
<shortdesc lang="en">profile name</shortdesc>
<content type="string" default="${OCF_RESKEY_profile_default}" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="180s" />
<action name="stop" timeout="180s" />
<action name="monitor" depth="0" timeout="30s" interval="30s" />
<action name="validate-all" timeout="5s" />
<action name="meta-data" timeout="5s" />
</actions>
</resource-agent>
END
}

ecs_ip_validate() {
	ocf_log debug "function: validate"

	# IP address
	if [ -z "$OCF_RESKEY_address" ]; then
		ocf_log err "IP address parameter not set $OCF_RESKEY_ADDRESS!"
		exit $OCF_ERR_CONFIGURED
	fi

	# Network Interface
	if [ -z "$OCF_RESKEY_interface" ]; then
		ocf_log err "Network interface parameter not set $OCF_RESKEY_INTERFACE!"
		exit $OCF_ERR_CONFIGURED
	fi

	# Routing Table
	if [ -z "$OCF_RESKEY_routing_table" ]; then
		ocf_log err "Routing table parameter not set $OCF_RESKEY_ROUTING_TABLE!"
		exit $OCF_ERR_CONFIGURED
	fi

	if [ -z "${ECS_INSTANCE_ID}" ]; then
		ocf_exit_reason "Instance ID not found. Is this a ECS instance?"
		return $OCF_ERR_GENERIC
	fi

	return $OCF_SUCCESS
}

ecs_ip_start() {
	ocf_log info "ECS: Moving IP address $OCF_RESKEY_address to this host by adjusting routing table $OCF_RESKEY_routing_table"

	ecs_ip_monitor
	if [ $? = $OCF_SUCCESS ]; then
		ocf_log info "ECS: $OCF_RESKEY_address already started"
		return $OCF_SUCCESS
	fi

	ocf_log info "ECS: Adjusting routing table and locally configuring IP address"
	ip_get_and_configure
	rc=$?
	if [ $rc -ne 0 ]; then
		ocf_log err "Received $rc from 'aliyun cli'"
		return $OCF_ERR_GENERIC
	fi

	ecs_ip_monitor
	rc=$?
	if [ $rc -ne $OCF_SUCCESS ]; then
		ocf_log err "IP address couldn't be configured on this host (IP: $OCF_RESKEY_address, Interface: $OCF_RESKEY_interface)"
		return $rc
	fi

	return $OCF_SUCCESS
}

ecs_ip_stop() {
	ocf_log info "ECS: Bringing down IP address $OCF_RESKEY_address"

	ecs_ip_monitor
	if [ $? = $OCF_NOT_RUNNING ]; then
		ocf_log info "ECS: Address $OCF_RESKEY_address already down"
		return $OCF_SUCCESS
	fi

	ip_drop
	if [ $? -ne $OCF_SUCCESS ]; then
		ocf_log err "ECS: Couldn't drop IP address $OCF_RESKEY_address on interface $OCF_RESKEY_interface."
		return $OCF_ERR_GENERIC
	fi

	ecs_ip_monitor
	if [ $? = $OCF_NOT_RUNNING ]; then
		ocf_log info "ECS: Successfully brought down $OCF_RESKEY_address"
		return $OCF_SUCCESS
	fi

	ocf_log err "ECS: Couldn't bring down IP address $OCF_RESKEY_address on interface $OCF_RESKEY_interface."
	return $OCF_ERR_GENERIC
}

ecs_ip_monitor() {
	ocf_log debug "function: ecsip_monitor: check routing table"
	cmd="aliyuncli vpc DescribeRouteTables --RouteTableId $OCF_RESKEY_routing_table --output text"
	ocf_log debug "executing command: $cmd"

	ROUTE_TO_INSTANCE="$($cmd |grep $OCF_RESKEY_address | awk '{ print $3 }')"

	if [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; then
		ocf_log debug "not routed to this instance ($ECS_INSTANCE_ID) but to instance $ROUTE_TO_INSTANCE"
		return $OCF_NOT_RUNNING
	fi

	cmd="ping -W 1 -c 1 $OCF_RESKEY_address"
	ocf_log debug "executing command: $cmd"
	$cmd > /dev/null
	if [ $? -ne 0 ]; then
		ocf_log debug "IP $OCF_RESKEY_address not locally reachable via ping on this system"
		return $OCF_NOT_RUNNING
	fi
	ocf_log debug "routed in VPC and locally reachable"
	return $OCF_SUCCESS
}


###############################################################################
#
# MAIN
#
###############################################################################

case $__OCF_ACTION in
	meta-data) ecs_ip_metadata
		   exit $OCF_SUCCESS;;
	validate-all) ecs_ip_validate;;
esac

ECS_INSTANCE_ID="$(curl -s http://100.100.100.200/latest/meta-data/instance-id)"

case $__OCF_ACTION in
	start)
		ecs_ip_validate
		ecs_ip_start;;
	stop)
		ecs_ip_stop;;
	monitor)
		ecs_ip_monitor;;
	*)	exit $OCF_ERR_UNIMPLEMENTED;;
esac
