1#!/usr/local/bin/bash 2# 3# OCF resource agent to move an IP address within a VPC in the Aliyun 4# Based on code of Markus Guertler (GitHub AWS-VPC-move-IP) 5# Based on code of Adam Gandelman (GitHub ec2-resource-agents/elasticip) 6# 7 8####################################################################### 9# Initialization: 10: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} 11. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs 12 13# Parameter defaults 14 15OCF_RESKEY_address_default="" 16OCF_RESKEY_routing_table_default="" 17OCF_RESKEY_interface_default="eth0" 18OCF_RESKEY_profile_default="default" 19OCF_RESKEY_endpoint_default="vpc.aliyuncs.com" 20OCF_RESKEY_aliyuncli_default="detect" 21 22 23: ${OCF_RESKEY_address=${OCF_RESKEY_address_default}} 24: ${OCF_RESKEY_routing_table=${OCF_RESKEY_routing_table_default}} 25: ${OCF_RESKEY_interface=${OCF_RESKEY_interface_default}} 26: ${OCF_RESKEY_profile=${OCF_RESKEY_profile_default}} 27: ${OCF_RESKEY_endpoint=${OCF_RESKEY_endpoint_default}} 28: ${OCF_RESKEY_aliyuncli=${OCF_RESKEY_aliyuncli_default}} 29 30####################################################################### 31 32# aliyun cli doesnt work without HOME parameter 33export HOME="/root" 34 35USAGE="usage: $0 {start|stop|status|meta-data}"; 36 37if [ "${OCF_RESKEY_aliyuncli}" = "detect" ]; then 38 OCF_RESKEY_aliyuncli="$(which aliyuncli 2> /dev/null || which aliyun 2> /dev/null)" 39fi 40 41if [ "${OCF_RESKEY_aliyuncli##*/}" = 'aliyuncli' ]; then 42 OUTPUT="text" 43 EXECUTING='{ print $3 }' 44 IFS_=" " 45 ENDPOINT="" 46elif [ "${OCF_RESKEY_aliyuncli##*/}" = 'aliyun' ]; then 47 OUTPUT="table cols=InstanceId,DestinationCidrBlock rows=RouteTables.RouteTable[].RouteEntrys.RouteEntry[]" 48 EXECUTING='{ gsub (" ", "", $0); print $1 }' 49 IFS_="|" 50 ENDPOINT="--endpoint $OCF_RESKEY_endpoint" 51fi 52############################################################################### 53 54 55############################################################################### 56# 57# Functions 58# 59############################################################################### 60 61request_create_route_entry() { 62 cmd="${OCF_RESKEY_aliyuncli} vpc CreateRouteEntry --RouteTableId $OCF_RESKEY_routing_table --DestinationCidrBlock ${OCF_RESKEY_address}/32 --NextHopId $ECS_INSTANCE_ID --NextHopType Instance ${ENDPOINT}" 63 ocf_log debug "executing command: $cmd" 64 res=$($cmd 2>&1) 65 rc=$? 66 if [ $rc -eq 0 ] 67 then 68 ocf_log debug "result: $res; rc: $rc" 69 else 70 ocf_log err "result: $res; cmd: $cmd; rc: $rc" 71 fi 72 return $rc 73} 74 75request_delete_route_entry() { 76 cmd="${OCF_RESKEY_aliyuncli} vpc DeleteRouteEntry --RouteTableId $OCF_RESKEY_routing_table --DestinationCidrBlock ${OCF_RESKEY_address}/32 --NextHopId $ROUTE_TO_INSTANCE ${ENDPOINT}" 77 ocf_log debug "executing command: $cmd" 78 res=$($cmd) 79 rc=$? 80 if [ $rc -eq 0 ] 81 then 82 ocf_log debug "result: $res; rc: $rc" 83 else 84 ocf_log err "result: $res; cmd: $cmd; rc: $rc" 85 fi 86 return $rc 87} 88 89request_describe_route_tables() { 90 cmd="${OCF_RESKEY_aliyuncli} vpc DescribeRouteTables --RouteTableId $OCF_RESKEY_routing_table --output ${OUTPUT} ${ENDPOINT}" 91 ocf_log debug "executing command: $cmd" 92 res=$($cmd) 93 rc=$? 94 if [ $rc -eq 0 ] 95 then 96 ROUTE_TO_INSTANCE=$(echo "$res" |grep "\s${OCF_RESKEY_address}/" | awk -F "${IFS_}" "${EXECUTING}") 97 ocf_log debug "ROUTE_TO_INSTANCE: $ROUTE_TO_INSTANCE" 98 else 99 ocf_log err "result: $res; cmd: $cmd; rc: $rc" 100 fi 101} 102 103ip_get_and_configure() { 104 ocf_log debug "function: ip_get_and_configure" 105 106 request_describe_route_tables 107 if [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; then 108 if [ -n "$ROUTE_TO_INSTANCE" ]; then 109 ip_drop 110 fi 111 request_create_route_entry 112 rc=$? 113 while [ $rc -ne 0 ]; do 114 sleep 1 115 request_create_route_entry 116 rc=$? 117 done 118 wait_for_started 119 fi 120 121 122 # Reconfigure the local ip address 123 ip addr add "${OCF_RESKEY_address}/32" dev $OCF_RESKEY_interface 124 rc=$? 125 if [ $rc -ne 0 ]; then 126 ocf_log err "command failed, rc: $rc" 127 return $OCF_ERR_GENERIC 128 fi 129 130 ocf_log debug "IP added" 131 132 return $OCF_SUCCESS 133} 134 135ip_drop() { 136 ocf_log debug "function: ip_drop" 137 cmd="ip addr delete ${OCF_RESKEY_address}/32 dev $OCF_RESKEY_interface" 138 ocf_log debug "executing command: $cmd" 139 res=$($cmd) 140 rc=$? 141 if [ $rc -ne 0 ] && [ $rc -ne 2 ]; then 142 ocf_log err "command failed, rc: $rc; cmd: $cmd; result: $res" 143 return $OCF_ERR_GENERIC 144 fi 145 request_delete_route_entry 146 rc=$? 147 if [ $rc -ne 0 ]; then 148 ocf_log err "command failed, rc: $rc" 149 return $OCF_ERR_GENERIC 150 fi 151 wait_for_deleted 152 153 ocf_log debug "IP dropped" 154 155 return $OCF_SUCCESS 156} 157 158wait_for_started() { 159 request_describe_route_tables 160 while [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; do 161 sleep 3 162 request_describe_route_tables 163 done 164} 165 166wait_for_deleted() { 167 request_describe_route_tables 168 while [ ! -z "$ROUTE_TO_INSTANCE" ]; do 169 sleep 1 170 request_describe_route_tables 171 done 172} 173 174ecs_ip_metadata() { 175 cat <<END 176<?xml version="1.0"?> 177<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> 178<resource-agent name="aliyun-vpc-move-ip"> 179<version>2.0</version> 180<longdesc lang="en"> 181Resource Agent to move IP addresses within a VPC of the Aliyun Webservices ECS 182by changing an entry in an specific routing table 183</longdesc> 184<shortdesc lang="en">Move IP within a VPC of the Aliyun ECS</shortdesc> 185 186<parameters> 187<parameter name="aliyuncli" required="0"> 188<longdesc lang="en"> 189Path to command line tools for Aliyun 190</longdesc> 191<shortdesc lang="en">Path to Aliyun CLI tools</shortdesc> 192<content type="string" default="${OCF_RESKEY_aliyuncli_default}" /> 193</parameter> 194 195<parameter name="address" required="1"> 196<longdesc lang="en"> 197VPC private IP address 198</longdesc> 199<shortdesc lang="en">vpc ip</shortdesc> 200<content type="string" default="${OCF_RESKEY_address_default}" /> 201</parameter> 202 203<parameter name="routing_table" required="1"> 204<longdesc lang="en"> 205Name of the routing table, where the route for the IP address should be changed, i.e. vtb-... 206</longdesc> 207<shortdesc lang="en">routing table name</shortdesc> 208<content type="string" default="${OCF_RESKEY_routing_table_default}" /> 209</parameter> 210 211<parameter name="interface" required="1"> 212<longdesc lang="en"> 213Name of the network interface, i.e. eth0 214</longdesc> 215<shortdesc lang="en">network interface name</shortdesc> 216<content type="string" default="${OCF_RESKEY_interface_default}" /> 217</parameter> 218 219<parameter name="endpoint" required="0"> 220<longdesc lang="en"> 221An endpoint is the service entry of an Alibaba Cloud service, i.e. vpc.cn-beijing.aliyuncs.com 222</longdesc> 223<shortdesc lang="en">service endpoint</shortdesc> 224<content type="string" default="${OCF_RESKEY_endpoint_default}" /> 225</parameter> 226 227<parameter name="profile" required="0"> 228<longdesc lang="en"> 229Valid Aliyun CLI profile name (see 'aliyun cli configure'). 230See https://www.alibabacloud.com/help/zh/product/29991.htm for more information about aliyun cli. 231</longdesc> 232<shortdesc lang="en">profile name</shortdesc> 233<content type="string" default="${OCF_RESKEY_profile_default}" /> 234</parameter> 235</parameters> 236 237<actions> 238<action name="start" timeout="180s" /> 239<action name="stop" timeout="180s" /> 240<action name="monitor" depth="0" timeout="30s" interval="30s" /> 241<action name="validate-all" timeout="5s" /> 242<action name="meta-data" timeout="5s" /> 243</actions> 244</resource-agent> 245END 246} 247 248ecs_ip_validate() { 249 ocf_log debug "function: validate" 250 251 if [ -z "${OCF_RESKEY_aliyuncli}" ]; then 252 ocf_exit_reason "unable to detect aliyuncli binary" 253 exit $OCF_ERR_INSTALLED 254 fi 255 256 # IP address 257 if [ -z "$OCF_RESKEY_address" ]; then 258 ocf_log err "IP address parameter not set $OCF_RESKEY_ADDRESS!" 259 exit $OCF_ERR_CONFIGURED 260 fi 261 262 # Network Interface 263 if [ -z "$OCF_RESKEY_interface" ]; then 264 ocf_log err "Network interface parameter not set $OCF_RESKEY_INTERFACE!" 265 exit $OCF_ERR_CONFIGURED 266 fi 267 268 # Routing Table 269 if [ -z "$OCF_RESKEY_routing_table" ]; then 270 ocf_log err "Routing table parameter not set $OCF_RESKEY_ROUTING_TABLE!" 271 exit $OCF_ERR_CONFIGURED 272 fi 273 274 if [ -z "${ECS_INSTANCE_ID}" ]; then 275 ocf_exit_reason "Instance ID not found. Is this a ECS instance?" 276 return $OCF_ERR_GENERIC 277 fi 278 279 return $OCF_SUCCESS 280} 281 282ecs_ip_start() { 283 ocf_log info "ECS: Moving IP address $OCF_RESKEY_address to this host by adjusting routing table $OCF_RESKEY_routing_table" 284 285 ecs_ip_monitor 286 if [ $? = $OCF_SUCCESS ]; then 287 ocf_log info "ECS: $OCF_RESKEY_address already started" 288 return $OCF_SUCCESS 289 fi 290 291 ocf_log info "ECS: Adjusting routing table and locally configuring IP address" 292 ip_get_and_configure 293 rc=$? 294 if [ $rc -ne 0 ]; then 295 ocf_log err "Received $rc from 'aliyun cli'" 296 return $OCF_ERR_GENERIC 297 fi 298 299 ecs_ip_monitor 300 rc=$? 301 if [ $rc -ne $OCF_SUCCESS ]; then 302 ocf_log err "IP address couldn't be configured on this host (IP: $OCF_RESKEY_address, Interface: $OCF_RESKEY_interface)" 303 return $rc 304 fi 305 306 return $OCF_SUCCESS 307} 308 309ecs_ip_stop() { 310 ocf_log info "ECS: Bringing down IP address $OCF_RESKEY_address" 311 312 ecs_ip_monitor 313 if [ $? = $OCF_NOT_RUNNING ]; then 314 ocf_log info "ECS: Address $OCF_RESKEY_address already down" 315 return $OCF_SUCCESS 316 fi 317 318 ip_drop 319 if [ $? -ne $OCF_SUCCESS ]; then 320 ocf_log err "ECS: Couldn't drop IP address $OCF_RESKEY_address on interface $OCF_RESKEY_interface." 321 return $OCF_ERR_GENERIC 322 fi 323 324 ecs_ip_monitor 325 if [ $? = $OCF_NOT_RUNNING ]; then 326 ocf_log info "ECS: Successfully brought down $OCF_RESKEY_address" 327 return $OCF_SUCCESS 328 fi 329 330 ocf_log err "ECS: Couldn't bring down IP address $OCF_RESKEY_address on interface $OCF_RESKEY_interface." 331 return $OCF_ERR_GENERIC 332} 333 334ecs_ip_monitor() { 335 ocf_log debug "function: ecsip_monitor: check routing table" 336 request_describe_route_tables 337 338 if [ "$ECS_INSTANCE_ID" != "$ROUTE_TO_INSTANCE" ]; then 339 ocf_log debug "not routed to this instance ($ECS_INSTANCE_ID) but to instance $ROUTE_TO_INSTANCE" 340 return $OCF_NOT_RUNNING 341 fi 342 343 cmd="ping -W 1 -c 1 $OCF_RESKEY_address" 344 ocf_log debug "executing command: $cmd" 345 $cmd > /dev/null 346 if [ $? -ne 0 ]; then 347 ocf_log debug "IP $OCF_RESKEY_address not locally reachable via ping on this system" 348 return $OCF_NOT_RUNNING 349 fi 350 ocf_log debug "routed in VPC and locally reachable" 351 return $OCF_SUCCESS 352} 353 354 355############################################################################### 356# 357# MAIN 358# 359############################################################################### 360 361case $__OCF_ACTION in 362 meta-data) ecs_ip_metadata 363 exit $OCF_SUCCESS;; 364 validate-all) ecs_ip_validate;; 365esac 366 367ECS_INSTANCE_ID="$(curl -s http://100.100.100.200/latest/meta-data/instance-id)" 368 369case $__OCF_ACTION in 370 start) 371 ecs_ip_validate 372 ecs_ip_start;; 373 stop) 374 ecs_ip_stop;; 375 monitor) 376 ecs_ip_monitor;; 377 *) exit $OCF_ERR_UNIMPLEMENTED;; 378esac 379