#!/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_endpoint_default="vpc.aliyuncs.com" OCF_RESKEY_aliyuncli_default="detect" : ${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}} : ${OCF_RESKEY_endpoint=${OCF_RESKEY_endpoint_default}} : ${OCF_RESKEY_aliyuncli=${OCF_RESKEY_aliyuncli_default}} ####################################################################### # aliyun cli doesnt work without HOME parameter export HOME="/root" USAGE="usage: $0 {start|stop|status|meta-data}"; if [ "${OCF_RESKEY_aliyuncli}" = "detect" ]; then OCF_RESKEY_aliyuncli="$(which aliyuncli 2> /dev/null || which aliyun 2> /dev/null)" fi if [ "${OCF_RESKEY_aliyuncli##*/}" = 'aliyuncli' ]; then OUTPUT="text" EXECUTING='{ print $3 }' IFS_=" " ENDPOINT="" elif [ "${OCF_RESKEY_aliyuncli##*/}" = 'aliyun' ]; then OUTPUT="table cols=InstanceId,DestinationCidrBlock rows=RouteTables.RouteTable[].RouteEntrys.RouteEntry[]" EXECUTING='{ gsub (" ", "", $0); print $1 }' IFS_="|" ENDPOINT="--endpoint $OCF_RESKEY_endpoint" fi ############################################################################### ############################################################################### # # Functions # ############################################################################### request_create_route_entry() { cmd="${OCF_RESKEY_aliyuncli} vpc CreateRouteEntry --RouteTableId $OCF_RESKEY_routing_table --DestinationCidrBlock ${OCF_RESKEY_address}/32 --NextHopId $ECS_INSTANCE_ID --NextHopType Instance ${ENDPOINT}" ocf_log debug "executing command: $cmd" res=$($cmd 2>&1) rc=$? if [ $rc -eq 0 ] then ocf_log debug "result: $res; rc: $rc" else ocf_log err "result: $res; cmd: $cmd; rc: $rc" fi return $rc } request_delete_route_entry() { cmd="${OCF_RESKEY_aliyuncli} vpc DeleteRouteEntry --RouteTableId $OCF_RESKEY_routing_table --DestinationCidrBlock ${OCF_RESKEY_address}/32 --NextHopId $ROUTE_TO_INSTANCE ${ENDPOINT}" ocf_log debug "executing command: $cmd" res=$($cmd) rc=$? if [ $rc -eq 0 ] then ocf_log debug "result: $res; rc: $rc" else ocf_log err "result: $res; cmd: $cmd; rc: $rc" fi return $rc } request_describe_route_tables() { cmd="${OCF_RESKEY_aliyuncli} vpc DescribeRouteTables --RouteTableId $OCF_RESKEY_routing_table --output ${OUTPUT} ${ENDPOINT}" ocf_log debug "executing command: $cmd" res=$($cmd) rc=$? if [ $rc -eq 0 ] then ROUTE_TO_INSTANCE=$(echo "$res" |grep "\s${OCF_RESKEY_address}/" | awk -F "${IFS_}" "${EXECUTING}") ocf_log debug "ROUTE_TO_INSTANCE: $ROUTE_TO_INSTANCE" else ocf_log err "result: $res; cmd: $cmd; rc: $rc" fi } ip_get_and_configure() { ocf_log debug "function: ip_get_and_configure" request_describe_route_tables if [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; then if [ -n "$ROUTE_TO_INSTANCE" ]; then ip_drop fi request_create_route_entry rc=$? while [ $rc -ne 0 ]; do sleep 1 request_create_route_entry 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" res=$($cmd) rc=$? if [ $rc -ne 0 ] && [ $rc -ne 2 ]; then ocf_log err "command failed, rc: $rc; cmd: $cmd; result: $res" return $OCF_ERR_GENERIC fi request_delete_route_entry rc=$? if [ $rc -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() { request_describe_route_tables while [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; do sleep 3 request_describe_route_tables done } wait_for_deleted() { request_describe_route_tables while [ ! -z "$ROUTE_TO_INSTANCE" ]; do sleep 1 request_describe_route_tables done } ecs_ip_metadata() { cat < 1.0 Resource Agent to move IP addresses within a VPC of the Aliyun Webservices ECS by changing an entry in an specific routing table Move IP within a VPC of the Aliyun ECS Path to command line tools for Aliyun Path to Aliyun CLI tools VPC private IP address vpc ip Name of the routing table, where the route for the IP address should be changed, i.e. vtb-... routing table name Name of the network interface, i.e. eth0 network interface name An endpoint is the service entry of an Alibaba Cloud service, i.e. vpc.cn-beijing.aliyuncs.com service endpoint Valid Aliyun CLI profile name (see 'aliyun cli configure'). See https://www.alibabacloud.com/help/zh/product/29991.htm for more information about aliyun cli. profile name END } ecs_ip_validate() { ocf_log debug "function: validate" if [ -z "${OCF_RESKEY_aliyuncli}" ]; then ocf_exit_reason "unable to detect aliyuncli binary" exit $OCF_ERR_INSTALLED fi # 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" request_describe_route_tables 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