summaryrefslogtreecommitdiffstats
path: root/heartbeat/awseip
diff options
context:
space:
mode:
Diffstat (limited to 'heartbeat/awseip')
-rwxr-xr-xheartbeat/awseip287
1 files changed, 287 insertions, 0 deletions
diff --git a/heartbeat/awseip b/heartbeat/awseip
new file mode 100755
index 0000000..dc48460
--- /dev/null
+++ b/heartbeat/awseip
@@ -0,0 +1,287 @@
+#!/bin/sh
+#
+#
+# Manage Elastic IP with Pacemaker
+#
+#
+# Copyright 2016-2018 guessi <guessi@gmail.com>
+#
+# 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.
+#
+#
+
+#
+# Prerequisites:
+#
+# - preconfigured AWS CLI running environment (AccessKey, SecretAccessKey, etc.)
+# - a reserved secondary private IP address for EC2 instances high availability
+# - IAM user role with the following permissions:
+# * DescribeInstances
+# * AssociateAddress
+# * DescribeAddresses
+# * DisassociateAddress
+#
+
+#######################################################################
+# Initialization:
+
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+
+#######################################################################
+
+#
+# Defaults
+#
+OCF_RESKEY_awscli_default="/usr/bin/aws"
+OCF_RESKEY_profile_default="default"
+OCF_RESKEY_api_delay_default="3"
+
+: ${OCF_RESKEY_awscli=${OCF_RESKEY_awscli_default}}
+: ${OCF_RESKEY_profile=${OCF_RESKEY_profile_default}}
+: ${OCF_RESKEY_api_delay=${OCF_RESKEY_api_delay_default}}
+
+meta_data() {
+ cat <<END
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="awseip" version="1.0">
+<version>1.0</version>
+
+<longdesc lang="en">
+Resource Agent for Amazon AWS Elastic IP Addresses.
+
+It manages AWS Elastic IP Addresses with awscli.
+
+Credentials needs to be setup by running "aws configure".
+
+See https://aws.amazon.com/cli/ for more information about awscli.
+</longdesc>
+<shortdesc lang="en">Amazon AWS Elastic IP Address Resource Agent</shortdesc>
+
+<parameters>
+
+<parameter name="awscli" unique="0">
+<longdesc lang="en">
+command line tools for aws services
+</longdesc>
+<shortdesc lang="en">aws cli tools</shortdesc>
+<content type="string" default="${OCF_RESKEY_awscli_default}" />
+</parameter>
+
+<parameter name="profile">
+<longdesc lang="en">
+Valid AWS CLI profile name (see ~/.aws/config and 'aws configure')
+</longdesc>
+<shortdesc lang="en">profile name</shortdesc>
+<content type="string" default="${OCF_RESKEY_profile_default}" />
+</parameter>
+
+<parameter name="elastic_ip" unique="1" required="1">
+<longdesc lang="en">
+reserved elastic ip for ec2 instance
+</longdesc>
+<shortdesc lang="en">reserved elastic ip for ec2 instance</shortdesc>
+<content type="string" default="" />
+</parameter>
+
+<parameter name="allocation_id" unique="1" required="1">
+<longdesc lang="en">
+reserved allocation id for ec2 instance
+</longdesc>
+<shortdesc lang="en">reserved allocation id for ec2 instance</shortdesc>
+<content type="string" default="" />
+</parameter>
+
+<parameter name="private_ip_address" unique="1" required="0">
+<longdesc lang="en">
+predefined private ip address for ec2 instance
+</longdesc>
+<shortdesc lang="en">predefined private ip address for ec2 instance</shortdesc>
+<content type="string" default="" />
+</parameter>
+
+<parameter name="api_delay" unique="0">
+<longdesc lang="en">
+a short delay between API calls, to avoid sending API too quick
+</longdesc>
+<shortdesc lang="en">a short delay between API calls</shortdesc>
+<content type="integer" default="${OCF_RESKEY_api_delay_default}" />
+</parameter>
+
+</parameters>
+
+<actions>
+<action name="start" timeout="30s" />
+<action name="stop" timeout="30s" />
+<action name="monitor" timeout="30s" interval="20s" depth="0" />
+<action name="migrate_to" timeout="30s" />
+<action name="migrate_from" timeout="30s" />
+<action name="meta-data" timeout="5s" />
+<action name="validate" timeout="10s" />
+<action name="validate-all" timeout="10s" />
+</actions>
+</resource-agent>
+END
+}
+
+#######################################################################
+
+awseip_usage() {
+ cat <<END
+usage: $0 {start|stop|monitor|migrate_to|migrate_from|validate|validate-all|meta-data}
+
+Expects to have a fully populated OCF RA-compliant environment set.
+END
+}
+
+awseip_start() {
+ awseip_monitor && return $OCF_SUCCESS
+
+ if [ -n "${PRIVATE_IP_ADDRESS}" ]; then
+ NETWORK_INTERFACES_MACS=$(curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/ -H "X-aws-ec2-metadata-token: $TOKEN")
+ for MAC in ${NETWORK_INTERFACES_MACS}; do
+ curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC}/local-ipv4s -H "X-aws-ec2-metadata-token: $TOKEN" |
+ grep -q "^${PRIVATE_IP_ADDRESS}$"
+ if [ $? -eq 0 ]; then
+ NETWORK_ID=$(curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC}/interface-id -H "X-aws-ec2-metadata-token: $TOKEN")
+ fi
+ done
+ $AWSCLI --profile $OCF_RESKEY_profile ec2 associate-address \
+ --network-interface-id ${NETWORK_ID} \
+ --allocation-id ${ALLOCATION_ID} \
+ --private-ip-address ${PRIVATE_IP_ADDRESS}
+ RET=$?
+ else
+ $AWSCLI --profile $OCF_RESKEY_profile ec2 associate-address \
+ --instance-id ${INSTANCE_ID} \
+ --allocation-id ${ALLOCATION_ID}
+ RET=$?
+ fi
+
+ # delay to avoid sending request too fast
+ sleep ${OCF_RESKEY_api_delay}
+
+ if [ $RET -ne 0 ]; then
+ return $OCF_NOT_RUNNING
+ fi
+
+ ocf_log info "elastic_ip has been successfully brought up (${ELASTIC_IP})"
+ return $OCF_SUCCESS
+}
+
+awseip_stop() {
+ awseip_monitor || return $OCF_SUCCESS
+
+ ASSOCIATION_ID=$($AWSCLI --profile $OCF_RESKEY_profile --output json ec2 describe-addresses \
+ --allocation-id ${ALLOCATION_ID} | grep -m 1 "AssociationId" | awk -F'"' '{print$4}')
+
+ if [ -z "${ASSOCIATION_ID}" ]; then
+ ocf_log info "ASSOCIATION_ID not found while stopping AWS Elastic IP"
+ return $OCF_NOT_RUNNING
+ fi
+
+ $AWSCLI --profile ${OCF_RESKEY_profile} \
+ ec2 disassociate-address \
+ --association-id ${ASSOCIATION_ID}
+ RET=$?
+
+ # delay to avoid sending request too fast
+ sleep ${OCF_RESKEY_api_delay}
+
+ if [ $RET -ne 0 ]; then
+ return $OCF_NOT_RUNNING
+ fi
+
+ ocf_log info "elastic_ip has been successfully brought down (${ELASTIC_IP})"
+ return $OCF_SUCCESS
+}
+
+awseip_monitor() {
+ $AWSCLI --profile $OCF_RESKEY_profile ec2 describe-instances --instance-id "${INSTANCE_ID}" | grep -q "${ELASTIC_IP}"
+ RET=$?
+
+ if [ $RET -ne 0 ]; then
+ return $OCF_NOT_RUNNING
+ fi
+ return $OCF_SUCCESS
+}
+
+awseip_validate() {
+ check_binary ${AWSCLI}
+
+ if [ -z "$OCF_RESKEY_profile" ]; then
+ ocf_exit_reason "profile parameter not set"
+ return $OCF_ERR_CONFIGURED
+ fi
+
+ if [ -z "${INSTANCE_ID}" ]; then
+ ocf_exit_reason "instance_id not found. Is this a EC2 instance?"
+ return $OCF_ERR_GENERIC
+ fi
+
+ return $OCF_SUCCESS
+}
+
+case $__OCF_ACTION in
+ meta-data)
+ meta_data
+ exit $OCF_SUCCESS
+ ;;
+esac
+
+AWSCLI="${OCF_RESKEY_awscli}"
+ELASTIC_IP="${OCF_RESKEY_elastic_ip}"
+ALLOCATION_ID="${OCF_RESKEY_allocation_id}"
+PRIVATE_IP_ADDRESS="${OCF_RESKEY_private_ip_address}"
+TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
+INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id -H "X-aws-ec2-metadata-token: $TOKEN")
+
+case $__OCF_ACTION in
+ start)
+ awseip_validate
+ awseip_start
+ ;;
+ stop)
+ awseip_stop
+ ;;
+ monitor)
+ awseip_monitor
+ ;;
+ migrate_to)
+ ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrate_target}."
+ awseip_stop
+ ;;
+ migrate_from)
+ ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} from ${OCF_RESKEY_CRM_meta_migrate_source}."
+ awseip_start
+ ;;
+ reload)
+ ocf_log info "Reloading ${OCF_RESOURCE_INSTANCE} ..."
+ ;;
+ validate|validate-all)
+ awseip_validate
+ ;;
+ usage|help)
+ awseip_usage
+ exit $OCF_SUCCESS
+ ;;
+ *)
+ awseip_usage
+ exit $OCF_ERR_UNIMPLEMENTED
+ ;;
+esac
+
+rc=$?
+ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
+exit $rc