diff options
Diffstat (limited to 'heartbeat/awseip')
-rwxr-xr-x | heartbeat/awseip | 287 |
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 |