Skip to content

Instantly share code, notes, and snippets.

@meeDamian
Last active September 21, 2018 07:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save meeDamian/fec388a943e0d4e64c876e6196a8d18f to your computer and use it in GitHub Desktop.
Save meeDamian/fec388a943e0d4e64c876e6196a8d18f to your computer and use it in GitHub Desktop.
This is a series of utility functions to interface with RBP metrics, bitcoind, lnd and lightningd. To install, download and execute `install.sh` file.
#!/bin/sh
. /usr/local/lib/metrics/utilities.sh
# BITCOIN CORE
# ------------
#
# * assumes default ~/.bitcoin folder location
# * tries to detect bitcoin-cli path
# * tries to determine user running bitcoind
# * whenever possible or makes sense the fn will try to cache the result
# Returns full path to bitcoin-cli
get_bitcoind_binary_path() {
_cache "command -v bitcoin-cli"
}
# Returns user under which bitcoind is running
get_bitcoind_user() {
_cache "ps -eo user:10,command | grep \"[b]itcoind\" | awk '{ print \$1 }' | grep -v root | head -n 1"
}
# Returns full path to bitcoind' data dir
get_bitcoind_data_dir_path() {
user="$(get_bitcoind_user)"
if [ -z "${user}" ]; then
return 1
fi
path="/home/${user}/.bitcoin"
if [ ! -d "${path}" ]; then
return 1
fi
echo "${path}"
}
# Returns exist code=0 if bitcoind is available, or exit code=1 otherwise
is_bitcoind_available() {
if [ -z "$(get_bitcoind_binary_path)" ] || [ -z "$(get_bitcoind_data_dir_path)" ]; then
return 1
fi
return 0
}
# Returns complete bitcoind command
get_bitcoind_full_cmd() {
if is_bitcoind_available; [ "$?" -ne "0" ]; then
return 1
fi
echo "timeout 5s ${SUDO} $(get_bitcoind_binary_path) -datadir=$(get_bitcoind_data_dir_path)" | tr -s " "
}
# Returns & caches the output of `getblockchaininfo` command
_cache_bitcoind_getblockchaininfo() {
_cache_node "bitcoind" "getblockchaininfo"
}
# Returns & caches the output of `getpeerinfo` command
_cache_bitcoind_getpeerinfo() {
_cache_node "bitcoind" "getpeerinfo"
}
# Returns & caches the output of `getnetworkinfo` command
_cache_bitcoind_getnetworkinfo() {
_cache_node "bitcoind" "getnetworkinfo"
}
# Returns & caches the output of `getmempoolinfo` command
_cache_bitcoind_getmempoolinfo() {
_cache_node "bitcoind" "getmempoolinfo"
}
# Returns chain on which bitcoind instance is running: {mainnet,testnet,regnet}
get_bitcoind_chain() {
chain="$(_cache_bitcoind_getblockchaininfo | jq -r '.chain')"
if [ -z "${chain}" ]; then
return 1
fi
echo "${chain}net"
}
# Returns the amount of blocks on the currently followed chain
get_bitcoind_blocks() {
blocks="$(_cache_bitcoind_getblockchaininfo | jq -r '.blocks')"
if [ -z "${blocks}" ]; then
return 1
fi
echo "${blocks}"
}
# Return bitcoind chain sync progress (in %)
get_bitcoind_progress() {
progress="$(_cache_bitcoind_getblockchaininfo | jq -r '.verificationprogress')"
if [ -z "${progress}" ]; then
return 1
fi
printf "%.2f%%" "$(echo ${progress} | awk '{print 100 * $1}')"
}
# Returns the amount of peers the node is currently connected to
# probably the same number as get_bitcoind_connection_count
get_bitcoind_peer_count() {
peers="$(_cache_bitcoind_getpeerinfo | jq 'length')"
if [ -z "${peers}" ]; then
return 1
fi
echo "${peers}"
}
# Returns semver-like version of the running instance
get_bitcoind_version() {
version="$(_cache_bitcoind_getnetworkinfo | jq -r '.subversion' | grep -Po '((\d+\.?){3})')"
if [ -z "${version}" ]; then
return 1
fi
echo "${version}"
}
# Returns a newline-separated list of addresses under which bitcoind should be reachable
get_bitcoind_addresses() {
addresses="$(_cache_bitcoind_getnetworkinfo | jq -r '[.localaddresses[] | [.address, .port|tostring] | join(":")] | join("\n")')"
if [ -z "${addresses}" ]; then
return 1
fi
echo "${addresses}"
}
# Returns the amount of connections bitcoind has open
# probably the same number as get_bitcoind_peer_count
get_bitcoind_connection_count() {
connections="$(_cache_bitcoind_getnetworkinfo | jq -r '.connections')"
if [ -z "${connections}" ]; then
return 1
fi
echo "${connections}"
}
# Returns the amount of transactions currently in the local mempool
get_bitcoind_mempool_count() {
mempool="$(_cache_bitcoind_getmempoolinfo | jq -r '.size')"
if [ -z "${mempool}" ]; then
return 1
fi
echo "${mempool}"
}
# Returns current bitcoind balance
get_bitcoind_balance() {
# NOTE: special case/direct call/no cache
cmd="$(get_bitcoind_full_cmd)"
if [ -z "${cmd}" ]; then
return 1
fi
balance=$(${cmd} getbalance 2>/dev/null)
if [ "$1" = "btc" ]; then
printf "%0.8f" "${balance}"
return 0
fi
echo "$(btc_to_sat ${balance})"
}
metrics_bitcoind_print_all() {
echo "~~~ BITCOIND ~~~"
_pair "path" "$(_handle_empty "$(get_bitcoind_binary_path)")"
_pair "user" "$(_handle_empty "$(get_bitcoind_user)")"
_pair "data" "$(_handle_empty "$(get_bitcoind_data_dir_path)")"
_pair "avail" "$(is_bitcoind_available && echo "yes" || echo "no")"
_pair "cmd" "$(_handle_empty "$(get_bitcoind_full_cmd)")"
_pair "chain" "$(_handle_empty "$(get_bitcoind_chain)")"
_pair "block" "$(_handle_empty "$(get_bitcoind_blocks)")"
_pair "ver" "$(_handle_empty "$(get_bitcoind_version)")"
_pair "addrs" "$(_handle_empty "$(get_bitcoind_addresses | tr "\n" " ")")"
_pair "sync" "$(_handle_empty "$(get_bitcoind_progress)")"
_pair "peers" "$(_handle_empty "$(get_bitcoind_peer_count)")"
_pair "conns" "$(_handle_empty "$(get_bitcoind_connection_count)")"
_pair "mempl" "$(_handle_empty "$(get_bitcoind_mempool_count)")"
_print_all_monetary_fmts "balance" "get_bitcoind_balance"
}
#!/bin/sh
. /usr/local/lib/metrics/utilities.sh
# GENERAL RBP METRICS
# ===================
# Returns RBP hostname
get_host() {
hostname
}
# Returns current temperature (in C) as self-reported by RBP
get_temp() {
temp="$(/opt/vc/bin/vcgencmd measure_temp)"
if [ "$?" -ne "0" ]; then
return 1
fi
echo "${temp}" | cut -d "'" -f 1 | cut -d "=" -f 2
}
# Returns current voltage (in V) as self-reported by RBP
get_volt() {
volt="$(/opt/vc/bin/vcgencmd measure_volts core)"
if [ "$?" -ne "0" ]; then
return 1
fi
echo "${volt}" | tr -d V | cut -d "=" -f 2
}
# Returns current CPU frequency (in GHz) as self-reported by RBP
get_freq() {
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq | jq '. / 1e6'
}
get_is_undervolt() {
vcgencmd get_throttled | cut -d 'x' -f 2 | awk '{print "obase=2; ibase=16; "$1}' | bc | awk '{printf "%20s", $1}' | tr ' ' '0' | rev | cut -c $((0 + 1))
}
get_is_throttled() {
vcgencmd get_throttled | cut -d 'x' -f 2 | awk '{print "obase=2; ibase=16; "$1}' | bc | awk '{printf "%20s", $1}' | tr ' ' '0' | rev | cut -c $((2 + 1))
}
get_was_undervolt() {
vcgencmd get_throttled | cut -d 'x' -f 2 | awk '{print "obase=2; ibase=16; "$1}' | bc | awk '{printf "%20s", $1}' | tr ' ' '0' | rev | cut -c $((16 + 1))
}
get_was_throttled() {
vcgencmd get_throttled | cut -d 'x' -f 2 | awk '{print "obase=2; ibase=16; "$1}' | bc | awk '{printf "%20s", $1}' | tr ' ' '0' | rev | cut -c $((18 + 1))
}
# Returns SSID of the current WiFi
get_ssid() {
${SUDO} iwgetid --raw
}
# Returns currently assigned local IP
get_local_ip() {
ip route get 1 | awk '{print $NF;exit}'
}
# Returns currently assigned public IP
get_public_ip() {
curl -s ipinfo.io/ip
}
# Returns uptime in seconds
get_uptime() {
cat /proc/uptime | cut -d '.' -f 1
}
is_external_storage_available() {
if df | grep "/dev/sd" 2>&1 1>/dev/null; [ "$?" -eq "0" ]; then
return 0 # available
fi
return 1 # not available
}
# Returns storage status specification in a form:
# {sd,ext} {total,used,free}: bytes human-readeable
#
# Example usage:
# get_storage | grep sd | grep free | cut -f 3 # for bytes
# get_storage | grep ext | grep free | cut -f 4 # for human-readeable
get_storage() {
_stor_mem_format "sd" "total" "$(df | grep '/$' | awk '{s = $3+$4} END {print s}')000"
_stor_mem_format "sd" "used" "$(df | grep '/$' | awk '{print $3}')000"
_stor_mem_format "sd" "free" "$(df | grep '/$' | awk '{print $4}')000"
if is_external_storage_available; [ "$?" -eq "0" ]; then
_stor_mem_format "ext" "total" "$(df | grep '/dev/sd' | awk '{s += $3+$4} END {print s}')000"
_stor_mem_format "ext" "used" "$(df | grep '/dev/sd' | awk '{s += $3 } END {print s}')000"
_stor_mem_format "ext" "free" "$(df | grep '/dev/sd' | awk '{s += $4} END {print s}')000"
fi
}
# Returns exit code=0 if SWAP is enabled, or exit code=1 otherwise
is_swap_enabled() {
if [ "$(cat /proc/swaps | wc -l)" -ge "2" ]; then
return 0 # enabled
fi
return 1 # disabled
}
# Returns memory status specification in a form:
# {ram,swap} {total,used,free}: bytes human-readeable
#
# Example usage:
# get_memory | grep ram | grep free | cut -f 3 # for bytes
# get_memory | grep swap | grep free | cut -f 4 # for human-readeable
get_memory() {
_stor_mem_format "ram" "total" "$(free -b | grep 'Mem' | awk '{print $2}')"
_stor_mem_format "ram" "used" "$(free -b | grep 'Mem' | awk '{print $3}')"
_stor_mem_format "ram" "free" "$(free -b | grep 'Mem' | awk '{print $4}')"
if is_swap_enabled; [ "$?" -eq "0" ]; then
_stor_mem_format "swap" "total" "$(free -b | grep 'Swap' | awk '{print $2}')"
_stor_mem_format "swap" "used" "$(free -b | grep 'Swap' | awk '{print $3}')"
_stor_mem_format "swap" "free" "$(free -b | grep 'Swap' | awk '{print $4}')"
fi
}
metrics_board_print_all() {
echo "~~~ BOARD ~~~"
_pair "host" "$(get_host)"
_pair "temp" "$(_handle_empty "$(get_temp)")"
_pair "volt" "$(_handle_empty "$(get_volt)")"
_pair "freq" "$(get_freq)"
_pair "ssid" "$(_handle_empty "$(get_ssid)")"
_pair "lan_ip" "$(get_local_ip)"
_pair "pub_ip" "$(get_public_ip)"
_pair "uptime" "$(get_uptime)"
_pair "is_undervolt" "$(get_is_undervolt)"
_pair "is_throttled" "$(get_is_throttled)"
_pair "was_undervolt" "$(get_was_undervolt)"
_pair "was_throttled" "$(get_was_throttled)"
_pair "ext_avail" "$(is_external_storage_available && echo "yes" || echo "no")"
get_storage
_pair "swap_enabled" "$(is_swap_enabled && echo "yes" || echo "no")"
get_memory
}
#!/bin/sh
# Line below ensures that TERM is set, so that colors
# are displayed properly even if env is empty (ex. motd)
# src: https://unix.stackexchange.com/a/417223/31104
export TERM="${TERM:-xterm-256color}"
BOLD=$(tput bold)
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
BLUE=$(tput setaf 4)
VIOLET=$(tput setaf 5)
SOME_COLOR=$(tput setaf 6)
WHITE=$(tput setaf 7)
RESET_STYLE=$(tput sgr0)
#!/bin/sh
if [ "$(id -u)" -ne "0" ]; then
echo "Installing these functions requires root"
exit 1
fi
if command -v jq 2>&1 1>/dev/null; [ "$?" -ne "0" ]; then
echo "'jq' is needed by functions within this script. If you're on Debian-based system, you can install it with:"
echo " sudo apt install jq"
exit 1
fi
if command -v bc 2>&1 1>/dev/null; [ "$?" -ne "0" ]; then
echo "'bc' is needed by functions within this script. If you're on Debian-based system, you can install it with:"
echo " sudo apt install bc"
exit 1
fi
DEST=/usr/local/lib/metrics
mkdir -p "${DEST}"
wget -qN 'https://gist.githubusercontent.com/meeDamian/fec388a943e0d4e64c876e6196a8d18f/raw/36bb0af82a8ce1690ef37a2e5b0527824079e838/bitcoind.sh' -P "${DEST}"
wget -qN 'https://gist.githubusercontent.com/meeDamian/fec388a943e0d4e64c876e6196a8d18f/raw/33e976d80a41a8de5d3cc647e5e403e37e343d21/board.sh' -P "${DEST}"
wget -qN 'https://gist.githubusercontent.com/meeDamian/fec388a943e0d4e64c876e6196a8d18f/raw/9de742af6393df8ac61599aa27027c8801aa4a65/colors.sh' -P "${DEST}"
wget -qN 'https://gist.githubusercontent.com/meeDamian/fec388a943e0d4e64c876e6196a8d18f/raw/cf6d7804ffc424b2be93a224ed18c426f73680fe/lightningd.sh' -P "${DEST}"
wget -qN 'https://gist.githubusercontent.com/meeDamian/fec388a943e0d4e64c876e6196a8d18f/raw/97782561a08ebdc9635ab7584d8d2c82957b9bfe/lnd.sh' -P "${DEST}"
wget -qN 'https://gist.githubusercontent.com/meeDamian/fec388a943e0d4e64c876e6196a8d18f/raw/67306628651c2d3342df24cf7ad764141809306d/print.sh' -P "${DEST}"
wget -qN 'https://gist.githubusercontent.com/meeDamian/fec388a943e0d4e64c876e6196a8d18f/raw/a501bf1f76b7d891fb66a6ef3b27ab21391275af/utilities.sh' -P "${DEST}"
chmod -R +x "${DEST}/"
"${DEST}/print.sh"
#!/bin/sh
. /usr/local/lib/metrics/utilities.sh
# C-LIGHTNING
# -----------
#
# * assumes default ~/.lightning folder location
# * tries to detect lightning-cli path
# * tries to determine user running lightningd
# * whenever possible or makes sense the fn will try to cache the result
# Returns full path to lightning-cli
get_lightningd_binary_path() {
_cache "command -v lightning-cli"
}
# Returns user under which lightningd is running
get_lightningd_user() {
_cache "ps -eo user:10,command | grep \"[l]ightningd\" | awk '{ print \$1 }' | grep -v root | head -n 1"
}
# Returns full path to lightningd' data dir
get_lightningd_data_dir_path() {
user="$(get_lightningd_user)"
if [ -z "${user}" ]; then
return 1
fi
path="/home/${user}/.lightning"
if [ ! -d "${path}" ]; then
return 1
fi
echo "${path}"
}
# Returns exist code=0 if lightningd is available, or exit code=1 otherwise
is_lightningd_available() {
if [ -z "$(get_lightningd_binary_path)" ] || [ -z "$(get_lightningd_data_dir_path)" ]; then
return 1
fi
return 0
}
# Returns complete lightningd command
get_lightningd_full_cmd() {
if is_lightningd_available; [ "$?" -ne "0" ]; then
return 1
fi
echo "timeout 5s ${SUDO} $(get_lightningd_binary_path) --lightning-dir=$(get_lightningd_data_dir_path)" | tr -s " "
}
# Returns & caches the output of `getinfo` command
_cache_lightningd_getinfo() {
_cache_node "lightningd" "getinfo"
}
# Returns & caches the output of `listpeers` command
_cache_lightningd_listpeers() {
_cache_node "lightningd" "listpeers"
}
# Returns & caches the output of `listfunds` command
_cache_lightningd_listfunds() {
_cache_node "lightningd" "listfunds"
}
# Returns & caches the output of `listconfigs` command
_cache_lightningd_listconfigs() {
_cache_node "lightningd" "listconfigs"
}
# Returns node's user-defined alias
get_lightningd_alias() {
alias="$(_cache_lightningd_listconfigs | jq -r '.alias')"
if [ -z "${alias}" ]; then
return 1
fi
echo "${alias}"
}
# Returns chain on which lightningd instance is running: {mainnet,testnet,regnet}
get_lightningd_chain() {
chain="$(_cache_lightningd_getinfo | jq -r '.network')"
if [ -z "${chain}" ]; then
return 1
fi
if [ "${chain}" = "bitcoin" ]; then
chain="mainnet"
fi
echo "${chain}"
}
# Returns the amount of blocks on the currently followed chain
get_lightningd_blocks() {
blocks="$(_cache_lightningd_getinfo | jq -r '.blockheight')"
if [ -z "${blocks}" ]; then
return 1
fi
echo "${blocks}"
}
# Returns semver-like version of the running instance
get_lightningd_version() {
version="$(_cache_lightningd_getinfo | jq -r '.version')"
if [ -z "${version}" ]; then
return 1
fi
echo "${version}"
}
# Returns pubkey
get_lightningd_pubkey() {
pubkey="$(_cache_lightningd_getinfo | jq -r '.id')"
if [ -z "${pubkey}" ]; then
return 1
fi
echo "${pubkey}"
}
# Returns a tab-separated list of addresses under which lightningd should be reachable
get_lightningd_addresses() {
addresses="$(_cache_lightningd_getinfo | jq -r '[.address[] | [.address, .port|tostring] | join(":")] | join("\n")')"
if [ -z "${addresses}" ]; then
return 1
fi
echo "${addresses}"
}
get_lightningd_address() {
if [ "$1" != "ipv4" ] && [ "$1" != "ipv6" ] && [ "$1" != "torv2" ] && [ "$1" != "torv3" ]; then
return 1
fi
addresses="$(_cache_lightningd_getinfo | jq -r '[.address[] | select(.type == "'$1'") | [.address, .port|tostring] | join(":")] | join(" ")')"
if [ -z "${addresses}" ]; then
return 1
fi
echo "${addresses}"
}
# Returns the amount of peers the node is currently connected to
get_lightningd_peer_count() {
peers="$(_cache_lightningd_listpeers | jq '.peers | length')"
if [ -z "${peers}" ]; then
return 1
fi
echo "${peers}"
}
get_lightningd_channels_count_all() {
listpeers="$(_cache_lightningd_listpeers)"
if [ -z "${listpeers}" ]; then
return 1
fi
all="$(echo "${listpeers}" | jq -r '[.peers[].channels | select(. != null)] | length')"
echo "${all:-0}"
}
get_lightningd_channels_count_pending() {
STATES="OPENINGD CHANNELD_AWAITING_LOCKIN"
listpeers="$(_cache_lightningd_listpeers)"
if [ -z "${listpeers}" ]; then
return 1
fi
pending="$(echo "${listpeers}" | jq -r '.peers[].channels | select(. != null) | .[].state' | sort | uniq -c | grep "$(echo "${STATES}" | sed "s/ /\\\|/g")" | awk '{s += $1} END {print s}')"
echo "${pending:-0}"
}
get_lightningd_channels_count_open() {
STATES="CHANNELD_NORMAL"
listpeers="$(_cache_lightningd_listpeers)"
if [ -z "${listpeers}" ]; then
return 1
fi
open="$(echo "${listpeers}" | jq -r '.peers[].channels | select(. != null) | .[].state' | sort | uniq -c | grep "$(echo "${STATES}" | sed "s/ /\\\|/g")" | awk '{s += $1} END {print s}')"
echo "${open:-0}"
}
get_lightningd_channels_count_closing() {
STATES="CHANNELD_SHUTTING_DOWN CLOSINGD_SIGEXCHANGE CLOSINGD_COMPLETE FUNDING_SPEND_SEEN"
listpeers="$(_cache_lightningd_listpeers)"
if [ -z "${listpeers}" ]; then
return 1
fi
closing="$(echo "${listpeers}" | jq -r '.peers[].channels | select(. != null) | .[].state' | sort | uniq -c | grep "$(echo "${STATES}" | sed "s/ /\\\|/g")" | awk '{s += $1} END {print s}')"
echo "${closing:-0}"
}
get_lightningd_channels_count_other() {
NOT_STATES="OPENINGD CHANNELD_AWAITING_LOCKIN CHANNELD_NORMAL CHANNELD_SHUTTING_DOWN CLOSINGD_SIGEXCHANGE CLOSINGD_COMPLETE FUNDING_SPEND_SEEN"
listpeers="$(_cache_lightningd_listpeers)"
if [ -z "${listpeers}" ]; then
return 1
fi
other="$(echo "${listpeers}" | jq -r '.peers[].channels | select(. != null) | .[].state' | sort | uniq -c | grep -v "$(echo "${NOT_STATES}" | sed "s/ /\\\|/g")" | awk '{s += $1} END {print s}')"
echo "${other:-0}"
}
get_lightningd_funds_onchain() {
listfunds="$(_cache_lightningd_listfunds)"
if [ -z "${listfunds}" ]; then
return 1
fi
onchain="$(echo "${listfunds}" | jq '[.outputs[].value] | add | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${onchain}")"
return 0
fi
echo "${onchain}"
}
get_lightningd_funds_chan_spend_total() {
listfunds="$(_cache_lightningd_listfunds)"
if [ -z "${listfunds}" ]; then
return 1
fi
chan_spend_total="$(echo "${listfunds}" | jq '[.channels[].channel_sat] | add | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_spend_total}")"
return 0
fi
echo "${chan_spend_total}"
}
get_lightningd_funds_chan_spend_max() {
listfunds="$(_cache_lightningd_listfunds)"
if [ -z "${listfunds}" ]; then
return 1
fi
chan_spend_max="$(echo "${listfunds}" | jq '[.channels[].channel_sat] | max | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_spend_max}")"
return 0
fi
echo "${chan_spend_max}"
}
get_lightningd_funds_chan_receive_total() {
listfunds="$(_cache_lightningd_listfunds)"
if [ -z "${listfunds}" ]; then
return 1
fi
chan_receive_total="$(echo "${listfunds}" | jq '[.channels[] | .channel_total_sat - .channel_sat] | add | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_receive_total}")"
return 0
fi
echo "${chan_receive_total}"
}
get_lightningd_funds_chan_receive_max() {
listfunds="$(_cache_lightningd_listfunds)"
if [ -z "${listfunds}" ]; then
return 1
fi
chan_receive_max="$(echo "${listfunds}" | jq '[.channels[] | .channel_total_sat - .channel_sat] | max | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_receive_max}")"
return 0
fi
echo "${chan_receive_max}"
}
metrics_lightningd_print_all() {
echo "~~~ C-LIGHTNING ~~~"
_pair "path" "$(_handle_empty "$(get_lightningd_binary_path)")"
_pair "user" "$(_handle_empty "$(get_lightningd_user)")"
_pair "data" "$(_handle_empty "$(get_lightningd_data_dir_path)")"
_pair "avail" "$(is_lightningd_available && echo "yes" || echo "no")"
_pair "cmd" "$(_handle_empty "$(get_lightningd_full_cmd)")"
_pair "alias" "$(_handle_empty "$(get_lightningd_alias)")"
_pair "chain" "$(_handle_empty "$(get_lightningd_chain)")"
_pair "block" "$(_handle_empty "$(get_lightningd_blocks)")"
_pair "ver" "$(_handle_empty "$(get_lightningd_version)")"
_pair "pubkey" "$(_handle_empty "$(get_lightningd_pubkey)")"
_pair "addrs" "$(_handle_empty "$(get_lightningd_addresses | tr "\n" " ")")"
_pair "ipv4" "$(_handle_empty "$(get_lightningd_address ipv4)")"
_pair "ipv6" "$(_handle_empty "$(get_lightningd_address ipv6)")"
_pair "torv2" "$(_handle_empty "$(get_lightningd_address torv2)")"
_pair "torv3" "$(_handle_empty "$(get_lightningd_address torv3)")"
_pair "peers" "$(_handle_empty "$(get_lightningd_peer_count)")"
_pair "chan_all" "$(_handle_empty "$(get_lightningd_channels_count_all)")"
_pair "chan_pending" "$(_handle_empty "$(get_lightningd_channels_count_pending)")"
_pair "chan_open" "$(_handle_empty "$(get_lightningd_channels_count_open)")"
_pair "chan_closing" "$(_handle_empty "$(get_lightningd_channels_count_closing)")"
_pair "chan_other" "$(_handle_empty "$(get_lightningd_channels_count_other)")"
_print_all_monetary_fmts "funds_chain" "get_lightningd_funds_onchain"
_print_all_monetary_fmts "spendable_tot" "get_lightningd_funds_chan_spend_total"
_print_all_monetary_fmts "spendable_max" "get_lightningd_funds_chan_spend_max"
_print_all_monetary_fmts "receive_tot" "get_lightningd_funds_chan_receive_total"
_print_all_monetary_fmts "receive_max" "get_lightningd_funds_chan_receive_max"
}
#!/bin/sh
. /usr/local/lib/metrics/utilities.sh
# LND
# -----------
#
# * assumes default ~/.lnd folder location
# * tries to detect lncli path
# * tries to determine user running lnd
# * whenever possible or makes sense the fn will try to cache the result
# Returns full path to lncli
get_lnd_binary_path() {
_cache "command -v lncli"
}
# Returns user under which lnd is running
get_lnd_user() {
_cache "ps -eo user:10,command | grep \"[l]nd\" | awk '{ print \$1 }' | grep -v root | head -n 1"
}
# Returns full path to lnd' data dir
get_lnd_data_dir_path() {
user="$(get_lnd_user)"
if [ -z "${user}" ]; then
return 1
fi
path="/home/${user}/.lnd"
if [ ! -d "${path}" ]; then
return 1
fi
echo "${path}"
}
# Returns exist code=0 if lnd is available, or exit code=1 otherwise
is_lnd_available() {
if [ -z "$(get_lnd_binary_path)" ] || [ -z "$(get_lnd_data_dir_path)" ]; then
return 1
fi
return 0
}
# Returns complete lnd command
get_lnd_full_cmd() {
if is_lnd_available; [ "$?" -ne "0" ]; then
return 1
fi
# NOTE: starting with lnd version 0.5 `lncli` requires `-n={test,main,reg}net` param to be passed
# NOTE: this is a rather naive approach, but should work for now…
ver="$($(get_lnd_binary_path) --version | awk '{print $3}')"
major="$(echo ${ver} | cut -d '.' -f 1)"
minor="$(echo ${ver} | cut -d '.' -f 2)"
if [ "${major}" -ge "0" ] && [ "${minor}" -ge "5" ]; then
networks="mainnet testnet regnet"
for NETWORK in ${networks}; do
if [ -f "$(get_lnd_data_dir_path)/data/chain/bitcoin/${NETWORK}/admin.macaroon" ]; then
network_flag="--network=${NETWORK}"
break
fi
done
fi
echo "timeout 5s ${SUDO} $(get_lnd_binary_path) ${network_flag} --lnddir=$(get_lnd_data_dir_path)" | tr -s " "
}
# Returns & caches the output of `getinfo` command
_cache_lnd_getinfo() {
_cache_node "lnd" "getinfo"
}
# Returns & caches the output of `pendingchannels` command
_cache_lnd_pendingchannels() {
_cache_node "lnd" "pendingchannels"
}
# Returns & caches the output of `listchannels` command
_cache_lnd_listchannels() {
_cache_node "lnd" "listchannels"
}
# Returns & caches the output of `walletbalance` command
_cache_lnd_walletbalance() {
_cache_node "lnd" "walletbalance"
}
# Returns & caches the output of `walletbalance` command
_cache_lnd_fwdinghistory() {
_cache_node "lnd" "fwdinghistory --max_events=10000000 --start_time=1 --end_time=$(date +%s)"
}
# Returns node's user-defined alias
get_lnd_alias() {
alias="$(_cache_lnd_getinfo | jq -r '.alias')"
if [ -z "${alias}" ]; then
return 1
fi
echo "${alias}"
}
# Returns chain on which lnd instance is running: {mainnet,testnet}
get_lnd_chain() {
is_testnet="$(_cache_lnd_getinfo | jq -r '.testnet')"
if [ -z "${is_testnet}" ]; then
return 1
fi
chain="mainnet"
if [ "${is_testnet}" = "true" ]; then
chain="testnet"
fi
echo "${chain}"
}
# Returns the amount of blocks on the currently followed chain
get_lnd_blocks() {
blocks="$(_cache_lnd_getinfo | jq -r '.block_height')"
if [ -z "${blocks}" ]; then
return 1
fi
echo "${blocks}"
}
# Returns semver-like version of the running instance
get_lnd_version() {
version="$(_cache_lnd_getinfo | jq -r '.version' | cut -d ' ' -f 1)"
if [ -z "${version}" ]; then
return 1
fi
echo "${version}"
}
# Returns pubkey
get_lnd_pubkey() {
pubkey="$(_cache_lnd_getinfo | jq -r '.identity_pubkey')"
if [ -z "${pubkey}" ]; then
return 1
fi
echo "${pubkey}"
}
# Returns a tab-separated list of addresses under which lnd should be reachable
get_lnd_addresses() {
addresses="$(_cache_lnd_getinfo | jq -r '.uris | map(split ("@")[1]) | join("\n")')"
if [ -z "${addresses}" ]; then
return 1
fi
echo "${addresses}"
}
# get_lnd_address() {
# if [ "$1" != "ipv4" ] && [ "$1" != "ipv6" ] && [ "$1" != "torv2" ] && [ "$1" != "torv3" ]; then
# return 1
# fi
#
# addresses="$(_cache_lnd_getinfo | jq -r '[.address[] | select(.type == "'$1'") | [.address, .port|tostring] | join(":")] | join(" ")')"
# if [ -z "${addresses}" ]; then
# return 1
# fi
#
# echo "${addresses}"
# }
# Returns the amount of peers the node is currently connected to
get_lnd_peer_count() {
peers="$(_cache_lnd_getinfo | jq '.num_peers')"
if [ -z "${peers}" ]; then
return 1
fi
echo "${peers}"
}
get_lnd_channels_count_all() {
ch_pending="$(get_lnd_channels_count_pending)"
ch_open="$(get_lnd_channels_count_open)"
ch_closing="$(get_lnd_channels_count_closing)"
if [ "$?" -ne "0" ]; then
return 1
fi
echo $(( ${ch_pending:-0} + ${ch_open:-0} + ${ch_closing:-0} ))
}
get_lnd_channels_count_pending() {
pendingchannels="$(_cache_lnd_pendingchannels)"
if [ -z "${pendingchannels}" ]; then
return 1
fi
pending="$(echo "${pendingchannels}" | jq -r '.pending_open_channels | length')"
if [ "$?" -ne "0" ]; then
return 1
fi
echo "${pending:-0}"
}
get_lnd_channels_count_open() {
listchannels="$(_cache_lnd_listchannels)"
if [ -z "${listchannels}" ]; then
return 1
fi
open="$(echo "${listchannels}" | jq -r '.channels | length')"
echo "${open:-0}"
}
get_lnd_channels_count_closing() {
pendingchannels="$(_cache_lnd_pendingchannels)"
if [ -z "${pendingchannels}" ]; then
return 1
fi
closing="$(echo "${pendingchannels}" | jq -r '[.pending_closing_channels, .pending_force_closing_channels, .waiting_close_channels] | map(length) | add')"
echo "${closing:-0}"
}
get_lnd_relayed_txs_count() {
relays="$(_cache_lnd_fwdinghistory)"
if [ -z "${relays}" ]; then
return 1
fi
echo "${relays}" | jq '.forwarding_events | length | if . == null then 0 else . end'
}
get_lnd_relayed_txs_earned() {
relays="$(_cache_lnd_fwdinghistory)"
if [ -z "${relays}" ]; then
return 1
fi
earned="$(echo "${relays}" | jq '[.forwarding_events[].fee | tonumber] | add | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${earned}")"
return 0
fi
echo "${earned}"
}
get_lnd_funds_onchain() {
walletbalance="$(_cache_lnd_walletbalance)"
if [ -z "${walletbalance}" ]; then
return 1
fi
onchain="$(echo "${walletbalance}" | jq -r '.total_balance | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${onchain}")"
return 0
fi
echo "${onchain}"
}
get_lnd_funds_chan_spend_total() {
listchannels="$(_cache_lnd_listchannels)"
if [ -z "${listchannels}" ]; then
return 1
fi
chan_spend_total="$(echo "${listchannels}" | jq '[.channels[].local_balance] | map(tonumber) | add | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_spend_total}")"
return 0
fi
echo "${chan_spend_total}"
}
get_lnd_funds_chan_spend_max() {
listchannels="$(_cache_lnd_listchannels)"
if [ -z "${listchannels}" ]; then
return 1
fi
chan_spend_max="$(echo "${listchannels}" | jq '[.channels[].local_balance] | map(tonumber) | max | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_spend_max}")"
return 0
fi
echo "${chan_spend_max}"
}
get_lnd_funds_chan_receive_total() {
listchannels="$(_cache_lnd_listchannels)"
if [ -z "${listchannels}" ]; then
return 1
fi
chan_receive_total="$(echo "${listchannels}" | jq '[.channels[] | .remote_balance] | map(tonumber) | add | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_receive_total}")"
return 0
fi
echo "${chan_receive_total}"
}
get_lnd_funds_chan_receive_max() {
listchannels="$(_cache_lnd_listchannels)"
if [ -z "${listchannels}" ]; then
return 1
fi
chan_receive_max="$(echo "${listchannels}" | jq '[.channels[] | .remote_balance] | map(tonumber) | max | if . == null then 0 else . end')"
if [ "$1" = "btc" ]; then
printf "%0.8f" "$(sat_to_btc "${chan_receive_max}")"
return 0
fi
echo "${chan_receive_max}"
}
metrics_lnd_print_all() {
echo "~~~ LND ~~~"
_pair "path" "$(_handle_empty "$(get_lnd_binary_path)")"
_pair "user" "$(_handle_empty "$(get_lnd_user)")"
_pair "data" "$(_handle_empty "$(get_lnd_data_dir_path)")"
_pair "avail" "$(is_lnd_available && echo "yes" || echo "no")"
_pair "cmd" "$(_handle_empty "$(get_lnd_full_cmd)")"
_pair "alias" "$(_handle_empty "$(get_lnd_alias)")"
_pair "chain" "$(_handle_empty "$(get_lnd_chain)")"
_pair "block" "$(_handle_empty "$(get_lnd_blocks)")"
_pair "ver" "$(_handle_empty "$(get_lnd_version)")"
_pair "pubkey" "$(_handle_empty "$(get_lnd_pubkey)")"
_pair "addrs" "$(_handle_empty "$(get_lnd_addresses | tr "\n" " ")")"
# _pair "ipv4" "$(_handle_empty "$(get_lnd_address ipv4)")"
# _pair "ipv6" "$(_handle_empty "$(get_lnd_address ipv6)")"
# _pair "torv2" "$(_handle_empty "$(get_lnd_address torv2)")"
# _pair "torv3" "$(_handle_empty "$(get_lnd_address torv3)")"
_pair "peers" "$(_handle_empty "$(get_lnd_peer_count)")"
_pair "chan_all" "$(_handle_empty "$(get_lnd_channels_count_all)")"
_pair "chan_pending" "$(_handle_empty "$(get_lnd_channels_count_pending)")"
_pair "chan_open" "$(_handle_empty "$(get_lnd_channels_count_open)")"
_pair "chan_closing" "$(_handle_empty "$(get_lnd_channels_count_closing)")"
_pair "relay_count" "$(_handle_empty "$(get_lnd_relayed_txs_count)")"
_print_all_monetary_fmts "relay_earn" "get_lnd_relayed_txs_earned"
_print_all_monetary_fmts "funds_chain" "get_lnd_funds_onchain"
_print_all_monetary_fmts "spendable_tot" "get_lnd_funds_chan_spend_total"
_print_all_monetary_fmts "spendable_max" "get_lnd_funds_chan_spend_max"
_print_all_monetary_fmts "receive_tot" "get_lnd_funds_chan_receive_total"
_print_all_monetary_fmts "receive_max" "get_lnd_funds_chan_receive_max"
}
#!/bin/sh
# GOALS OF THIS SCRIPT:
# * create minimal functions that return data in predictable format
# * make sure that whatever makes sense to be cached, is cached
scope="$1"
if [ -z "${scope}" ]; then
scope="all"
fi
if [ "${scope}" = "board" ] || [ "${scope}" = "all" ]; then
. /usr/local/lib/metrics/board.sh
metrics_board_print_all
fi
if [ "${scope}" = "bitcoind" ] || [ "${scope}" = "all" ]; then
. /usr/local/lib/metrics/bitcoind.sh
echo
metrics_bitcoind_print_all
fi
if [ "${scope}" = "lightningd" ] || [ "${scope}" = "all" ]; then
. /usr/local/lib/metrics/lightningd.sh
echo
metrics_lightningd_print_all
fi
if [ "${scope}" = "lnd" ] || [ "${scope}" = "all" ]; then
. /usr/local/lib/metrics/lnd.sh
echo
metrics_lnd_print_all
fi
#!/bin/sh
# UTILITY FUNCTIONS
# =================
# Returns $1, if available or `-`, if not
_handle_empty() {
[ -z "$1" ] && echo "-" || echo "$1"
}
# Formats $1/key and $2/value for display purposes
_pair() {
echo "$1: $2"
}
# Formats line to a format:
# $1<tab>$2:<tab>to_num($3)<tab>to_human_readeable($3)
_stor_mem_format() {
val="-"
if [ ! -z "$3" ]; then
val="$(echo ${3} | numfmt) $(echo "${3}" | numfmt --to=si)"
fi
_pair "$1 $2" "${val}"
}
_print_all_monetary_fmts() {
btc="$($2 btc)"
if [ "$?" -ne "0" ]; then
_pair "$1" "-"
return 1
fi
sat="$($2)"
_pair "$1" "$(_handle_empty ${btc}) $(_handle_empty "$(fmt_simplify "${btc}")") $(_handle_empty ${sat})"
}
_cache() {
_CACHE_PATH="/tmp/metrics/$(whoami)"
if [ -z "$1" ]; then
return 1
fi
cmd="$1"
if [ ! -z "$2" ]; then
cmd="${cmd} $2"
fi
filename="$(echo "${cmd}" | sed "s/[^a-zA-Z0-9]/_/g" | tr -s '_')"
full_path="${_CACHE_PATH}/${filename}"
if [ ! -f "${full_path}" ] || [ ! -z "$(find ${full_path} -mmin +1)" ]; then
mkdir -p "${_CACHE_PATH}"
if result="$(eval "${cmd} 2>&1")"; then
out="${result}"
else
logger -t metrics "${result}"
return 1
fi
echo "${out}" > ${full_path}
chmod 777 "$full_path"
fi
echo "$(cat ${full_path})"
}
_cache_node() {
if [ -z "$1" ]; then
return 1
fi
cmd="$(get_$1_full_cmd)"
if [ "$?" -ne "0" ]; then
return 1
fi
_cache "${cmd}" "$2"
}
# Determines `sudo` capabilities, and returns prefix accordingly:
# if `root` - no sudo prefix necessary
# if user can do `sudo` - return prefix for non-interactive sudo
# if user can't do `sudo` - return `exit 1 &&` as prefix to quickly terminate command that would fail anyway
#
# Usage:
# Prepend ${SUDO} to a command that needs `sudo` privileges
_sudo_prefix() {
if [ "$(id -u)" -eq "0" ]; then
return 0
fi
if sudo -n true 2> /dev/null; [ "$?" -eq "0" ]; then
echo "sudo -n"
return 0
fi
logger -t metrics "can't grant sudo to $(whoami)"
echo "exit 1 &&"
}
SUDO="$(_sudo_prefix)"
btc_to_sat() {
echo "$1" | jq '. * 1e8'
}
sat_to_btc() {
echo "$1" | jq '. / 1e8'
}
fmt_simplify() {
echo "$1" | jq '.'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment