Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am
index 409847970..655740f14 100644
--- a/heartbeat/Makefile.am
+++ b/heartbeat/Makefile.am
@@ -1,254 +1,255 @@
# 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 README.galera
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
ocf_PROGRAMS =
if USE_IPV6ADDR_AGENT
ocf_PROGRAMS += IPv6addr
endif
halib_PROGRAMS =
if IPV6ADDR_COMPATIBLE
halib_PROGRAMS += send_ua
endif
IPv6addr_SOURCES = IPv6addr.c IPv6addr_utils.c
IPv6addr_LDADD = -lplumb $(LIBNETLIBS)
send_ua_SOURCES = send_ua.c IPv6addr_utils.c
send_ua_LDADD = $(LIBNETLIBS)
ocf_SCRIPTS = AoEtarget \
AudibleAlarm \
ClusterMon \
CTDB \
Delay \
Dummy \
EvmsSCC \
Evmsd \
Filesystem \
ICP \
IPaddr \
IPaddr2 \
IPsrcaddr \
LVM \
LinuxSCSI \
lvmlockd \
LVM-activate \
MailTo \
ManageRAID \
ManageVE \
NodeUtilization \
Pure-FTPd \
Raid1 \
Route \
SAPDatabase \
SAPInstance \
SendArp \
ServeRAID \
SphinxSearchDaemon \
Squid \
Stateful \
SysInfo \
VIPArip \
VirtualDomain \
WAS \
WAS6 \
WinPopup \
Xen \
Xinetd \
ZFS \
aliyun-vpc-move-ip \
anything \
apache \
asterisk \
aws-vpc-move-ip \
aws-vpc-route53 \
awseip \
awsvip \
azure-lb \
clvm \
conntrackd \
corosync-qnetd \
crypt \
db2 \
dhcpd \
dnsupdate \
dummypy \
docker \
docker-compose \
dovecot \
eDir88 \
ethmonitor \
exportfs \
fio \
galera \
garbd \
gcp-ilb \
gcp-vpc-move-ip \
iSCSILogicalUnit \
iSCSITarget \
ids \
iface-bridge \
iface-macvlan \
iface-vlan \
ipsec \
iscsi \
jboss \
jira \
kamailio \
lxc \
lxd-info \
machine-info \
mariadb \
mdraid \
minio \
mysql \
mysql-proxy \
nagios \
named \
nfsnotify \
nfsserver \
nginx \
nvmet-subsystem \
nvmet-namespace \
nvmet-port \
ocivip \
openstack-cinder-volume \
openstack-floating-ip \
openstack-info \
openstack-virtual-ip \
oraasm \
oracle \
oralsnr \
osceip \
ovsmonitor \
pgagent \
pgsql \
pingd \
podman \
portblock \
postfix \
pound \
proftpd \
rabbitmq-cluster \
rabbitmq-server-ha \
redis \
rkt \
rsyncd \
rsyslog \
scsi2reservation \
sfex \
sg_persist \
mpathpersist \
slapd \
smb-share \
storage-mon \
sybaseASE \
symlink \
syslog-ng \
tomcat \
varnish \
vdo-vol \
vmware \
vsftpd \
zabbixserver
if BUILD_AZURE_EVENTS
ocf_SCRIPTS += azure-events
endif
if BUILD_AZURE_EVENTS_AZ
ocf_SCRIPTS += azure-events-az
endif
if BUILD_GCP_PD_MOVE
ocf_SCRIPTS += gcp-pd-move
endif
if BUILD_GCP_VPC_MOVE_ROUTE
ocf_SCRIPTS += gcp-vpc-move-route
endif
if BUILD_GCP_VPC_MOVE_VIP
ocf_SCRIPTS += gcp-vpc-move-vip
endif
if BUILD_POWERVS_SUBNET
ocf_SCRIPTS += powervs-subnet
endif
ocfcommondir = $(OCF_LIB_DIR_PREFIX)/heartbeat
ocfcommon_DATA = ocf-shellfuncs \
ocf-binaries \
ocf-directories \
ocf-returncodes \
ocf-rarun \
ocf-distro \
apache-conf.sh \
+ aws.sh \
http-mon.sh \
sapdb-nosha.sh \
sapdb.sh \
lvm-clvm.sh \
lvm-plain.sh \
lvm-tag.sh \
openstack-common.sh \
ora-common.sh \
mysql-common.sh \
nfsserver-redhat.sh \
findif.sh \
ocf.py
# 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 -
do_spellcheck = printf '[%s]\n' "$(agent)"; \
OCF_ROOT=$(abs_srcdir) OCF_FUNCTIONS_DIR=$(abs_srcdir) \
./$(agent) meta-data 2>/dev/null \
| xsltproc $(top_srcdir)/make/extract_text.xsl - \
| aspell pipe list -d en_US --ignore-case \
--home-dir=$(top_srcdir)/make -p spellcheck-ignore \
| sed -n 's|^&\([^:]*\):.*|\1|p';
spellcheck:
@$(foreach agent,$(ocf_SCRIPTS), $(do_spellcheck))
clean-local:
rm -rf __pycache__ *.pyc
diff --git a/heartbeat/aws-vpc-move-ip b/heartbeat/aws-vpc-move-ip
index fbeb2ee64..f4b0492f2 100755
--- a/heartbeat/aws-vpc-move-ip
+++ b/heartbeat/aws-vpc-move-ip
@@ -1,541 +1,542 @@
#!/bin/sh
#
#
# OCF resource agent to move an IP address within a VPC in the AWS
#
# Copyright (c) 2017 Markus Guertler (SUSE)
# Based on code of Adam Gandelman (GitHub ec2-resource-agents/elasticip)
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like. Any license provided herein, whether implied or
# otherwise, applies only to this software file. Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+. ${OCF_FUNCTIONS_DIR}/aws.sh
# Defaults
OCF_RESKEY_awscli_default="/usr/bin/aws"
OCF_RESKEY_auth_type_default="key"
OCF_RESKEY_profile_default="default"
OCF_RESKEY_region_default=""
OCF_RESKEY_ip_default=""
OCF_RESKEY_address_default=""
OCF_RESKEY_routing_table_default=""
OCF_RESKEY_routing_table_role_default=""
OCF_RESKEY_interface_default="eth0"
OCF_RESKEY_iflabel_default=""
OCF_RESKEY_monapi_default="false"
OCF_RESKEY_lookup_type_default="InstanceId"
OCF_RESKEY_curl_retries_default="3"
OCF_RESKEY_curl_sleep_default="1"
: ${OCF_RESKEY_awscli=${OCF_RESKEY_awscli_default}}
: ${OCF_RESKEY_auth_type=${OCF_RESKEY_auth_type_default}}
: ${OCF_RESKEY_profile=${OCF_RESKEY_profile_default}}
: ${OCF_RESKEY_region=${OCF_RESKEY_region_default}}
: ${OCF_RESKEY_ip=${OCF_RESKEY_ip_default}}
: ${OCF_RESKEY_address=${OCF_RESKEY_address_default}}
: ${OCF_RESKEY_routing_table=${OCF_RESKEY_routing_table_default}}
: ${OCF_RESKEY_routing_table_role=${OCF_RESKEY_routing_table_role_default}}
: ${OCF_RESKEY_interface=${OCF_RESKEY_interface_default}}
: ${OCF_RESKEY_iflabel=${OCF_RESKEY_iflabel_default}}
: ${OCF_RESKEY_monapi=${OCF_RESKEY_monapi_default}}
: ${OCF_RESKEY_lookup_type=${OCF_RESKEY_lookup_type_default}}
: ${OCF_RESKEY_curl_retries=${OCF_RESKEY_curl_retries_default}}
: ${OCF_RESKEY_curl_sleep=${OCF_RESKEY_curl_sleep_default}}
#######################################################################
USAGE="usage: $0 {start|stop|status|meta-data}";
###############################################################################
###############################################################################
#
# Functions
#
###############################################################################
metadata() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="aws-vpc-move-ip" version="2.0">
<version>1.0</version>
<longdesc lang="en">
Resource Agent to move IP addresses within a VPC of the Amazon Webservices EC2
by changing an entry in an specific routing table
Credentials needs to be setup by running "aws configure", or by using AWS Policies.
See https://aws.amazon.com/cli/ for more information about awscli.
</longdesc>
<shortdesc lang="en">Move IP within a VPC of the AWS EC2</shortdesc>
<parameters>
<parameter name="awscli">
<longdesc lang="en">
Path to command line tools for AWS
</longdesc>
<shortdesc lang="en">Path to AWS CLI tools</shortdesc>
<content type="string" default="${OCF_RESKEY_awscli_default}" />
</parameter>
<parameter name="auth_type">
<longdesc lang="en">
Authentication type "key" for AccessKey and SecretAccessKey set via "aws configure",
or "role" to use AWS Policies.
</longdesc>
<shortdesc lang="en">Authentication type</shortdesc>
<content type="string" default="${OCF_RESKEY_auth_type_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="region">
<longdesc lang="en">
Valid AWS region name (e.g., 'us-west-2')
</longdesc>
<shortdesc lang="en">region name</shortdesc>
<content type="string" default="${OCF_RESKEY_region_default}" />
</parameter>
<parameter name="ip" required="1">
<longdesc lang="en">
VPC private IP address
</longdesc>
<shortdesc lang="en">VPC private IP</shortdesc>
<content type="string" default="${OCF_RESKEY_ip_default}" />
</parameter>
<parameter name="address">
<longdesc lang="en">
Deprecated IP address param. Use the ip param instead.
</longdesc>
<shortdesc lang="en">Deprecated VPC private IP Address</shortdesc>
<content type="string" default="${OCF_RESKEY_address_default}" />
</parameter>
<parameter name="routing_table" required="1">
<longdesc lang="en">
Name of the routing table(s), where the route for the IP address should be changed. If declaring multiple routing tables they should be separated by comma. Example: rtb-XXXXXXXX,rtb-YYYYYYYYY
</longdesc>
<shortdesc lang="en">routing table name(s)</shortdesc>
<content type="string" default="${OCF_RESKEY_routing_table_default}" />
</parameter>
<parameter name="routing_table_role" required="0">
<longdesc lang="en">
Role to use to query/update the route table
</longdesc>
<shortdesc lang="en">route table query/update role</shortdesc>
<content type="string" default="${OCF_RESKEY_routing_table_role_default}" />
</parameter>
<parameter name="interface" required="1">
<longdesc lang="en">
Name of the network interface, i.e. eth0
</longdesc>
<shortdesc lang="en">network interface name</shortdesc>
<content type="string" default="${OCF_RESKEY_interface_default}" />
</parameter>
<parameter name="iflabel">
<longdesc lang="en">
You can specify an additional label for your IP address here.
This label is appended to your interface name.
The kernel allows alphanumeric labels up to a maximum length of 15
characters including the interface name and colon (e.g. eth0:foobar1234)
</longdesc>
<shortdesc lang="en">Interface label</shortdesc>
<content type="string" default="${OCF_RESKEY_iflabel_default}"/>
</parameter>
<parameter name="monapi">
<longdesc lang="en">
Enable enhanced monitoring using AWS API calls to check route table entry
</longdesc>
<shortdesc lang="en">Enhanced Monitoring</shortdesc>
<content type="boolean" default="${OCF_RESKEY_monapi_default}" />
</parameter>
<parameter name="lookup_type" required="0">
<longdesc lang="en">
Name of resource type to lookup in route table.
"InstanceId" : EC2 instance ID. (default)
"NetworkInterfaceId" : ENI ID. (useful in shared VPC setups).
</longdesc>
<shortdesc lang="en">lookup type for route table resource</shortdesc>
<content type="string" default="${OCF_RESKEY_lookup_type_default}" />
</parameter>
<parameter name="curl_retries" unique="0">
<longdesc lang="en">
curl retries before failing
</longdesc>
<shortdesc lang="en">curl retries</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_retries_default}" />
</parameter>
<parameter name="curl_sleep" unique="0">
<longdesc lang="en">
curl sleep between tries
</longdesc>
<shortdesc lang="en">curl sleep</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_sleep_default}" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="180s" />
<action name="stop" timeout="180s" />
<action name="monitor" depth="0" timeout="30s" interval="60s" />
<action name="validate-all" timeout="5s" />
<action name="meta-data" timeout="5s" />
</actions>
</resource-agent>
END
}
execute_cmd_as_role(){
cmd=$1
role=$2
output="$($AWSCLI_CMD sts assume-role --role-arn $role --role-session-name AWSCLI-RouteTableUpdate --output=text)"
export AWS_ACCESS_KEY_ID="$(echo $output | awk -F" " '$4=="CREDENTIALS" {print $5}')"
export AWS_SECRET_ACCESS_KEY="$(echo $output | awk -F" " '$4=="CREDENTIALS" {print $7}')"
export AWS_SESSION_TOKEN="$(echo $output | awk -F" " '$4=="CREDENTIALS" {print $8}')"
#Execute command
ocf_log debug "Assumed Role ${role}"
ocf_log debug "$($OCF_RESKEY_awscli sts get-caller-identity)"
ocf_log debug "executing command: $cmd"
response="$($cmd)"
unset output AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
echo $response
}
ec2ip_set_address_param_compat(){
# Include backward compatibility for the deprecated address parameter
if [ -z "$OCF_RESKEY_ip" ] && [ -n "$OCF_RESKEY_address" ]; then
OCF_RESKEY_ip="$OCF_RESKEY_address"
fi
}
ec2ip_validate() {
for cmd in "$OCF_RESKEY_awscli" ip curl; do
check_binary "$cmd"
done
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ] && [ -z "$OCF_RESKEY_profile" ]; then
ocf_exit_reason "profile parameter not set"
return $OCF_ERR_CONFIGURED
fi
if [ -n "$OCF_RESKEY_iflabel" ]; then
label=${OCF_RESKEY_interface}:${OFC_RESKEY_iflabel}
if [ ${#label} -gt 15 ]; then
ocf_exit_reason "Interface label [$label] exceeds maximum character limit of 15"
exit $OCF_ERR_CONFIGURED
fi
fi
TOKEN=$(get_token)
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
EC2_INSTANCE_ID=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/instance-id")
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
if [ -z "${EC2_INSTANCE_ID}" ]; then
ocf_exit_reason "Instance ID not found. Is this a EC2 instance?"
return $OCF_ERR_GENERIC
fi
return $OCF_SUCCESS
}
ec2ip_monitor() {
MON_RES=""
if [ "${OCF_RESKEY_lookup_type}" = "NetworkInterfaceId" ]; then
EC2_ID="$(ec2ip_get_instance_eni)"
RESOURCE_TYPE="interface"
else
EC2_ID="$EC2_INSTANCE_ID"
RESOURCE_TYPE="instance"
fi
if ocf_is_true ${OCF_RESKEY_monapi} || [ "$__OCF_ACTION" = "start" ] || ocf_is_probe; then
for rtb in $(echo $OCF_RESKEY_routing_table | sed -e 's/,/ /g'); do
ocf_log info "monitor: check routing table (API call) - $rtb"
if [ -z "${OCF_RESKEY_routing_table_role}" ]; then
cmd="$AWSCLI_CMD --output text ec2 describe-route-tables --route-table-ids $rtb --query RouteTables[*].Routes[?DestinationCidrBlock=='$OCF_RESKEY_ip/32'].$OCF_RESKEY_lookup_type"
ocf_log debug "executing command: $cmd"
ROUTE_TO_INSTANCE="$($cmd)"
else
cmd="$OCF_RESKEY_awscli $region_opt --output text ec2 describe-route-tables --route-table-ids $rtb --query RouteTables[*].Routes[?DestinationCidrBlock=='$OCF_RESKEY_ip/32'].$OCF_RESKEY_lookup_type"
ROUTE_TO_INSTANCE="$(execute_cmd_as_role "$cmd" $OCF_RESKEY_routing_table_role)"
fi
ocf_log debug "Overlay IP is currently routed to ${ROUTE_TO_INSTANCE}"
if [ -z "$ROUTE_TO_INSTANCE" ]; then
ROUTE_TO_INSTANCE="<unknown>"
fi
if [ "$EC2_ID" != "$ROUTE_TO_INSTANCE" ]; then
ocf_log warn "not routed to this $RESOURCE_TYPE ($EC2_ID) but to $RESOURCE_TYPE $ROUTE_TO_INSTANCE on $rtb"
MON_RES="$MON_RES $rtb"
fi
sleep 1
done
if [ ! -z "$MON_RES" ]; then
return $OCF_NOT_RUNNING
fi
else
ocf_log debug "monitor: Enhanced Monitoring disabled - omitting API call"
fi
cmd="ip addr show to $OCF_RESKEY_ip up"
ocf_log debug "executing command: $cmd"
RESULT=$($cmd | grep "$OCF_RESKEY_ip")
if [ -z "$RESULT" ]; then
if [ "$__OCF_ACTION" = "monitor" ] && ! ocf_is_probe; then
level="error"
else
level="info"
fi
ocf_log "$level" "IP $OCF_RESKEY_ip not assigned to running interface"
return $OCF_NOT_RUNNING
fi
ocf_log debug "route in VPC and address assigned"
return $OCF_SUCCESS
}
ec2ip_drop() {
cmd="ip addr delete ${OCF_RESKEY_ip}/32 dev $OCF_RESKEY_interface"
ocf_log debug "executing command: $cmd"
output=$($cmd 2>&1)
rc=$?
if [ "$rc" -gt 0 ]; then
if [ "$__OCF_ACTION" = "start" ]; then
# expected to fail during start
level="debug"
else
level="warn"
fi
ocf_log "$level" "command failed, rc $rc"
ocf_log "$level" "output/error: $output"
return $OCF_ERR_GENERIC
else
ocf_log debug "output/error: $output"
fi
# delete remaining route-entries if any
ip route show to exact ${OCF_RESKEY_ip}/32 dev $OCF_RESKEY_interface | xargs -r ip route delete
ip route show table local to exact ${OCF_RESKEY_ip}/32 dev $OCF_RESKEY_interface | xargs -r ip route delete
return $OCF_SUCCESS
}
ec2ip_get_instance_eni() {
MAC_FILE="/sys/class/net/${OCF_RESKEY_interface}/address"
if [ -f $MAC_FILE ]; then
cmd="cat ${MAC_FILE}"
else
cmd="ip -br link show dev ${OCF_RESKEY_interface} | tr -s ' ' | cut -d' ' -f3"
fi
ocf_log debug "executing command: $cmd"
MAC_ADDR="$(eval $cmd)"
rc=$?
if [ $rc != 0 ]; then
ocf_log warn "command failed, rc: $rc"
return $OCF_ERR_GENERIC
fi
ocf_log debug "MAC address associated with interface ${OCF_RESKEY_interface}: ${MAC_ADDR}"
cmd="curl_retry \"$OCF_RESKEY_curl_retries\" \"$OCF_RESKEY_curl_sleep\" \"--show-error -s -H 'X-aws-ec2-metadata-token: $TOKEN'\" \"http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC_ADDR}/interface-id\""
EC2_NETWORK_INTERFACE_ID="$(eval $cmd)"
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
ocf_log debug "network interface id associated MAC address ${MAC_ADDR}: ${EC2_NETWORK_INTERFACE_ID}"
echo $EC2_NETWORK_INTERFACE_ID
}
ec2ip_get_and_configure() {
EC2_NETWORK_INTERFACE_ID="$(ec2ip_get_instance_eni)"
for rtb in $(echo $OCF_RESKEY_routing_table | sed -e 's/,/ /g'); do
if [ -z "${OCF_RESKEY_routing_table_role}" ]; then
cmd="$AWSCLI_CMD --output text ec2 replace-route --route-table-id $rtb --destination-cidr-block ${OCF_RESKEY_ip}/32 --network-interface-id $EC2_NETWORK_INTERFACE_ID"
ocf_log debug "executing command: $cmd"
$cmd
else
cmd="$OCF_RESKEY_awscli $region_opt --output text ec2 replace-route --route-table-id $rtb --destination-cidr-block ${OCF_RESKEY_ip}/32 --network-interface-id $EC2_NETWORK_INTERFACE_ID"
update_response="$(execute_cmd_as_role "$cmd" $OCF_RESKEY_routing_table_role)"
fi
rc=$?
if [ "$rc" != 0 ]; then
ocf_log warn "command failed, rc: $rc"
return $OCF_ERR_GENERIC
fi
sleep 1
done
# Reconfigure the local ip address
ec2ip_drop
extra_opts=""
if [ -n "$OCF_RESKEY_iflabel" ]; then
extra_opts="$extra_opts label $OCF_RESKEY_interface:$OCF_RESKEY_iflabel"
fi
cmd="ip addr add ${OCF_RESKEY_ip}/32 dev $OCF_RESKEY_interface $extra_opts"
ocf_log debug "executing command: $cmd"
$cmd
rc=$?
if [ $rc != 0 ]; then
ocf_log warn "command failed, rc: $rc"
return $OCF_ERR_GENERIC
fi
return $OCF_SUCCESS
}
ec2ip_stop() {
ocf_log info "EC2: Bringing down IP address $OCF_RESKEY_ip"
ec2ip_monitor
if [ $? = $OCF_NOT_RUNNING ]; then
ocf_log info "EC2: Address $OCF_RESKEY_ip already down"
return $OCF_SUCCESS
fi
ec2ip_drop
if [ $? != $OCF_SUCCESS ]; then
return $OCF_ERR_GENERIC
fi
ec2ip_monitor
if [ $? != $OCF_NOT_RUNNING ]; then
ocf_log error "EC2: Couldn't bring down IP address $OCF_RESKEY_ip on interface $OCF_RESKEY_interface."
return $OCF_ERR_GENERIC
fi
ocf_log info "EC2: Successfully brought down $OCF_RESKEY_ip"
return $OCF_SUCCESS
}
ec2ip_start() {
ocf_log info "EC2: Moving IP address $OCF_RESKEY_ip to this host by adjusting routing table $OCF_RESKEY_routing_table"
ec2ip_monitor
if [ $? = $OCF_SUCCESS ]; then
ocf_log info "EC2: $OCF_RESKEY_ip already started"
return $OCF_SUCCESS
fi
ocf_log info "EC2: Adjusting routing table and locally configuring IP address"
ec2ip_get_and_configure
rc=$?
if [ $rc != $OCF_SUCCESS ]; then
ocf_log error "Received $rc from 'aws'"
return $OCF_ERR_GENERIC
fi
ec2ip_monitor
if [ $? != $OCF_SUCCESS ]; then
ocf_log error "EC2: IP address couldn't be configured on this host (IP: $OCF_RESKEY_ip, Interface: $OCF_RESKEY_interface)"
return $OCF_ERR_GENERIC
fi
return $OCF_SUCCESS
}
###############################################################################
#
# MAIN
#
###############################################################################
case $__OCF_ACTION in
meta-data)
metadata
exit $OCF_SUCCESS
;;
usage|help)
echo $USAGE
exit $OCF_SUCCESS
;;
esac
if ! ocf_is_root; then
ocf_log err "You must be root for $__OCF_ACTION operation."
exit $OCF_ERR_PERM
fi
AWSCLI_CMD="${OCF_RESKEY_awscli}"
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ]; then
AWSCLI_CMD="$AWSCLI_CMD --profile ${OCF_RESKEY_profile}"
elif [ "x${OCF_RESKEY_auth_type}" = "xrole" ]; then
if [ -z "${OCF_RESKEY_region}" ]; then
ocf_exit_reason "region needs to be set when using role-based authentication"
exit $OCF_ERR_CONFIGURED
fi
else
ocf_exit_reason "Incorrect auth_type: ${OCF_RESKEY_auth_type}"
exit $OCF_ERR_CONFIGURED
fi
if [ -n "${OCF_RESKEY_region}" ]; then
AWSCLI_CMD="$AWSCLI_CMD --region ${OCF_RESKEY_region}"
fi
ec2ip_set_address_param_compat
ec2ip_validate
case $__OCF_ACTION in
start)
ec2ip_start;;
stop)
ec2ip_stop;;
monitor)
ec2ip_monitor;;
validate-all)
exit $?;;
*)
echo $USAGE
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
diff --git a/heartbeat/aws-vpc-route53.in b/heartbeat/aws-vpc-route53.in
index eba2ed95c..f7e756782 100644
--- a/heartbeat/aws-vpc-route53.in
+++ b/heartbeat/aws-vpc-route53.in
@@ -1,501 +1,502 @@
#!@BASH_SHELL@
#
# 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_FUNCTIONS_DIR}/aws.sh
# Defaults
OCF_RESKEY_awscli_default="/usr/bin/aws"
OCF_RESKEY_auth_type_default="key"
OCF_RESKEY_profile_default="default"
OCF_RESKEY_region_default=""
OCF_RESKEY_hostedzoneid_default=""
OCF_RESKEY_fullname_default=""
OCF_RESKEY_ip_default="local"
OCF_RESKEY_ttl_default=10
OCF_RESKEY_curl_retries_default="3"
OCF_RESKEY_curl_sleep_default="1"
: ${OCF_RESKEY_awscli=${OCF_RESKEY_awscli_default}}
: ${OCF_RESKEY_auth_type=${OCF_RESKEY_auth_type_default}}
: ${OCF_RESKEY_profile=${OCF_RESKEY_profile_default}}
: ${OCF_RESKEY_region=${OCF_RESKEY_region_default}}
: ${OCF_RESKEY_hostedzoneid:=${OCF_RESKEY_hostedzoneid_default}}
: ${OCF_RESKEY_fullname:=${OCF_RESKEY_fullname_default}}
: ${OCF_RESKEY_ip:=${OCF_RESKEY_ip_default}}
: ${OCF_RESKEY_ttl:=${OCF_RESKEY_ttl_default}}
: ${OCF_RESKEY_curl_retries=${OCF_RESKEY_curl_retries_default}}
: ${OCF_RESKEY_curl_sleep=${OCF_RESKEY_curl_sleep_default}}
usage() {
cat <<-EOT
usage: $0 {start|stop|status|monitor|validate-all|meta-data}
EOT
}
metadata() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="aws-vpc-route53" version="1.0">
<version>1.0</version>
<longdesc lang="en">
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
</longdesc>
<shortdesc lang="en">Update Route53 VPC record for AWS EC2</shortdesc>
<parameters>
<parameter name="awscli">
<longdesc lang="en">
Path to command line tools for AWS
</longdesc>
<shortdesc lang="en">Path to AWS CLI tools</shortdesc>
<content type="string" default="${OCF_RESKEY_awscli_default}" />
</parameter>
<parameter name="auth_type">
<longdesc lang="en">
Authentication type "key" for AccessKey and SecretAccessKey set via "aws configure",
or "role" to use AWS Policies.
</longdesc>
<shortdesc lang="en">Authentication type</shortdesc>
<content type="string" default="${OCF_RESKEY_auth_type_default}" />
</parameter>
<parameter name="profile">
<longdesc lang="en">
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".
</longdesc>
<shortdesc lang="en">AWS Profile Name</shortdesc>
<content type="string" default="${OCF_RESKEY_profile_default}" />
</parameter>
<parameter name="hostedzoneid" required="1">
<longdesc lang="en">
Hosted zone ID of Route 53. This is the table of
the Route 53 record.
</longdesc>
<shortdesc lang="en">AWS hosted zone ID</shortdesc>
<content type="string" default="${OCF_RESKEY_hostedzoneid_default}" />
</parameter>
<parameter name="fullname" required="1">
<longdesc lang="en">
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!
</longdesc>
<shortdesc lang="en">Full service name</shortdesc>
<content type="string" default="${OCF_RESKEY_fullname_default}" />
</parameter>
<parameter name="ip" required="0">
<longdesc lang="en">
IP (local (default), public or secondary private IP address (e.g. 10.0.0.1).
A secondary private IP can be setup with the awsvip agent.
</longdesc>
<shortdesc lang="en">Type of IP or secondary private IP address (local, public or e.g. 10.0.0.1)</shortdesc>
<content type="string" default="${OCF_RESKEY_ip_default}" />
</parameter>
<parameter name="ttl" required="0">
<longdesc lang="en">
Time to live for Route53 ARECORD
</longdesc>
<shortdesc lang="en">ARECORD TTL</shortdesc>
<content type="string" default="${OCF_RESKEY_ttl_default}" />
</parameter>
<parameter name="curl_retries" unique="0">
<longdesc lang="en">
curl retries before failing
</longdesc>
<shortdesc lang="en">curl retries</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_retries_default}" />
</parameter>
<parameter name="curl_sleep" unique="0">
<longdesc lang="en">
curl sleep between tries
</longdesc>
<shortdesc lang="en">curl sleep</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_sleep_default}" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="180s" />
<action name="stop" timeout="180s" />
<action name="monitor" depth="0" timeout="180s" interval="300s" />
<action name="validate-all" timeout="5s" />
<action name="meta-data" timeout="5s" />
</actions>
</resource-agent>
END
}
r53_validate() {
ocf_log debug "function: validate"
# Check for required binaries
ocf_log debug "Checking for required binaries"
for command in "${OCF_RESKEY_awscli}" curl dig; do
check_binary "$command"
done
# 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
# Type of IP/secondary IP address
case $OCF_RESKEY_ip in
local|public|*.*.*.*)
;;
*)
ocf_exit_reason "Invalid value for ip: ${OCF_RESKEY_ip}"
exit $OCF_ERR_CONFIGURED
esac
# profile
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ] && [ -z "$OCF_RESKEY_profile" ]; then
ocf_exit_reason "profile parameter not set"
return $OCF_ERR_CONFIGURED
fi
# TTL
[[ -z "$OCF_RESKEY_ttl" ]] && ocf_log error "TTL not set $OCF_RESKEY_ttl!" && exit $OCF_ERR_CONFIGURED
ocf_log debug "Testing aws command"
$OCF_RESKEY_awscli --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"
return $OCF_SUCCESS
}
r53_start() {
#
# Start agent and config DNS in Route53
#
ocf_log info "Starting Route53 DNS update...."
_get_ip
r53_monitor
if [ $? != $OCF_SUCCESS ]; then
ocf_log info "Could not start agent - check configurations"
return $OCF_ERR_GENERIC
fi
return $OCF_SUCCESS
}
r53_stop() {
#
# Stop operation doesn't perform any API call or try to remove the DNS record
# this mostly because this is not necessarily mandatory or desired
# the start and monitor functions will take care of changing the DNS record
# if the agent starts in a different cluster node
#
ocf_log info "Bringing down Route53 agent. (Will NOT remove Route53 DNS record)"
return $OCF_SUCCESS
}
r53_monitor() {
#
# For every start action the agent will call Route53 API to check for DNS record
# otherwise it will try to get results directly by querying the DNS using "dig".
# Due to complexity in some DNS architectures "dig" can fail, and if this happens
# the monitor will fallback to the Route53 API call.
#
# There will be no failure, failover or restart of the agent if the monitor operation fails
# hence we only return $OCF_SUCESS in this function
#
# In case of the monitor operation detects a wrong or non-existent Route53 DNS entry
# it will try to fix the existing one, or create it again
#
#
ARECORD=""
IPREGEX="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"
r53_validate
ocf_log debug "Checking Route53 record sets"
#
_get_ip
#
if [ "$__OCF_ACTION" = "start" ] || ocf_is_probe ; then
#
cmd="$OCF_RESKEY_awscli $AWS_PROFILE_OPT route53 list-resource-record-sets --hosted-zone-id $OCF_RESKEY_hostedzoneid --query ResourceRecordSets[?Name=='$OCF_RESKEY_fullname']"
ocf_log info "Route53 Agent Starting or probing - executing monitoring API call: $cmd"
CLIRES="$($cmd 2>&1)"
rc=$?
ocf_log debug "awscli returned code: $rc"
if [ $rc -ne 0 ]; then
CLIRES=$(echo $CLIRES | grep -v '^$')
ocf_log warn "Route53 API returned an error: $CLIRES"
ocf_log warn "Skipping cluster action due to API call error"
return $OCF_ERR_GENERIC
fi
ARECORD=$(echo $CLIRES | grep RESOURCERECORDS | awk '{ print $5 }')
#
if ocf_is_probe; then
#
# Prevent R53 record change during probe
#
if [[ $ARECORD =~ $IPREGEX ]] && [ "$ARECORD" != "$IPADDRESS" ]; then
ocf_log debug "Route53 DNS record $ARECORD found at probing, disregarding"
return $OCF_NOT_RUNNING
fi
fi
else
#
cmd="dig +retries=3 +time=5 +short $OCF_RESKEY_fullname 2>/dev/null"
ocf_log info "executing monitoring command : $cmd"
ARECORD="$($cmd)"
rc=$?
ocf_log debug "dig return code: $rc"
#
if [[ ! $ARECORD =~ $IPREGEX ]] || [ $rc -ne 0 ]; then
ocf_log info "Fallback to Route53 API query due to DNS resolution failure"
cmd="$OCF_RESKEY_awscli $AWS_PROFILE_OPT route53 list-resource-record-sets --hosted-zone-id $OCF_RESKEY_hostedzoneid --query ResourceRecordSets[?Name=='$OCF_RESKEY_fullname']"
ocf_log debug "executing monitoring API call: $cmd"
CLIRES="$($cmd 2>&1)"
rc=$?
ocf_log debug "awscli return code: $rc"
if [ $rc -ne 0 ]; then
CLIRES=$(echo $CLIRES | grep -v '^$')
ocf_log warn "Route53 API returned an error: $CLIRES"
ocf_log warn "Monitor skipping cluster action due to API call error"
return $OCF_SUCCESS
fi
ARECORD=$(echo $CLIRES | grep RESOURCERECORDS | awk '{ print $5 }')
fi
#
fi
ocf_log info "Route53 DNS record pointing $OCF_RESKEY_fullname to IP address $ARECORD"
#
if [ "$ARECORD" == "$IPADDRESS" ]; then
ocf_log info "Route53 DNS record $ARECORD found"
return $OCF_SUCCESS
elif [[ $ARECORD =~ $IPREGEX ]] && [ "$ARECORD" != "$IPADDRESS" ]; then
ocf_log info "Route53 DNS record points to a different host, setting DNS record on Route53 to this host"
_update_record "UPSERT" "$IPADDRESS"
return $OCF_SUCCESS
else
ocf_log info "No Route53 DNS record found, setting DNS record on Route53 to this host"
_update_record "UPSERT" "$IPADDRESS"
return $OCF_SUCCESS
fi
return $OCF_SUCCESS
}
_get_ip() {
case $OCF_RESKEY_ip in
local|public)
- TOKEN=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -sX PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600'" "http://169.254.169.254/latest/api/token")
+ TOKEN=$(get_token)
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
IPADDRESS=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/${OCF_RESKEY_ip}-ipv4")
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
;;
*.*.*.*)
IPADDRESS="${OCF_RESKEY_ip}";;
esac
}
_update_record() {
#
# This function is the one that will actually execute Route53's API call
# and configure the DNS record using the correct API calls and parameters
#
# It creates a temporary JSON file under /tmp with the required API payload
#
# Failures in this function are critical and will cause the agent to fail
#
update_action="$1"
IPADDRESS="$2"
ocf_log info "Updating Route53 $OCF_RESKEY_hostedzoneid with $IPADDRESS for $OCF_RESKEY_fullname"
ROUTE53RECORD="$(maketempfile)"
if [ $? -ne 0 ] || [ -z "$ROUTE53RECORD" ]; then
ocf_exit_reason "Failed to create temporary file for record update"
exit $OCF_ERR_GENERIC
fi
cat >>"$ROUTE53RECORD" <<-EOF
{
"Comment": "Update record to reflect new IP address for a system ",
"Changes": [
{
"Action": "$update_action",
"ResourceRecordSet": {
"Name": "$OCF_RESKEY_fullname",
"Type": "A",
"TTL": $OCF_RESKEY_ttl,
"ResourceRecords": [
{
"Value": "$IPADDRESS"
}
]
}
}
]
}
EOF
cmd="$OCF_RESKEY_awscli $AWS_PROFILE_OPT route53 change-resource-record-sets --hosted-zone-id $OCF_RESKEY_hostedzoneid --change-batch file://$ROUTE53RECORD "
ocf_log debug "Executing command: $cmd"
CLIRES="$($cmd 2>&1)"
rc=$?
ocf_log debug "awscli returned code: $rc"
if [ $rc -ne 0 ]; then
CLIRES=$(echo $CLIRES | grep -v '^$')
ocf_log warn "Route53 API returned an error: $CLIRES"
ocf_log warn "Skipping cluster action due to API call error"
return $OCF_ERR_GENERIC
fi
CHANGEID=$(echo $CLIRES | awk '{ print $12 }')
ocf_log debug "Change id: $CHANGEID"
rmtempfile $ROUTE53RECORD
CHANGEID=$(echo $CHANGEID | cut -d'/' -f 3 | cut -d'"' -f 1 )
ocf_log debug "Change id: $CHANGEID"
STATUS="PENDING"
MYSECONDS=20
while [ "$STATUS" = 'PENDING' ]; do
sleep $MYSECONDS
STATUS="$($OCF_RESKEY_awscli $AWS_PROFILE_OPT route53 get-change --id $CHANGEID | grep CHANGEINFO | 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
}
###############################################################################
case $__OCF_ACTION in
usage|help)
usage
exit $OCF_SUCCESS
;;
meta-data)
metadata
exit $OCF_SUCCESS
;;
esac
AWSCLI_CMD="${OCF_RESKEY_awscli}"
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ]; then
AWSCLI_CMD="$AWSCLI_CMD --profile ${OCF_RESKEY_profile}"
elif [ "x${OCF_RESKEY_auth_type}" = "xrole" ]; then
if [ -z "${OCF_RESKEY_region}" ]; then
ocf_exit_reason "region needs to be set when using role-based authentication"
exit $OCF_ERR_CONFIGURED
fi
else
ocf_exit_reason "Incorrect auth_type: ${OCF_RESKEY_auth_type}"
exit $OCF_ERR_CONFIGURED
fi
if [ -n "${OCF_RESKEY_region}" ]; then
AWSCLI_CMD="$AWSCLI_CMD --region ${OCF_RESKEY_region}"
fi
AWSCLI_CMD="$AWSCLI_CMD --cli-connect-timeout 10"
case $__OCF_ACTION in
start)
r53_validate || exit $?
r53_start
;;
stop)
r53_stop
;;
monitor)
r53_monitor
;;
validate-all)
r53_validate
;;
*)
usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
exit $?
diff --git a/heartbeat/aws.sh b/heartbeat/aws.sh
new file mode 100644
index 000000000..a77733a91
--- /dev/null
+++ b/heartbeat/aws.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+#
+# AWS Helper Scripts
+#
+#
+
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+
+# Defaults
+OCF_RESKEY_curl_retries_default="3"
+OCF_RESKEY_curl_sleep_default="1"
+
+# Function to enable reusable IMDS token retrieval for efficient repeated access
+# File to store the token and timestamp
+TOKEN_FILE="/tmp/.imds_token"
+TOKEN_LIFETIME=21600 # Token lifetime in seconds (6 hours)
+TOKEN_EXPIRY_THRESHOLD=3600 # Renew token if less than 60 minutes (1 hour) remaining
+
+# Function to fetch a new token
+fetch_new_token() {
+ TOKEN=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -sX PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: $TOKEN_LIFETIME'" "http://169.254.169.254/latest/api/token")
+ echo "$TOKEN $(date +%s)" > "$TOKEN_FILE"
+ echo "$TOKEN"
+}
+
+# Function to retrieve or renew the token
+get_token() {
+ if [ -f "$TOKEN_FILE" ]; then
+ read -r STORED_TOKEN STORED_TIMESTAMP < "$TOKEN_FILE"
+ CURRENT_TIME=$(date +%s)
+ ELAPSED_TIME=$((CURRENT_TIME - STORED_TIMESTAMP))
+
+ if (( ELAPSED_TIME < (TOKEN_LIFETIME - TOKEN_EXPIRY_THRESHOLD) )); then
+ # Token is still valid
+ echo "$STORED_TOKEN"
+ return
+ fi
+ fi
+ # Fetch a new token if not valid
+ fetch_new_token
+}
\ No newline at end of file
diff --git a/heartbeat/awseip b/heartbeat/awseip
index ffb6223a1..049c2e566 100755
--- a/heartbeat/awseip
+++ b/heartbeat/awseip
@@ -1,347 +1,348 @@
#!/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.) or
# (AWSRole) Setup up relevant AWS Policies to allow agent related functions to be executed.
# - 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
+. ${OCF_FUNCTIONS_DIR}/aws.sh
#######################################################################
#
# Defaults
#
OCF_RESKEY_awscli_default="/usr/bin/aws"
OCF_RESKEY_auth_type_default="key"
OCF_RESKEY_profile_default="default"
OCF_RESKEY_region_default=""
OCF_RESKEY_api_delay_default="3"
OCF_RESKEY_curl_retries_default="3"
OCF_RESKEY_curl_sleep_default="1"
: ${OCF_RESKEY_awscli=${OCF_RESKEY_awscli_default}}
: ${OCF_RESKEY_auth_type=${OCF_RESKEY_auth_type_default}}
: ${OCF_RESKEY_profile=${OCF_RESKEY_profile_default}}
: ${OCF_RESKEY_region=${OCF_RESKEY_region_default}}
: ${OCF_RESKEY_api_delay=${OCF_RESKEY_api_delay_default}}
: ${OCF_RESKEY_curl_retries=${OCF_RESKEY_curl_retries_default}}
: ${OCF_RESKEY_curl_sleep=${OCF_RESKEY_curl_sleep_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", or by using AWS Policies.
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="auth_type">
<longdesc lang="en">
Authentication type "key" for AccessKey and SecretAccessKey set via "aws configure",
or "role" to use AWS Policies.
</longdesc>
<shortdesc lang="en">Authentication type</shortdesc>
<content type="string" default="${OCF_RESKEY_auth_type_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="region" required="0">
<longdesc lang="en">
Region for AWS resource (required for role-based authentication)
</longdesc>
<shortdesc lang="en">Region</shortdesc>
<content type="string" default="${OCF_RESKEY_region_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>
<parameter name="curl_retries" unique="0">
<longdesc lang="en">
curl retries before failing
</longdesc>
<shortdesc lang="en">curl retries</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_retries_default}" />
</parameter>
<parameter name="curl_sleep" unique="0">
<longdesc lang="en">
curl sleep between tries
</longdesc>
<shortdesc lang="en">curl sleep</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_sleep_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_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "-s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/network/interfaces/macs/")
for MAC in ${NETWORK_INTERFACES_MACS}; do
curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "-s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC%/*}/local-ipv4s" |
grep -q "^${PRIVATE_IP_ADDRESS}$"
if [ $? -eq 0 ]; then
NETWORK_ID=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "-s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC%/*}/interface-id")
fi
done
if [ -z "$NETWORK_ID" ]; then
ocf_exit_reason "Could not find network interface for private_ip_address: $PRIVATE_IP_ADDRESS"
exit $OCF_ERR_GENERIC
fi
$AWSCLI_CMD ec2 associate-address \
--network-interface-id ${NETWORK_ID} \
--allocation-id ${ALLOCATION_ID} \
--private-ip-address ${PRIVATE_IP_ADDRESS}
RET=$?
else
$AWSCLI_CMD 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_CMD --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_CMD 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_CMD 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 "${OCF_RESKEY_awscli}"
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ] && [ -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
;;
usage|help)
awseip_usage
exit $OCF_SUCCESS
;;
esac
AWSCLI_CMD="${OCF_RESKEY_awscli}"
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ]; then
AWSCLI_CMD="$AWSCLI_CMD --profile ${OCF_RESKEY_profile}"
elif [ "x${OCF_RESKEY_auth_type}" = "xrole" ]; then
if [ -z "${OCF_RESKEY_region}" ]; then
ocf_exit_reason "region needs to be set when using role-based authentication"
exit $OCF_ERR_CONFIGURED
fi
else
ocf_exit_reason "Incorrect auth_type: ${OCF_RESKEY_auth_type}"
exit $OCF_ERR_CONFIGURED
fi
if [ -n "${OCF_RESKEY_region}" ]; then
AWSCLI_CMD="$AWSCLI_CMD --region ${OCF_RESKEY_region}"
fi
ELASTIC_IP="${OCF_RESKEY_elastic_ip}"
ALLOCATION_ID="${OCF_RESKEY_allocation_id}"
PRIVATE_IP_ADDRESS="${OCF_RESKEY_private_ip_address}"
-TOKEN=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -sX PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600'" "http://169.254.169.254/latest/api/token")
+TOKEN=$(get_token)
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
INSTANCE_ID=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/instance-id")
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
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
;;
*)
awseip_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc
diff --git a/heartbeat/awsvip b/heartbeat/awsvip
index ca19ac086..de67981d8 100755
--- a/heartbeat/awsvip
+++ b/heartbeat/awsvip
@@ -1,311 +1,312 @@
#!/bin/sh
#
#
# Manage Secondary Private 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.) or
# (AWSRole) Setup up relevant AWS Policies to allow agent related functions to be executed.
# - a reserved secondary private IP address for EC2 instances high availablity
# - IAM user role with the following permissions:
# * DescribeInstances
# * AssignPrivateIpAddresses
# * UnassignPrivateIpAddresses
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+. ${OCF_FUNCTIONS_DIR}/aws.sh
#######################################################################
#
# Defaults
#
OCF_RESKEY_awscli_default="/usr/bin/aws"
OCF_RESKEY_auth_type_default="key"
OCF_RESKEY_profile_default="default"
OCF_RESKEY_region_default=""
OCF_RESKEY_api_delay_default="3"
OCF_RESKEY_curl_retries_default="3"
OCF_RESKEY_curl_sleep_default="1"
: ${OCF_RESKEY_awscli=${OCF_RESKEY_awscli_default}}
: ${OCF_RESKEY_auth_type=${OCF_RESKEY_auth_type_default}}
: ${OCF_RESKEY_profile=${OCF_RESKEY_profile_default}}
: ${OCF_RESKEY_region=${OCF_RESKEY_region_default}}
: ${OCF_RESKEY_api_delay=${OCF_RESKEY_api_delay_default}}
: ${OCF_RESKEY_curl_retries=${OCF_RESKEY_curl_retries_default}}
: ${OCF_RESKEY_curl_sleep=${OCF_RESKEY_curl_sleep_default}}
meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="awsvip" version="1.0">
<version>1.0</version>
<longdesc lang="en">
Resource Agent for Amazon AWS Secondary Private IP Addresses.
It manages AWS Secondary Private IP Addresses with awscli.
Credentials needs to be setup by running "aws configure", or by using AWS Policies.
See https://aws.amazon.com/cli/ for more information about awscli.
</longdesc>
<shortdesc lang="en">Amazon AWS Secondary Private 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="auth_type">
<longdesc lang="en">
Authentication type "key" for AccessKey and SecretAccessKey set via "aws configure",
or "role" to use AWS Policies.
</longdesc>
<shortdesc lang="en">Authentication type</shortdesc>
<content type="string" default="${OCF_RESKEY_auth_type_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="secondary_private_ip" unique="1" required="1">
<longdesc lang="en">
reserved secondary private ip for ec2 instance
</longdesc>
<shortdesc lang="en">reserved secondary private ip for ec2 instance</shortdesc>
<content type="string" default="" />
</parameter>
<parameter name="region" required="0">
<longdesc lang="en">
Region for AWS resource (required for role-based authentication)
</longdesc>
<shortdesc lang="en">Region</shortdesc>
<content type="string" default="${OCF_RESKEY_region_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>
<parameter name="curl_retries" unique="0">
<longdesc lang="en">
curl retries before failing
</longdesc>
<shortdesc lang="en">curl retries</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_retries_default}" />
</parameter>
<parameter name="curl_sleep" unique="0">
<longdesc lang="en">
curl sleep between tries
</longdesc>
<shortdesc lang="en">curl sleep</shortdesc>
<content type="integer" default="${OCF_RESKEY_curl_sleep_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
}
#######################################################################
awsvip_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
}
awsvip_start() {
awsvip_monitor && return $OCF_SUCCESS
$AWSCLI_CMD ec2 assign-private-ip-addresses \
--network-interface-id ${NETWORK_ID} \
--private-ip-addresses ${SECONDARY_PRIVATE_IP} \
--allow-reassignment
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 "secondary_private_ip has been successfully brought up (${SECONDARY_PRIVATE_IP})"
return $OCF_SUCCESS
}
awsvip_stop() {
awsvip_monitor || return $OCF_SUCCESS
$AWSCLI_CMD ec2 unassign-private-ip-addresses \
--network-interface-id ${NETWORK_ID} \
--private-ip-addresses ${SECONDARY_PRIVATE_IP}
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 "secondary_private_ip has been successfully brought down (${SECONDARY_PRIVATE_IP})"
return $OCF_SUCCESS
}
awsvip_monitor() {
$AWSCLI_CMD ec2 describe-instances \
--instance-id "${INSTANCE_ID}" \
--query 'Reservations[].Instances[].NetworkInterfaces[].PrivateIpAddresses[].PrivateIpAddress[]' \
--output text | \
grep -qE "(^|\s)${SECONDARY_PRIVATE_IP}(\s|$)"
RET=$?
if [ $RET -ne 0 ]; then
return $OCF_NOT_RUNNING
fi
return $OCF_SUCCESS
}
awsvip_validate() {
check_binary "${OCF_RESKEY_awscli}"
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ] && [ -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
;;
usage|help)
awsvip_usage
exit $OCF_SUCCESS
;;
esac
AWSCLI_CMD="${OCF_RESKEY_awscli}"
if [ "x${OCF_RESKEY_auth_type}" = "xkey" ]; then
AWSCLI_CMD="$AWSCLI_CMD --profile ${OCF_RESKEY_profile}"
elif [ "x${OCF_RESKEY_auth_type}" = "xrole" ]; then
if [ -z "${OCF_RESKEY_region}" ]; then
ocf_exit_reason "region needs to be set when using role-based authentication"
exit $OCF_ERR_CONFIGURED
fi
else
ocf_exit_reason "Incorrect auth_type: ${OCF_RESKEY_auth_type}"
exit $OCF_ERR_CONFIGURED
fi
if [ -n "${OCF_RESKEY_region}" ]; then
AWSCLI_CMD="$AWSCLI_CMD --region ${OCF_RESKEY_region}"
fi
SECONDARY_PRIVATE_IP="${OCF_RESKEY_secondary_private_ip}"
TOKEN=$(get_token)
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
INSTANCE_ID=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/instance-id")
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
MAC_ADDRESS=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/mac")
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
NETWORK_ID=$(curl_retry "$OCF_RESKEY_curl_retries" "$OCF_RESKEY_curl_sleep" "--show-error -s -H 'X-aws-ec2-metadata-token: $TOKEN'" "http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC_ADDRESS}/interface-id")
[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
case $__OCF_ACTION in
start)
awsvip_validate
awsvip_start
;;
stop)
awsvip_stop
;;
monitor)
awsvip_monitor
;;
migrate_to)
ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrate_target}."
awsvip_stop
;;
migrate_from)
ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} from ${OCF_RESKEY_CRM_meta_migrate_source}."
awsvip_start
;;
reload)
ocf_log info "Reloading ${OCF_RESOURCE_INSTANCE} ..."
;;
validate|validate-all)
awsvip_validate
;;
*)
awsvip_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc
diff --git a/heartbeat/ocf-shellfuncs.in b/heartbeat/ocf-shellfuncs.in
index 0c4632cf9..922c6ea45 100644
--- a/heartbeat/ocf-shellfuncs.in
+++ b/heartbeat/ocf-shellfuncs.in
@@ -1,1144 +1,1113 @@
#
#
# Common helper functions for the OCF Resource Agents supplied by
# heartbeat.
#
# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée
# All Rights Reserved.
#
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Build version: $Format:%H$
# TODO: Some of this should probably split out into a generic OCF
# library for shell scripts, but for the time being, we'll just use it
# ourselves...
#
# TODO wish-list:
# - Generic function for evaluating version numbers
# - Generic function(s) to extract stuff from our own meta-data
# - Logging function which automatically adds resource identifier etc
# prefixes
# TODO: Move more common functionality for OCF RAs here.
#
# This was common throughout all legacy Heartbeat agents
unset LC_ALL; export LC_ALL
unset LANGUAGE; export LANGUAGE
: ${HA_SBIN_DIR:=@sbindir@}
__SCRIPT_NAME=`basename $0`
if [ -z "$OCF_ROOT" ]; then
: ${OCF_ROOT=@OCF_ROOT_DIR@}
fi
if [ "$OCF_FUNCTIONS_DIR" = ${OCF_ROOT}/resource.d/heartbeat ]; then # old
unset OCF_FUNCTIONS_DIR
fi
: ${OCF_FUNCTIONS_DIR:=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-binaries
. ${OCF_FUNCTIONS_DIR}/ocf-returncodes
. ${OCF_FUNCTIONS_DIR}/ocf-directories
. ${OCF_FUNCTIONS_DIR}/ocf-rarun
. ${OCF_FUNCTIONS_DIR}/ocf-distro
# Define OCF_RESKEY_CRM_meta_interval in case it isn't already set,
# to make sure that ocf_is_probe() always works
: ${OCF_RESKEY_CRM_meta_interval=0}
ocf_is_root() {
if [ X`id -u` = X0 ]; then
true
else
false
fi
}
ocf_maybe_random() {
if test -c /dev/urandom; then
od -An -N4 -tu4 /dev/urandom | tr -d '[:space:]'
else
awk -v pid=$$ 'BEGIN{srand(pid); print rand()}' | sed 's/^.*[.]//'
fi
}
# Portability comments:
# o The following rely on Bourne "sh" pattern-matching, which is usually
# that for filename generation (note: not regexp).
# o The "*) true ;;" clause is probably unnecessary, but is included
# here for completeness.
# o The negation in the pattern uses "!". This seems to be common
# across many OSes (whereas the alternative "^" fails on some).
# o If an OS is encountered where this negation fails, then a possible
# alternative would be to replace the function contents by (e.g.):
# [ -z "`echo $1 | tr -d '[0-9]'`" ]
#
ocf_is_decimal() {
case "$1" in
""|*[!0-9]*) # empty, or at least one non-decimal
false ;;
*)
true ;;
esac
}
ocf_is_true() {
case "$1" in
yes|true|1|YES|TRUE|True|ja|on|ON) true ;;
*) false ;;
esac
}
ocf_is_hex() {
case "$1" in
""|*[!0-9a-fA-F]*) # empty, or at least one non-hex
false ;;
*)
true ;;
esac
}
ocf_is_octal() {
case "$1" in
""|*[!0-7]*) # empty, or at least one non-octal
false ;;
*)
true ;;
esac
}
__ocf_set_defaults() {
__OCF_ACTION="$1"
# Return to sanity for the agents...
unset LANG
LC_ALL=C
export LC_ALL
# TODO: Review whether we really should source this. Or rewrite
# to match some emerging helper function syntax...? This imports
# things which no OCF RA should be using...
# Strip the OCF_RESKEY_ prefix from this particular parameter
if [ -z "$OCF_RESKEY_OCF_CHECK_LEVEL" ]; then
: ${OCF_CHECK_LEVEL:=0}
else
: ${OCF_CHECK_LEVEL:=$OCF_RESKEY_OCF_CHECK_LEVEL}
fi
if [ ! -d "$OCF_ROOT" ]; then
ha_log "ERROR: OCF_ROOT points to non-directory $OCF_ROOT."
exit $OCF_ERR_GENERIC
fi
if [ -z "$OCF_RESOURCE_TYPE" ]; then
: ${OCF_RESOURCE_TYPE:=$__SCRIPT_NAME}
fi
if [ "x$__OCF_ACTION" = "xmeta-data" ]; then
: ${OCF_RESOURCE_INSTANCE:="RESOURCE_ID"}
fi
if [ -z "$OCF_RA_VERSION_MAJOR" ]; then
: We are being invoked as an init script.
: Fill in some things with reasonable values.
: ${OCF_RESOURCE_INSTANCE:="default"}
return 0
fi
if [ -z "$OCF_RESOURCE_INSTANCE" ]; then
ha_log "ERROR: Need to tell us our resource instance name."
exit $OCF_ERR_ARGS
fi
}
hadate() {
date "+${HA_DATEFMT}"
}
set_logtag() {
if [ -z "$HA_LOGTAG" ]; then
if [ -n "$OCF_RESOURCE_INSTANCE" ]; then
HA_LOGTAG="$__SCRIPT_NAME($OCF_RESOURCE_INSTANCE)[$$]"
else
HA_LOGTAG="$__SCRIPT_NAME[$$]"
fi
fi
}
__ha_log() {
local ignore_stderr=false
local loglevel
[ "x$1" = "x--ignore-stderr" ] && ignore_stderr=true && shift
[ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""
# if we're connected to a tty, then output to stderr
if tty >/dev/null; then
if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then
return 0
elif [ "$ignore_stderr" = "true" ]; then
# something already printed this error to stderr, so ignore
return 0
fi
if [ "$HA_LOGTAG" ]; then
echo "$HA_LOGTAG: $*"
else
echo "$*"
fi >&2
return 0
fi
set_logtag
if [ "x${HA_LOGD}" = "xyes" ] ; then
ha_logger -t "${HA_LOGTAG}" "$@"
if [ "$?" -eq "0" ] ; then
return 0
fi
fi
if
[ -n "$HA_LOGFACILITY" ]
then
: logging through syslog
# loglevel is unknown, use 'notice' for now
loglevel=notice
case "${*}" in
*ERROR*) loglevel=err;;
*WARN*) loglevel=warning;;
*INFO*|info) loglevel=info;;
esac
logger -t "$HA_LOGTAG" -p ${HA_LOGFACILITY}.${loglevel} "${*}"
fi
if
[ -n "$HA_LOGFILE" ]
then
: appending to $HA_LOGFILE
echo `hadate`" $HA_LOGTAG: ${*}" >> $HA_LOGFILE
fi
if
[ -z "$HA_LOGFACILITY" -a -z "$HA_LOGFILE" ] && ! [ "$ignore_stderr" = "true" ]
then
: appending to stderr
echo `hadate`"${*}" >&2
fi
if
[ -n "$HA_DEBUGLOG" ]
then
: appending to $HA_DEBUGLOG
if [ "$HA_LOGFILE"x != "$HA_DEBUGLOG"x ]; then
echo "$HA_LOGTAG: "`hadate`"${*}" >> $HA_DEBUGLOG
fi
fi
}
ha_log()
{
__ha_log "$@"
}
ha_debug() {
if [ "x${HA_debug}" = "x0" ] || [ -z "${HA_debug}" ] ; then
return 0
fi
if tty >/dev/null; then
if [ "$HA_LOGTAG" ]; then
echo "$HA_LOGTAG: $*"
else
echo "$*"
fi >&2
return 0
fi
set_logtag
if [ "x${HA_LOGD}" = "xyes" ] ; then
ha_logger -t "${HA_LOGTAG}" -D "ha-debug" "$@"
if [ "$?" -eq "0" ] ; then
return 0
fi
fi
[ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""
if
[ -n "$HA_LOGFACILITY" ]
then
: logging through syslog
logger -t "$HA_LOGTAG" -p "${HA_LOGFACILITY}.debug" "${*}"
fi
if
[ -n "$HA_DEBUGLOG" ]
then
: appending to $HA_DEBUGLOG
echo "$HA_LOGTAG: "`hadate`"${*}" >> $HA_DEBUGLOG
fi
if
[ -z "$HA_LOGFACILITY" -a -z "$HA_DEBUGLOG" ]
then
: appending to stderr
echo "$HA_LOGTAG: `hadate`${*}: ${HA_LOGFACILITY}" >&2
fi
}
ha_parameter() {
local VALUE
VALUE=`sed -e 's%[ ][ ]*% %' -e 's%^ %%' -e 's%#.*%%' $HA_CF | grep -i "^$1 " | sed 's%[^ ]* %%'`
if
[ "X$VALUE" = X ]
then
case $1 in
keepalive) VALUE=2;;
deadtime)
ka=`ha_parameter keepalive`
VALUE=`expr $ka '*' 2 '+' 1`;;
esac
fi
echo $VALUE
}
ocf_log() {
# TODO: Revisit and implement internally.
if
[ $# -lt 2 ]
then
ocf_log err "Not enough arguments [$#] to ocf_log."
fi
__OCF_PRIO="$1"
shift
__OCF_MSG="$*"
case "${__OCF_PRIO}" in
crit) __OCF_PRIO="CRIT";;
err) __OCF_PRIO="ERROR";;
warn) __OCF_PRIO="WARNING";;
info) __OCF_PRIO="INFO";;
debug)__OCF_PRIO="DEBUG";;
*) __OCF_PRIO=`echo ${__OCF_PRIO}| tr '[a-z]' '[A-Z]'`;;
esac
if [ "${__OCF_PRIO}" = "DEBUG" ]; then
ha_debug "${__OCF_PRIO}: $__OCF_MSG"
else
ha_log "${__OCF_PRIO}: $__OCF_MSG"
fi
}
#
# ocf_exit_reason: print exit error string to stderr
# Usage: Allows the OCF script to provide a string
# describing why the exit code was returned.
# Arguments: reason - required, The string that represents why the error
# occured.
#
ocf_exit_reason()
{
local cookie="$OCF_EXIT_REASON_PREFIX"
local fmt
local msg
# No argument is likely not intentional.
# Just one argument implies a printf format string of just "%s".
# "Least surprise" in case some interpolated string from variable
# expansion or other contains a percent sign.
# More than one argument: first argument is going to be the format string.
case $# in
0) ocf_log err "Not enough arguments to ocf_log_exit_msg." ;;
1) fmt="%s" ;;
*) fmt=$1
shift
case $fmt in
*%*) : ;; # ok, does look like a format string
*) ocf_log warn "Does not look like format string: [$fmt]" ;;
esac ;;
esac
if [ -z "$cookie" ]; then
# use a default prefix
cookie="ocf-exit-reason:"
fi
msg=$(printf "${fmt}" "$@")
printf >&2 "%s%s\n" "$cookie" "$msg"
__ha_log --ignore-stderr "ERROR: $msg"
}
#
# ocf_deprecated: Log a deprecation warning
# Usage: ocf_deprecated [param-name]
# Arguments: param-name optional, name of a boolean resource
# parameter that can be used to suppress
# the warning (default
# "ignore_deprecation")
ocf_deprecated() {
local param
param=${1:-ignore_deprecation}
# don't use ${!param} here, it's a bashism
if ! ocf_is_true $(eval echo \$OCF_RESKEY_$param); then
ocf_log warn "This resource agent is deprecated" \
"and may be removed in a future release." \
"See the man page for details." \
"To suppress this warning, set the \"${param}\"" \
"resource parameter to true."
fi
}
#
# Ocf_run: Run a script, and log its output.
# Usage: ocf_run [-q] [-info|-warn|-err] <command>
# -q: don't log the output of the command if it succeeds
# -info|-warn|-err: log the output of the command at given
# severity if it fails (defaults to err)
#
ocf_run() {
local rc
local output
local verbose=1
local loglevel=err
local var
for var in 1 2
do
case "$1" in
"-q")
verbose=""
shift 1;;
"-info"|"-warn"|"-err"|"-debug")
loglevel=${1#-}
shift 1;;
*)
;;
esac
done
output=`"$@" 2>&1`
rc=$?
[ -n "$output" ] && output="$(echo "$output" | tr -s ' \t\r\n' ' ')"
if [ $rc -eq 0 ]; then
if [ "$verbose" -a ! -z "$output" ]; then
ocf_log info "$output"
fi
else
if [ ! -z "$output" ]; then
ocf_log $loglevel "$output"
else
ocf_log $loglevel "command failed: $*"
fi
fi
return $rc
}
ocf_pidfile_status() {
local pid pidfile="$1"
if [ ! -e "$pidfile" ]; then
# Not exists
return 2
fi
pid=$(cat "$pidfile")
kill -0 "$pid" > /dev/null 2>&1
if [ $? = 0 ]; then
return 0
fi
# Stale
return 1
}
# mkdir(1) based locking
# first the directory is created with the name given as $1
# then a file named "pid" is created within that directory with
# the process PID
# stale locks are handled carefully, the inode of a directory
# needs to match before and after test if the process is running
# empty directories are also handled appropriately
# we relax (sleep) occasionally to allow for other processes to
# finish managing the lock in case they are in the middle of the
# business
relax() { sleep 0.5; }
ocf_get_stale_pid() {
local piddir pid dir_inode
piddir="$1"
[ -z "$piddir" ] && return 2
dir_inode="`ls -di $piddir 2>/dev/null`"
[ -z "$dir_inode" ] && return 1
pid=`cat $piddir/pid 2>/dev/null`
if [ -z "$pid" ]; then
# empty directory?
relax
if [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
echo $dir_inode
else
return 1
fi
elif kill -0 $pid >/dev/null 2>&1; then
return 1
elif relax && [ -e "$piddir/pid" ] && [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
echo $pid
else
return 1
fi
}
# There is a race when the following two functions to manage the
# lock file (mk and rm) are invoked in parallel by different
# instances. It is up to the caller to reduce probability of that
# taking place (see ocf_take_lock() below).
ocf_mk_pid() {
mkdir $1 2>/dev/null && echo $$ > $1/pid
}
ocf_rm_pid() {
rm -f $1/pid
rmdir $1 2>/dev/null
}
# Testing and subsequently removing a stale lock (containing the
# process pid) is inherently difficult to do in such a way as to
# prevent a race between creating a pid file and removing it and
# its directory. We reduce the probability of that happening by
# checking if the stale lock persists over a random period of
# time.
ocf_take_lock() {
local lockdir=$1
local rnd
local stale_pid
# we don't want it too short, so strip leading zeros
rnd=$(ocf_maybe_random | sed 's/^0*//')
stale_pid=`ocf_get_stale_pid $lockdir`
if [ -n "$stale_pid" ]; then
sleep 0.$rnd
# remove "stale pid" only if it persists
[ "$stale_pid" = "`ocf_get_stale_pid $lockdir`" ] &&
ocf_rm_pid $lockdir
fi
while ! ocf_mk_pid $lockdir; do
ocf_log info "Sleeping until $lockdir is released..."
sleep 0.$rnd
done
}
ocf_release_lock_on_exit() {
trap "ocf_rm_pid $1" EXIT
}
# returns true if the CRM is currently running a probe. A probe is
# defined as a monitor operation with a monitoring interval of zero.
ocf_is_probe() {
[ "$__OCF_ACTION" = "monitor" -a "$OCF_RESKEY_CRM_meta_interval" = 0 ]
}
# returns true if the resource is configured as a clone. This is
# defined as a resource where the clone-max meta attribute is present.
ocf_is_clone() {
[ ! -z "${OCF_RESKEY_CRM_meta_clone_max}" ]
}
# returns true if the resource is configured as a multistate
# (master/slave) resource. This is defined as a resource where the
# master-max meta attribute is present, and set to greater than zero.
ocf_is_ms() {
[ "${OCF_RESKEY_CRM_meta_promotable}" = "true" ] || { [ ! -z "${OCF_RESKEY_CRM_meta_master_max}" ] && [ "${OCF_RESKEY_CRM_meta_master_max}" -gt 0 ]; }
}
# version check functions
# allow . and - to delimit version numbers
# max version number is 999
#
ocf_is_ver() {
echo $1 | grep '^[0-9][0-9.-]*[0-9A-Za-z.\+-]*$' >/dev/null 2>&1
}
# usage: ocf_version_cmp VER1 VER2
# version strings can contain digits, dots, and dashes
# must start and end with a digit
# returns:
# 0: VER1 smaller (older) than VER2
# 1: versions equal
# 2: VER1 greater (newer) than VER2
# 3: bad format
ocf_version_cmp() {
ocf_is_ver "$1" || return 3
ocf_is_ver "$2" || return 3
local v1=$1
local v2=$2
sort_version="sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n"
older=$( (echo "$v1"; echo "$v2") | $sort_version | head -1 )
if [ "$v1" = "$v2" ]; then
return 1
elif [ "$v1" = "$older" ]; then
return 0
else
return 2 # -1 would look funny in shell ;-)
fi
}
ocf_local_nodename() {
# use crm_node -n for pacemaker > 1.1.8
which pacemakerd > /dev/null 2>&1
if [ $? -eq 0 ]; then
local version=$(pacemakerd -$ | awk '/^Pacemaker /{ print $2 }')
version=$(echo $version | awk -F- '{ print $1 }')
ocf_version_cmp "$version" "1.1.8"
if [ $? -eq 2 ]; then
which crm_node > /dev/null 2>&1
if [ $? -eq 0 ]; then
crm_node -n
return
fi
fi
fi
# otherwise use uname -n
uname -n
}
# usage: dirname DIR
dirname()
{
local a
local b
[ $# = 1 ] || return 1
a="$1"
while [ 1 ]; do
b="${a%/}"
[ "$a" = "$b" ] && break
a="$b"
done
b=${a%/*}
[ -z "$b" -o "$a" = "$b" ] && b="."
echo "$b"
return 0
}
# usage: systemd_is_running
# returns:
# 0 PID 1 is systemd
# 1 otherwise
systemd_is_running()
{
[ "$(cat /proc/1/comm 2>/dev/null)" = "systemd" ]
}
# usage: systemd_drop_in <name> <After|Before> <dependency.service>
systemd_drop_in()
{
local conf_file
if [ $# -ne 3 ]; then
ocf_log err "Incorrect number of arguments [$#] for systemd_drop_in."
fi
systemdrundir="/run/systemd/system/resource-agents-deps.target.d"
mkdir -p "$systemdrundir"
conf_file="$systemdrundir/$1.conf"
conf_line="$2=$3"
if ! { [ -f "$conf_file" ] && grep -q "^$conf_line$" "$conf_file" ; } ; then
cat > "$conf_file" <<-EOF
[Unit]
$conf_line
EOF
# The information is accessible through systemd API and systemd would
# complain about improper permissions.
chmod o+r "$conf_file"
systemctl daemon-reload
fi
}
# usage: curl_retry RETRIES SLEEP ARGS URL
#
# Use --show-error in ARGS to log HTTP error code
#
# returns:
# 0 success
# exit:
# 1 fail
curl_retry()
{
local retries=$1 sleep=$2 opts=$3 url=$4
local tries=$(($retries + 1))
local args="--fail $opts $url"
local result rc
for try in $(seq $tries); do
ocf_log debug "curl $args try $try of $tries"
result=$(echo "$args" | xargs curl 2>&1)
rc=$?
ocf_log debug "result: $result"
[ $rc -eq 0 ] && break
sleep $sleep
done
if [ $rc -ne 0 ]; then
ocf_exit_reason "curl $args failed $tries tries"
exit $OCF_ERR_GENERIC
fi
echo "$result"
return $rc
}
# move process to root cgroup if realtime scheduling is enabled
ocf_move_to_root_cgroup_if_rt_enabled()
{
if [ -e "/sys/fs/cgroup/cpu/cpu.rt_runtime_us" ]; then
echo $$ >> /sys/fs/cgroup/cpu/tasks
if [ "$?" -ne "0" ]; then
ocf_log warn "Unable to move PID $$ to the root cgroup"
fi
fi
}
# usage: crm_mon_no_validation args...
# run crm_mon without any cib schema validation
# This is useful when an agent runs in a bundle to avoid potential
# schema validation errors when host and bundle are not perfectly aligned
# To be used, your shell must support on process substitution (e.g. bash)
# returns:
# <crm_mon error codes>
crm_mon_no_validation()
{
# The subshell prevents parsing error with incompatible shells
ocf_version_cmp "$OCF_RESKEY_crm_feature_set" "3.19.7"
if [ $res -eq 2 ] || [ $res -eq 1 ]; then
"$SHELL" -c "CIB_file=<(${HA_SBIN_DIR}/cibadmin -Q \
${HA_SBIN_DIR}/crm_mon \$*" -- $*
else
"$SHELL" -c "CIB_file=<(${HA_SBIN_DIR}/cibadmin -Q | sed 's/validate-with=\"[^\"]*\"/validate-with=\"none\"/') \
${HA_SBIN_DIR}/crm_mon \$*" -- $*
fi
}
#
# pseudo_resource status tracking function...
#
# This allows pseudo resources to give correct status information. As we add
# resource monitoring, and better resource tracking in general, this will
# become essential.
#
# These scripts work because ${HA_RSCTMP} is cleaned on node reboot.
#
# We create "resource-string" tracking files under ${HA_RSCTMP} in a
# very simple way:
#
# Existence of "${HA_RSCTMP}/resource-string" means that we consider
# the resource named by "resource-string" to be running.
#
# Note that "resource-string" needs to be unique. Using the resource type
# plus the resource instance arguments to make up the resource string
# is probably sufficient...
#
# usage: ha_pseudo_resource resource-string op [tracking_file]
# where op is {start|stop|monitor|status|restart|reload|print}
# print is a special op which just prints the tracking file location
# user can override our choice of the tracking file location by
# specifying it as the third arg
# Note that all operations are silent...
#
ha_pseudo_resource()
{
local ha_resource_tracking_file="${3:-${HA_RSCTMP}/$1}"
case $2 in
start|restart|reload) touch "$ha_resource_tracking_file";;
stop) rm -f "$ha_resource_tracking_file";;
status|monitor)
if
[ -f "$ha_resource_tracking_file" ]
then
return 0
else
case $2 in
status) return 3;;
*) return 7;;
esac
fi;;
print) echo "$ha_resource_tracking_file";;
*) return 3;;
esac
}
# usage: rmtempdir TMPDIR
rmtempdir()
{
[ $# = 1 ] || return 1
if [ -e "$1" ]; then
rmdir "$1" || return 1
fi
return 0
}
# usage: maketempfile [-d]
maketempfile()
{
if [ $# = 1 -a "$1" = "-d" ]; then
mktemp -d
return 0
elif [ $# != 0 ]; then
return 1
fi
mktemp
return 0
}
# usage: rmtempfile TMPFILE
rmtempfile ()
{
[ $# = 1 ] || return 1
if [ -e "$1" ]; then
rm "$1" || return 1
fi
return 0
}
# echo the first lower supported check level
# pass set of levels supported by the agent
# (in increasing order, 0 is optional)
ocf_check_level()
{
local lvl prev
lvl=0
prev=0
if ocf_is_decimal "$OCF_CHECK_LEVEL"; then
# the level list should be very short
for lvl; do
if [ "$lvl" -eq "$OCF_CHECK_LEVEL" ]; then
break
elif [ "$lvl" -gt "$OCF_CHECK_LEVEL" ]; then
lvl=$prev # the previous one
break
fi
prev=$lvl
done
fi
echo $lvl
}
# usage: ocf_stop_processes SIGNALS WAIT_TIME PIDS
#
# we send signals (use quotes for more than one!) in the order
# given; if one or more processes are still running we try KILL;
# the wait_time is the _total_ time we'll spend in this function
# this time may be slightly exceeded if the processes won't leave
#
# returns:
# 0: all processes left
# 1: some processes still running
#
# example:
#
# ocf_stop_processes TERM 5 $pids
#
ocf_stop_processes() {
local signals="$1"
local wait_time="$(($2/`echo $signals|wc -w`))"
shift 2
local pids="$*"
local sig i
test -z "$pids" &&
return 0
for sig in $signals KILL; do
kill -s $sig $pids 2>/dev/null
# try to leave early, and yet leave processes time to exit
sleep 0.2
for i in `seq $wait_time`; do
kill -s 0 $pids 2>/dev/null ||
return 0
sleep 1
done
done
return 1
}
#
# create a given status directory
# if the directory path doesn't start with $HA_VARRUN, then
# we return with error (most of the calls would be with the user
# supplied configuration, hence we need to do necessary
# protection)
# used mostly for PID files
#
# usage: ocf_mkstatedir owner permissions path
#
# owner: user.group
# permissions: permissions
# path: directory path
#
# example:
# ocf_mkstatedir named 755 `dirname $pidfile`
#
ocf_mkstatedir()
{
local owner
local perms
local path
owner=$1
perms=$2
path=$3
test -d $path && return 0
[ $(id -u) = 0 ] || return 1
case $path in
${HA_VARRUN%/}/*) : this path is ok ;;
*) ocf_log err "cannot create $path (does not start with $HA_VARRUN)"
return 1
;;
esac
mkdir -p $path &&
chown $owner $path &&
chmod $perms $path
}
#
# create a unique status directory in $HA_VARRUN
# used mostly for PID files
# the directory is by default set to
# $HA_VARRUN/$OCF_RESOURCE_INSTANCE
# the directory name is printed to stdout
#
# usage: ocf_unique_rundir owner permissions name
#
# owner: user.group (default: "root")
# permissions: permissions (default: "755")
# name: some unique string (default: "$OCF_RESOURCE_INSTANCE")
#
# to use the default either don't set the parameter or set it to
# empty string ("")
# example:
#
# STATEDIR=`ocf_unique_rundir named "" myownstatedir`
#
ocf_unique_rundir()
{
local path
local owner
local perms
local name
owner=${1:-"root"}
perms=${2:-"755"}
name=${3:-"$OCF_RESOURCE_INSTANCE"}
path=$HA_VARRUN/$name
if [ ! -d $path ]; then
[ $(id -u) = 0 ] || return 1
mkdir -p $path &&
chown $owner $path &&
chmod $perms $path || return 1
fi
echo $path
}
#
# RA tracing may be turned on by setting OCF_TRACE_RA
# the trace output will be saved to OCF_TRACE_FILE, if set, or
# by default to
# $HA_VARLIB/trace_ra/<type>/<id>.<action>.<timestamp>
# e.g. $HA_VARLIB/trace_ra/oracle/db.start.2012-11-27.08:37:08
#
# OCF_TRACE_FILE:
# - FD (small integer [3-9]) in that case it is up to the callers
# to capture output; the FD _must_ be open for writing
# - absolute path
#
# NB: FD 9 may be used for tracing with bash >= v4 in case
# OCF_TRACE_FILE is set to a path.
#
ocf_bash_has_xtracefd() {
[ -n "$BASH_VERSION" ] && [ ${BASH_VERSINFO[0]} -ge 4 ]
}
# for backwards compatibility
ocf_is_bash4() {
ocf_bash_has_xtracefd
}
ocf_trace_redirect_to_file() {
local dest=$1
if ocf_bash_has_xtracefd; then
exec 9>$dest
BASH_XTRACEFD=9
else
exec 2>$dest
fi
}
ocf_trace_redirect_to_fd() {
local fd=$1
if ocf_bash_has_xtracefd; then
BASH_XTRACEFD=$fd
else
exec 2>&$fd
fi
}
__ocf_test_trc_dest() {
local dest=$1
if ! touch $dest; then
ocf_log warn "$dest not writable, trace not going to happen"
__OCF_TRC_DEST=""
__OCF_TRC_MANAGE=""
return 1
fi
return 0
}
ocf_default_trace_dest() {
tty >/dev/null && return
if [ -n "$OCF_RESOURCE_TYPE" -a \
-n "$OCF_RESOURCE_INSTANCE" -a -n "$__OCF_ACTION" ]; then
local ts=`date +%F.%T`
__OCF_TRC_DEST=${OCF_RESKEY_trace_dir}/${OCF_RESOURCE_TYPE}/${OCF_RESOURCE_INSTANCE}.${__OCF_ACTION}.$ts
__OCF_TRC_MANAGE="1"
fi
}
ocf_start_trace() {
export __OCF_TRC_DEST="" __OCF_TRC_MANAGE=""
case "$OCF_TRACE_FILE" in
[3-9]) ocf_trace_redirect_to_fd "$OCF_TRACE_FILE" ;;
/*/*) __OCF_TRC_DEST=$OCF_TRACE_FILE ;;
"") ocf_default_trace_dest ;;
*)
ocf_log warn "OCF_TRACE_FILE must be set to either FD (open for writing) or absolute file path"
ocf_default_trace_dest
;;
esac
if [ "$__OCF_TRC_DEST" ]; then
mkdir -p `dirname $__OCF_TRC_DEST`
__ocf_test_trc_dest $__OCF_TRC_DEST ||
return
ocf_trace_redirect_to_file "$__OCF_TRC_DEST"
fi
if [ -n "$BASH_VERSION" ]; then
PS4='+ `date +"%T"`: ${FUNCNAME[0]:+${FUNCNAME[0]}:}${LINENO}: '
fi
set -x
env=$( echo; printenv | sort )
}
ocf_stop_trace() {
set +x
}
# Helper functions to map from nodename/bundle-name and physical hostname
# list_index_for_word "node0 node1 node2 node3 node4 node5" node4 --> 5
# list_word_at_index "NA host1 host2 host3 host4 host5" 3 --> host2
# list_index_for_word "node1 node2 node3 node4 node5" node7 --> ""
# list_word_at_index "host1 host2 host3 host4 host5" 8 --> ""
# attribute_target node1 --> host1
list_index_for_word() {
echo $1 | tr ' ' '\n' | awk -v x="$2" '$0~x {print NR}'
}
list_word_at_index() {
echo $1 | tr ' ' '\n' | awk -v n="$2" 'n == NR'
}
ocf_attribute_target() {
if [ x$1 = x ]; then
if [ x$OCF_RESKEY_CRM_meta_container_attribute_target = xhost -a x$OCF_RESKEY_CRM_meta_physical_host != x ]; then
echo $OCF_RESKEY_CRM_meta_physical_host
else
if [ x$OCF_RESKEY_CRM_meta_on_node != x ]; then
echo $OCF_RESKEY_CRM_meta_on_node
else
ocf_local_nodename
fi
fi
return
elif [ x"$OCF_RESKEY_CRM_meta_notify_all_uname" != x ]; then
index=$(list_index_for_word "$OCF_RESKEY_CRM_meta_notify_all_uname" $1)
mapping=""
if [ x$index != x ]; then
mapping=$(list_word_at_index "$OCF_RESKEY_CRM_meta_notify_all_hosts" $index)
fi
if [ x$mapping != x -a x$mapping != xNA ]; then
echo $mapping
return
fi
fi
echo $1
}
ocf_promotion_score() {
ocf_version_cmp "$OCF_RESKEY_crm_feature_set" "3.10.0"
res=$?
if [ $res -eq 2 ] || [ $res -eq 1 ] || ! have_binary "crm_master"; then
${HA_SBIN_DIR}/crm_attribute -p ${OCF_RESOURCE_INSTANCE} $@
else
${HA_SBIN_DIR}/crm_master -l reboot $@
fi
}
__ocf_set_defaults "$@"
: ${OCF_TRACE_RA:=$OCF_RESKEY_trace_ra}
: ${OCF_RESKEY_trace_dir:="$HA_VARLIB/trace_ra"}
ocf_is_true "$OCF_TRACE_RA" && ocf_start_trace
# pacemaker sets HA_use_logd, some others use HA_LOGD :/
if ocf_is_true "$HA_use_logd"; then
: ${HA_LOGD:=yes}
-fi
-
-# File to store the token and timestamp
-TOKEN_FILE="/tmp/.imds_token"
-TOKEN_LIFETIME=21600 # Token lifetime in seconds (6 hours)
-TOKEN_EXPIRY_THRESHOLD=3600 # Renew token if less than 60 minutes (1 hour) remaining
-
-# Function to fetch a new token
-fetch_new_token() {
- TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: $TOKEN_LIFETIME")
- echo "$TOKEN $(date +%s)" > "$TOKEN_FILE"
- echo "$TOKEN"
-}
-
-# Function to retrieve or renew the token
-get_token() {
- if [[ -f "$TOKEN_FILE" ]]; then
- read -r STORED_TOKEN STORED_TIMESTAMP < "$TOKEN_FILE"
- CURRENT_TIME=$(date +%s)
- ELAPSED_TIME=$((CURRENT_TIME - STORED_TIMESTAMP))
-
- if (( ELAPSED_TIME < (TOKEN_LIFETIME - TOKEN_EXPIRY_THRESHOLD) )); then
- # Token is still valid
- echo "$STORED_TOKEN"
- return
- fi
- fi
- # Fetch a new token if not valid
- fetch_new_token
-}
-
-
+fi
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Mon, Feb 24, 12:07 PM (12 h, 7 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1464190
Default Alt Text
(91 KB)

Event Timeline