-
Notifications
You must be signed in to change notification settings - Fork 721
/
topologyUpdate
executable file
·232 lines (192 loc) · 8.2 KB
/
topologyUpdate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/bin/bash
# shellcheck disable=SC2086,SC2034
# shellcheck source=/dev/null
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail
set -eo pipefail
# This is borrowed from https://github.com/cardano-community/guild-operators
# and mainly just removes the auto-update of the script itself
#
# i.e. we want a stable/known version of this script
#
# Also, this is hopefully only a temporary thing until
# the node can take care about it's p2p2 updates itself
######################################
# User Variables - Change as desired #
######################################
#CNODE_HOSTNAME="CHANGE ME" # (Optional) Must resolve to the IP you are requesting from
#CUSTOM_PEERS="None" # *Additional* custom peers to (IP,port[,valency]) to add to your target topology.json
# eg: "10.0.0.1,3001|10.0.0.2,3002|relays.mydomain.com,3003,3"
CNODE_VALENCY=1 # (Optional) for multi-IP hostnames
MAX_PEERS=15 # Maximum number of peers to return on successful fetch
CARDANO_ENV="/usr/local/bin/env"
if [[ ! -f ${CARDANO_ENV} ]]; then
echo "[Error] Generated env file missing: ${CARDANO_ENV}"
echo "This is a mandatory prerequisite\n"
exit 1
fi
# source generated env variables
if ! source ${CARDANO_ENV}; then
echo "[Error] Cannot source: ${CARDANO_ENV}"
exit 1;
fi
# NetworkMagic extracted from the genesis file
NWMAGIC=$(jq -r .networkMagic < ${GENESIS_JSON})
# Installed by pkgs.cacert
# https://curl.se/docs/sslcerts.html
export CURL_CA_BUNDLE="/etc/ssl/certs/ca-bundle.crt"
######################################
# Do NOT modify code below #
######################################
usage() {
cat <<EOF
Usage: $(basename "$0") [-f] [-p] [-l]
Topology Updater - Build topology with community pools
-f Disable fetch of a fresh topology file
-p Disable node alive push to Topology Updater API
-l Enable an endless topology update loop (one call per hour)
EOF
exit 1
}
TU_FETCH='Y'
TU_PUSH='Y'
LOOP='N'
while getopts :fpl opt; do
case ${opt} in
f ) TU_FETCH='N' ;;
p ) TU_PUSH='N' ;;
l ) LOOP='Y' ;;
\? ) usage ;;
esac
done
shift $((OPTIND -1))
# Description : Helper function to validate that input is a number
# : $1 = number
isNumber() {
[[ -z $1 ]] && return 1
[[ $1 =~ ^[0-9]+$ ]] && return 0 || return 1
}
# Description : Helper function to validate IPv4 address
# : $1 = IP
isValidIPv4() {
local ip=$1
[[ -z ${ip} ]] && return 1
if [[ ${ip} =~ ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ || ${ip} =~ ^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ ]]; then
return 0
fi
return 1
}
# Description : Helper function to validate IPv6 address, works for normal IPv6 addresses, not dual incl IPv4
# : $1 = IP
isValidIPv6() {
local ip=$1
[[ -z ${ip} ]] && return 1
ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
[[ ${ip} =~ ${ipv6_regex} ]] && return 0
return 1
}
if [[ ${CNODE_HOSTNAME} != "" ]]; then
T_HOSTNAME="&hostname=${CNODE_HOSTNAME}"
else
T_HOSTNAME=''
fi
#####################################################################
#
# Fetch the current block number from the EKG endpoint
#
fetchBlockNumber () {
fail_cnt=0
while ! blockNo=$(curl -s -m ${EKG_TIMEOUT} -H 'Accept: application/json' "http://${EKG_HOST}:${EKG_PORT}/" 2>/dev/null | jq -er '.cardano.node.metrics.blockNum.int.val //0' ); do
((fail_cnt++))
if [[ ${fail_cnt} -eq 5 ]]; then echo "5 consecutive EKG queries failed, aborting!"; exit 1; fi
echo "(${fail_cnt}/5) Failed to grab blockNum from node EKG metrics, sleeping for 30s before retrying..."
sleep 30
done
}
#####################################################################
#
# Push the node config info to the topology endpoint
#
# If you run your node in IPv4/IPv6 dual stack network configuration and want announced the
# IPv4 address only please add the -4 parameter to the curl command below (curl -4 -s ...)
pushNodeInfo () {
if [[ ${TU_PUSH} = "Y" ]]; then
echo "[`date +'%Y-%m-%d %H:%M:%S %Z'`] Updating topology ..."
fetchBlockNumber
echo "curl -s -f https://api.clio.one/htopology/v1/?port=${CNODE_PORT}&blockNo=${blockNo}&valency=${CNODE_VALENCY}&magic=${NWMAGIC}${T_HOSTNAME}"
curl -s -f "https://api.clio.one/htopology/v1/?port=${CNODE_PORT}&blockNo=${blockNo}&valency=${CNODE_VALENCY}&magic=${NWMAGIC}${T_HOSTNAME}" | \
tee -a ${LOG_DIR}/topologyUpdateResult
else
echo "[Warning] Topology update push disabled"
fi
}
#####################################################################
#
# Fetch the next topology update
#
fetchTopology () {
if [[ ${TU_FETCH} = "Y" ]]; then
echo "curl -s -f https://api.clio.one/htopology/v1/fetch/?max=${MAX_PEERS}&magic=${NWMAGIC}"
curl -s -f -o ${TOPOLOGY}.tmp "https://api.clio.one/htopology/v1/fetch/?max=${MAX_PEERS}&magic=${NWMAGIC}" \
&& cat ${TOPOLOGY}.tmp
# Check if old style CUSTOM_PEERS with colon separator is used, if so convert to use commas
if [[ -n ${CUSTOM_PEERS} && ${CUSTOM_PEERS} != *","* ]]; then
CUSTOM_PEERS=${CUSTOM_PEERS//[:]/,}
fi
if [[ -n "${CUSTOM_PEERS}" ]]; then
topo="$(cat "${TOPOLOGY}".tmp)"
IFS='|' read -ra cpeers <<< "${CUSTOM_PEERS}"
for cpeer in "${cpeers[@]}"; do
IFS=',' read -ra cpeer_attr <<< "${cpeer}"
case ${#cpeer_attr[@]} in
2) addr="${cpeer_attr[0]}"
port=${cpeer_attr[1]}
valency=1 ;;
3) addr="${cpeer_attr[0]}"
port=${cpeer_attr[1]}
valency=${cpeer_attr[2]} ;;
*) echo "ERROR: Invalid Custom Peer definition '${cpeer}'. Please double check CUSTOM_PEERS definition"
exit 1 ;;
esac
if [[ ${addr} = *.* ]]; then
! isValidIPv4 "${addr}" && echo "ERROR: Invalid IPv4 address or hostname '${addr}'. Please check CUSTOM_PEERS definition" && continue
elif [[ ${addr} = *:* ]]; then
! isValidIPv6 "${addr}" && echo "ERROR: Invalid IPv6 address '${addr}'. Please check CUSTOM_PEERS definition" && continue
fi
! isNumber ${port} && echo "ERROR: Invalid port number '${port}'. Please check CUSTOM_PEERS definition" && continue
! isNumber ${valency} && echo "ERROR: Invalid valency number '${valency}'. Please check CUSTOM_PEERS definition" && continue
topo=$(jq '.Producers += [{"addr": $addr, "port": $port|tonumber, "valency": $valency|tonumber}]' --arg addr "${addr}" --arg port ${port} --arg valency ${valency} <<< "${topo}")
done
echo "${topo}" | jq -r . >/dev/null 2>&1 && echo "${topo}" > "${TOPOLOGY}".tmp
fi
mv ${TOPOLOGY}.tmp ${TOPOLOGY} && echo "Topology updated: ${TOPOLOGY}"
else
echo "[Warning] Topology update fetch disabled"
fi
}
##########################################################################################################################################
#
# Run a single topology update cycle
#
if [[ ${LOOP} = "N" ]]; then
pushNodeInfo
fetchTopology
exit 0
fi
##########################################################################################################################################
#
# Run an endless topology update loop
#
MIN=`date +"%-M"`
INITIAL_WAIT=5
MIN=$(($MIN + $INITIAL_WAIT))
if (( $MIN > 59 )); then MIN=$(($MIN - 60)); fi
CRONJOB="$MIN * * * * root topologyUpdate"
echo "Topology update: $CRONJOB"
sleep $(($INITIAL_WAIT * 60))
while true; do
# Allow each ef these to fail
pushNodeInfo || true
fetchTopology || true
sleep 3600
done
echo "[Warning] Exiting topology update loop"