diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am index d8c79b961..51ce7077b 100644 --- a/heartbeat/Makefile.am +++ b/heartbeat/Makefile.am @@ -1,173 +1,174 @@ # Makefile.am for OCF RAs # # Author: Sun Jing Dong # Copyright (C) 2004 IBM # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = $(ocf_SCRIPTS) $(ocfcommon_DATA) \ $(common_DATA) $(hb_DATA) $(dtd_DATA) \ README AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/linux-ha halibdir = $(libexecdir)/heartbeat ocfdir = $(OCF_RA_DIR_PREFIX)/heartbeat dtddir = $(datadir)/$(PACKAGE_NAME) dtd_DATA = ra-api-1.dtd metadata.rng if USE_IPV6ADDR_AGENT ocf_PROGRAMS = IPv6addr else ocf_PROGRAMS = endif if IPV6ADDR_COMPATIBLE halib_PROGRAMS = send_ua else halib_PROGRAMS = endif IPv6addr_SOURCES = IPv6addr.c IPv6addr_utils.c send_ua_SOURCES = send_ua.c IPv6addr_utils.c IPv6addr_LDADD = -lplumb $(LIBNETLIBS) send_ua_LDADD = $(LIBNETLIBS) ocf_SCRIPTS = AoEtarget \ AudibleAlarm \ ClusterMon \ CTDB \ Delay \ Dummy \ EvmsSCC \ Evmsd \ Filesystem \ ICP \ IPaddr \ IPaddr2 \ IPsrcaddr \ LVM \ LinuxSCSI \ MailTo \ ManageRAID \ ManageVE \ NodeUtilization \ Pure-FTPd \ Raid1 \ Route \ SAPDatabase \ SAPInstance \ SendArp \ ServeRAID \ SphinxSearchDaemon \ Squid \ Stateful \ SysInfo \ VIPArip \ VirtualDomain \ WAS \ WAS6 \ WinPopup \ Xen \ Xinetd \ ZFS \ anything \ apache \ asterisk \ + aws-vpc-route53 \ awseip \ awsvip \ clvm \ conntrackd \ db2 \ dhcpd \ dnsupdate \ docker \ eDir88 \ ethmonitor \ exportfs \ fio \ galera \ garbd \ iSCSILogicalUnit \ iSCSITarget \ ids \ iface-bridge \ iface-vlan \ iscsi \ jboss \ kamailio \ lxc \ mysql \ mysql-proxy \ nagios \ named \ nfsnotify \ nfsserver \ nginx \ oraasm \ oracle \ oralsnr \ pgagent \ pgsql \ pingd \ portblock \ postfix \ pound \ proftpd \ rabbitmq-cluster \ redis \ rsyncd \ rsyslog \ scsi2reservation \ sfex \ sg_persist \ slapd \ symlink \ syslog-ng \ tomcat \ varnish \ vmware \ vsftpd \ zabbixserver ocfcommondir = $(OCF_LIB_DIR_PREFIX)/heartbeat ocfcommon_DATA = ocf-shellfuncs \ ocf-binaries \ ocf-directories \ ocf-returncodes \ ocf-rarun \ ocf-distro \ apache-conf.sh \ http-mon.sh \ sapdb-nosha.sh \ sapdb.sh \ ora-common.sh \ mysql-common.sh \ nfsserver-redhat.sh \ findif.sh # Legacy locations hbdir = $(sysconfdir)/ha.d hb_DATA = shellfuncs check: $(ocf_SCRIPTS:=.check) %.check: % OCF_ROOT=$(abs_srcdir) OCF_FUNCTIONS_DIR=$(abs_srcdir) ./$< meta-data | xmllint --path $(abs_srcdir) --noout --relaxng $(abs_srcdir)/metadata.rng - diff --git a/heartbeat/aws-vpc-route53 b/heartbeat/aws-vpc-route53 new file mode 100755 index 000000000..d9b6d84b6 --- /dev/null +++ b/heartbeat/aws-vpc-route53 @@ -0,0 +1,330 @@ +#!/bin/bash +# +# Copyright 2017 Amazon.com, Inc. and its affiliates. All Rights Reserved. +# Licensed under the MIT License. +# +# Copyright 2017 Amazon.com, Inc. and its affiliates + +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +# +# +# +# OCF resource agent to move an IP address within a VPC in the AWS +# Written by Stefan Schneider , Martin Tegmeier (AWS) +# Based on code of Markus Guertler# +# +# +# OCF resource agent to move an IP address within a VPC in the AWS +# Written by Stefan Schneider (AWS) , Martin Tegmeier (AWS) +# Based on code of Markus Guertler (SUSE) +# +# Mar. 15, 2017, vers 1.0.2 + +####################################################################### +# Initialization: + +: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} +. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + +OCF_RESKEY_ttl_default=10 + +: ${OCF_RESKEY_ttl:=${OCF_RESKEY_ttl_default}} + +####################################################################### + +usage() { + cat <<-EOT + usage: $0 {start|stop|status|monitor|validate-all|meta-data} + EOT +} + +metadata() { +cat < + + +1.0 + +Update Route53 record of Amazon Webservices EC2 by updating an entry in a +hosted zone ID table. + +AWS instances will require policies which allow them to update Route53 ARecords: +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Stmt1471878724000", + "Effect": "Allow", + "Action": [ + "route53:ChangeResourceRecordSets", + "route53:GetChange", + "route53:ListResourceRecordSets", + ], + "Resource": [ + "*" + ] + } + ] +} + +Example Cluster Configuration: + +Use a configuration in "crm configure edit" which looks as follows. Replace +hostedzoneid, fullname and profile with the appropriate values: + +primitive res_route53 ocf:heartbeat:aws-vpc-route53 \ + params hostedzoneid=EX4MPL3EX4MPL3 fullname=service.cloud.example.corp. profile=cluster \ + op start interval=0 timeout=180 \ + op stop interval=0 timeout=180 \ + op monitor interval=300 timeout=180 \ + meta target-role=Started + +Update Route53 VPC record for AWS EC2 + + + +Hosted zone ID of Route 53. This is the table of +the Route 53 record. + +AWS hosted zone ID + + + + +The full name of the service which will host the IP address. +Example: service.cloud.example.corp. +Note: The trailing dot is important to Route53! + +Full service name + + + + +Time to live for Route53 ARECORD + +ARECORD TTL + + + + +The name of the AWS CLI profile of the root account. This +profile will have to use the "text" format for CLI output. +The file /root/.aws/config should have an entry which looks +like: + + [profile cluster] + region = us-east-1 + output = text + +"cluster" is the name which has to be used in the cluster +configuration. The region has to be the current one. The +output has to be "text". + +AWS Profile Name + + + + + + + + + + + +END +} + +ec2ip_validate() { + ocf_log debug "function: validate" + + # Full name + [[ -z "$OCF_RESKEY_fullname" ]] && ocf_log error "Full name parameter not set $OCF_RESKEY_fullname!" && exit $OCF_ERR_CONFIGURED + + # Hosted Zone ID + [[ -z "$OCF_RESKEY_hostedzoneid" ]] && ocf_log error "Hosted Zone ID parameter not set $OCF_RESKEY_hostedzoneid!" && exit $OCF_ERR_CONFIGURED + + # profile + [[ -z "$OCF_RESKEY_profile" ]] && ocf_log error "AWS CLI profile not set $OCF_RESKEY_profile!" && exit $OCF_ERR_CONFIGURED + + # TTL + [[ -z "$OCF_RESKEY_ttl" ]] && ocf_log error "TTL not set $OCF_RESKEY_ttl!" && exit $OCF_ERR_CONFIGURED + + ocf_log debug "Testing aws command" + aws --version 2>&1 + if [ "$?" -gt 0 ]; then + ocf_log error "Error while executing aws command as user root! Please check if AWS CLI tools (Python flavor) are properly installed and configured." && exit $OCF_ERR_INSTALLED + fi + ocf_log debug "ok" + + if [ -n "$OCF_RESKEY_profile" ]; then + AWS_PROFILE_OPT="--profile $OCF_RESKEY_profile" + else + AWS_PROFILE_OPT="--profile default" + fi + + return $OCF_SUCCESS +} + +ec2ip_monitor() { + ec2ip_validate + ocf_log debug "function: ec2ip_monitor: check Route53 record " + IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')" + ARECORD="$(aws $AWS_PROFILE_OPT route53 list-resource-record-sets --hosted-zone-id $OCF_RESKEY_hostedzoneid --query "ResourceRecordSets[?Name=='$OCF_RESKEY_fullname']" | grep RESOURCERECORDS | /usr/bin/awk '{ print $2 }' )" + ocf_log debug "function: ec2ip_monitor: found IP address: $ARECORD ." + if [ "${ARECORD}" == "${IPADDRESS}" ]; then + ocf_log debug "function: ec2ip_monitor: ARECORD $ARECORD found" + return $OCF_SUCCESS + else + ocf_log debug "function: ec2ip_monitor: no ARECORD found" + return $OCF_NOT_RUNNING + fi + + return $OCF_SUCCESS +} + +ec2ip_stop() { + ocf_log info "EC2: Bringing down Route53 agent. (Will remove ARECORD)" + IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')" + ARECORD="$(aws $AWS_PROFILE_OPT route53 list-resource-record-sets --hosted-zone-id $OCF_RESKEY_hostedzoneid --query "ResourceRecordSets[?Name=='$OCF_RESKEY_fullname']" | grep RESOURCERECORDS | /usr/bin/awk '{ print $2 }' )" + ocf_log debug "function: ec2ip_monitor: found IP address: $ARECORD ." + if [ ${ARECORD} != ${IPADDRESS} ]; then + ocf_log debug "function: ec2ip_monitor: no ARECORD found" + return $OCF_SUCCESS + else + ocf_log debug "function: ec2ip_monitor: ARECORD $ARECORD found" + # determine IP address + IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')" + # Patch file + ocf_log debug "function ec2ip_stop: will delete IP address to ${IPADDRESS}" + ocf_log info "EC2: Updating Route53 $OCF_RESKEY_hostedzoneid with $IPADDRESS for $OCF_RESKEY_fullname" + ROUTE53RECORD="/var/tmp/route53-${OCF_RESKEY_hostedzoneid}-${IPADDRESS}.json" + echo "{ " > ${ROUTE53RECORD} + echo " \"Comment\": \"Update record to reflect new IP address for a system \", " >> ${ROUTE53RECORD} + echo " \"Changes\": [ " >> ${ROUTE53RECORD} + echo " { " >> ${ROUTE53RECORD} + echo " \"Action\": \"DELETE\", " >> ${ROUTE53RECORD} + echo " \"ResourceRecordSet\": { " >> ${ROUTE53RECORD} + echo " \"Name\": \"${OCF_RESKEY_fullname}\", " >> ${ROUTE53RECORD} + echo " \"Type\": \"A\", " >> ${ROUTE53RECORD} + echo " \"TTL\": ${OCF_RESKEY_ttl}, " >> ${ROUTE53RECORD} + echo " \"ResourceRecords\": [ " >> ${ROUTE53RECORD} + echo " { " >> ${ROUTE53RECORD} + echo " \"Value\": \"${IPADDRESS}\" " >> ${ROUTE53RECORD} + echo " } " >> ${ROUTE53RECORD} + echo " ] " >> ${ROUTE53RECORD} + echo " } " >> ${ROUTE53RECORD} + echo " } " >> ${ROUTE53RECORD} + echo " ] " >> ${ROUTE53RECORD} + echo "}" >> ${ROUTE53RECORD} + cmd="aws --profile ${OCF_RESKEY_profile} route53 change-resource-record-sets --hosted-zone-id ${OCF_RESKEY_hostedzoneid} \ + --change-batch file://${ROUTE53RECORD} " + ocf_log debug "function ec2ip_start: executing command: $cmd" + CHANGEID=$($cmd | grep CHANGEINFO | /usr/bin/awk -F'\t' '{ print $3 }' ) + ocf_log debug "Change id: ${CHANGEID}" + rm ${ROUTE53RECORD} + CHANGEID=$(echo $CHANGEID |cut -d'/' -f 3 |cut -d'"' -f 1 ) + ocf_log debug "Change id: ${CHANGEID}" + STATUS="PENDING" + MYSECONDS=2 + while [ "$STATUS" = 'PENDING' ]; do + sleep ${MYSECONDS} + STATUS="$(aws --profile ${OCF_RESKEY_profile} route53 get-change --id $CHANGEID | grep CHANGEINFO | /usr/bin/awk -F'\t' '{ print $4 }' |cut -d'"' -f 2 )" + ocf_log debug "Waited for ${MYSECONDS} seconds and checked execution of Route 53 update status: ${STATUS} " + done + + return $OCF_SUCCESS + fi + + return $OCF_SUCCESS +} + +ec2ip_start() { + # determine IP address + IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')" + # Patch file + ocf_log debug "function ec2ip_start: will update IP address to ${IPADDRESS}" + ocf_log info "EC2: Updating Route53 $OCF_RESKEY_hostedzoneid with $IPADDRESS for $OCF_RESKEY_fullname" + ROUTE53RECORD="/var/tmp/route53-${OCF_RESKEY_hostedzoneid}-${IPADDRESS}.json" + echo "{ " > ${ROUTE53RECORD} + echo " \"Comment\": \"Update record to reflect new IP address for a system \", " >> ${ROUTE53RECORD} + echo " \"Changes\": [ " >> ${ROUTE53RECORD} + echo " { " >> ${ROUTE53RECORD} + echo " \"Action\": \"UPSERT\", " >> ${ROUTE53RECORD} + echo " \"ResourceRecordSet\": { " >> ${ROUTE53RECORD} + echo " \"Name\": \"${OCF_RESKEY_fullname}\", " >> ${ROUTE53RECORD} + echo " \"Type\": \"A\", " >> ${ROUTE53RECORD} + echo " \"TTL\": ${OCF_RESKEY_ttl} , " >> ${ROUTE53RECORD} + echo " \"ResourceRecords\": [ " >> ${ROUTE53RECORD} + echo " { " >> ${ROUTE53RECORD} + echo " \"Value\": \"${IPADDRESS}\" " >> ${ROUTE53RECORD} + echo " } " >> ${ROUTE53RECORD} + echo " ] " >> ${ROUTE53RECORD} + echo " } " >> ${ROUTE53RECORD} + echo " } " >> ${ROUTE53RECORD} + echo " ] " >> ${ROUTE53RECORD} + echo "}" >> ${ROUTE53RECORD} + cmd="aws --profile ${OCF_RESKEY_profile} route53 change-resource-record-sets --hosted-zone-id ${OCF_RESKEY_hostedzoneid} \ + --change-batch file://${ROUTE53RECORD} " + ocf_log debug "function ec2ip_start: executing command: $cmd" + CHANGEID=$($cmd | grep CHANGEINFO | /usr/bin/awk -F'\t' '{ print $3 }' ) + ocf_log debug "Change id: ${CHANGEID}" + rm ${ROUTE53RECORD} + CHANGEID=$(echo $CHANGEID |cut -d'/' -f 3 |cut -d'"' -f 1 ) + ocf_log debug "Change id: ${CHANGEID}" + STATUS="PENDING" + MYSECONDS=2 + while [ "$STATUS" = 'PENDING' ]; do + sleep ${MYSECONDS} + STATUS="$(aws --profile ${OCF_RESKEY_profile} route53 get-change --id $CHANGEID | grep CHANGEINFO | /usr/bin/awk -F'\t' '{ print $4 }' |cut -d'"' -f 2 )" + ocf_log debug "Waited for ${MYSECONDS} seconds and checked execution of Route 53 update status: ${STATUS} " + done + + return $OCF_SUCCESS +} + +############################################################################### + +case $__OCF_ACTION in + usage|help) + usage + exit $OCF_SUCCESS + ;; + meta-data) + metadata + exit $OCF_SUCCESS + ;; + monitor) + ec2ip_monitor + ;; + stop) + ec2ip_stop + ;; + validate-all) + ec2ip_validate + ;; + start) + ec2ip_start + ;; + *) + usage + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac