Commit 357eb383 authored by Vitaly Lipatov's avatar Vitaly Lipatov

Add route export API, telemt metrics, chat-dns split views, massdns CNAME fallback

route-web-api.py: - /api/export endpoint: filter by group/list/proto, text/mikrotik/json formats - /api/export/groups: list available groups with counts - aggregate=1 (exact) / aggregate=2 (approx via mapcidr) - Multiple groups support (group=gre,egw,zapret) - Resolved data from /var/lib/etersoft-router/state/ with in-memory cache (60s TTL) - Speed check: don't early-stop before checking first 2 gateways route-update.sh: - umask 022 for readable state files - chmod g+r on resolved after write - Per-list duration tracking (duration file in state) functions: - CNAME fallback: parallel dig (xargs -P 20) instead of sequential route-stats-metrics.sh: - New: collect route list counts and push to InfluxDB - Per-list duration metrics - Route-update total duration dns/chat-dns.sh: - Rewritten for split-view subzone chat.eterfund.ru via SSH to ns1 dns/telemt-metrics.sh: - Added upstream_success/fail/slow and handshake_timeout metrics Co-Authored-By: 's avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
parent f1216eb0
#!/bin/bash
# chat-dns.sh — manage A records for chat.eterfund.ru
# Run on ns1. Usage: chat-dns.sh on|off|status
# Usage: chat-dns.sh on|off|status
#
# on — add all three A records (round-robin)
# off — keep only the primary IP
# on — add all three A records (round-robin) for external view
# off — keep only the primary IP for external view
# status — show current A records
#
# Trusted view (office) always returns only primary IP.
# Zone chat.eterfund.ru is delegated from eterfund.ru with split views on ns1.
DOMAIN="chat.eterfund.ru"
ZONE="eterfund.ru"
TTL=300
NS="ns1.etersoft.ru"
NS_SSH="ssh -p32 root@$NS"
ZONE_FILE="/var/lib/bind/zone/chat.eterfund.ru.all"
PRIMARY="91.232.225.3"
EXTRA_IPS="95.47.184.52 217.12.37.55"
DIV="95.47.184.52"
BEGET="217.12.37.55"
usage() {
echo "Usage: $0 on|off|status"
echo " on — set all three A records (round-robin)"
echo " off — keep only primary ($PRIMARY)"
echo " on — set all three A records (round-robin, external only)"
echo " off — keep only primary ($PRIMARY, external)"
echo " status — show current A records"
exit 1
}
dns_status() {
echo "Current A records for $DOMAIN:"
echo " external (view all):"
dig +short "$DOMAIN" A @"$NS"
echo " office (view trusted):"
dig +short "$DOMAIN" A
}
inc_serial='
old=$(grep -oP "\d{10}(?=\s*;\s*serial)" '"$ZONE_FILE"')
today=$(date +%Y%m%d)
if [ "${old:0:8}" = "$today" ]; then
nn=$(printf "%02d" $(( ${old:8:2} + 1 )))
else
nn="01"
fi
new="${today}${nn}"
sed -i "s/$old/$new/" '"$ZONE_FILE"'
echo "Serial: $old -> $new"
'
dns_on() {
echo "Adding all A records for $DOMAIN..."
nsupdate -l <<EOF
zone $ZONE
update delete $DOMAIN. A
update add $DOMAIN. $TTL A $PRIMARY
update add $DOMAIN. $TTL A 95.47.184.52
update add $DOMAIN. $TTL A 217.12.37.55
send
EOF
echo "Adding all A records for $DOMAIN (external view)..."
$NS_SSH "
cat > $ZONE_FILE << 'ZONE'
\\\$TTL 300
@ IN SOA ns1.etersoft.ru. support.etersoft.ru. (
2026033101 ; serial
1H ; refresh
5M ; retry
1W ; expire
5M ; minimum
)
IN NS ns1.etersoft.ru.
IN A $PRIMARY
IN A $DIV
IN A $BEGET
ZONE
$inc_serial
cd /var/lib/bind/zone && named-checkzone $DOMAIN chat.eterfund.ru.all 2>&1
rndc reload $DOMAIN IN all 2>&1
cd /var/lib/bind && git add zone/chat.eterfund.ru.all && git commit -m 'chat.eterfund.ru: round-robin on (3 IPs)'
"
if [ $? -eq 0 ]; then
echo "Done. All three IPs active."
echo "Done. All three IPs active (external)."
else
echo "nsupdate failed. Check allow-update in zone config." >&2
echo "Failed." >&2
exit 1
fi
dns_status
}
dns_off() {
echo "Removing extra A records, keeping only $PRIMARY..."
nsupdate -l <<EOF
zone $ZONE
update delete $DOMAIN. A
update add $DOMAIN. $TTL A $PRIMARY
send
EOF
echo "Removing extra A records, keeping only $PRIMARY (external view)..."
$NS_SSH "
cat > $ZONE_FILE << 'ZONE'
\\\$TTL 300
@ IN SOA ns1.etersoft.ru. support.etersoft.ru. (
2026033101 ; serial
1H ; refresh
5M ; retry
1W ; expire
5M ; minimum
)
IN NS ns1.etersoft.ru.
IN A $PRIMARY
ZONE
$inc_serial
cd /var/lib/bind/zone && named-checkzone $DOMAIN chat.eterfund.ru.all 2>&1
rndc reload $DOMAIN IN all 2>&1
cd /var/lib/bind && git add zone/chat.eterfund.ru.all && git commit -m 'chat.eterfund.ru: primary only'
"
if [ $? -eq 0 ]; then
echo "Done. Only primary IP active."
echo "Done. Only primary IP active (external)."
else
echo "nsupdate failed." >&2
echo "Failed." >&2
exit 1
fi
dns_status
......
#!/bin/bash
# telemt-metrics.sh — collect telemt metrics and push to InfluxDB
# Install on each telemt server via cron: * * * * * /path/to/telemt-metrics.sh
# Requires: curl
INFLUX_URL="http://telegraf.office.etersoft.ru:8086"
INFLUX_DB="gateways"
METRICS_URL="http://127.0.0.1:9090/metrics"
HOST=$(hostname -s)
raw=$(curl -sf --max-time 5 "$METRICS_URL") || exit 0
connections_total=$(echo "$raw" | awk '/^telemt_connections_total / {print $2}')
connections_bad=$(echo "$raw" | awk '/^telemt_connections_bad_total / {print $2}')
connections_current=$(echo "$raw" | awk '/^telemt_user_connections_current\{/ {print $2}')
octets_in=$(echo "$raw" | awk '/^telemt_user_octets_from_client\{/ {print $2}')
octets_out=$(echo "$raw" | awk '/^telemt_user_octets_to_client\{/ {print $2}')
unique_ips=$(echo "$raw" | awk '/^telemt_user_unique_ips_current\{/ {print $2}')
uptime=$(echo "$raw" | awk '/^telemt_uptime_seconds / {printf "%.0f", $2}')
upstream_success=$(echo "$raw" | awk '/^telemt_upstream_connect_success_total / {print $2}')
upstream_fail=$(echo "$raw" | awk '/^telemt_upstream_connect_fail_total / {print $2}')
upstream_slow=$(echo "$raw" | awk '/^telemt_upstream_connect_duration_success_total\{bucket="gt_1000ms"\}/ {print $2}')
handshake_timeout=$(echo "$raw" | awk '/^telemt_handshake_timeouts_total / {print $2}')
# InfluxDB line protocol
line="telemt,host=$HOST connections_total=${connections_total:-0}i,connections_bad=${connections_bad:-0}i,connections_current=${connections_current:-0}i,octets_in=${octets_in:-0}i,octets_out=${octets_out:-0}i,unique_ips=${unique_ips:-0}i,uptime=${uptime:-0}i,upstream_success=${upstream_success:-0}i,upstream_fail=${upstream_fail:-0}i,upstream_slow=${upstream_slow:-0}i,handshake_timeout=${handshake_timeout:-0}i"
curl -sf --max-time 5 -XPOST "$INFLUX_URL/write?db=$INFLUX_DB" --data-binary "$line"
......@@ -164,14 +164,16 @@ get_ipv4_list_bulk()
print $2
}'
# Fallback to dig for CNAME domains (skip nxdomain/nodata/timeout)
# Fallback for CNAME domains: parallel dig
local cname_domains=$(mktemp)
grep "^;" "$adns_out" | grep -v 'nxdomain\|nodata\|timeout\|querydomaintoolong' | \
awk '{ for(i=4;i<=NF;i++) if($i ~ /^[a-z0-9].*\.[a-z]/) { print $i; break } }' | \
sort -u | while read domain ; do
echo
echo "# $domain (dig fallback)"
get_ipv4_list "$domain"
done
sort -u > "$cname_domains"
if [ -s "$cname_domains" ] ; then
xargs -P 20 -I{} dig +short {} A < "$cname_domains" 2>/dev/null
[ -n "$EXTRA_DNS" ] && xargs -P 20 -I{} dig @$EXTRA_DNS +short {} A < "$cname_domains" 2>/dev/null || true
fi
rm -f "$cname_domains"
}
# Bulk resolve domains to IPv6 via adnshost (async) with dig fallback for CNAMEs
......@@ -207,14 +209,20 @@ get_ipv6_list_bulk()
print $2
}'
# Fallback to dig for CNAME domains (skip nxdomain/nodata/timeout)
# Fallback for CNAME domains via massdns (fast parallel resolver)
local cname_domains=$(mktemp)
grep "^;" "$adns_out" | grep -v 'nxdomain\|nodata\|timeout\|querydomaintoolong' | \
awk '{ for(i=4;i<=NF;i++) if($i ~ /^[a-z0-9].*\.[a-z]/) { print $i; break } }' | \
sort -u | while read domain ; do
echo
echo "# $domain (dig fallback)"
get_ipv6_list "$domain"
done
sort -u > "$cname_domains"
if [ -s "$cname_domains" ] ; then
local resolvers=$(mktemp)
awk '/^nameserver/ {print $2}' /etc/resolv.conf > "$resolvers"
[ -n "$EXTRA_DNS" ] && echo "$EXTRA_DNS" >> "$resolvers"
massdns -r "$resolvers" -t AAAA -o S --quiet < "$cname_domains" 2>/dev/null | \
awk '/ AAAA / { sub(/\.$/, "", $3); print $3 }'
rm -f "$resolvers"
fi
rm -f "$cname_domains"
}
# Compute covering IPv6 subnets from a list of addresses (stdin).
......
#!/bin/bash
# route-stats-metrics.sh — collect route list stats and push to InfluxDB
# Install on igw via cron: * * * * * /root/etersoft-admin-essential/router/route-stats-metrics.sh
# Requires: curl
INFLUX_URL="http://telegraf.office.etersoft.ru:8086"
INFLUX_DB="gateways"
BASEDIR="/root/etersoft-admin-essential/router"
HOST=$(hostname -s)
lines=""
for base in routes.d routes6.d; do
basedir="$BASEDIR/$base"
[ -d "$basedir" ] || continue
case "$base" in
routes.d) proto="ipv4" ;;
routes6.d) proto="ipv6" ;;
esac
for dir in "$basedir"/*/; do
[ -d "$dir" ] || continue
group=$(basename "$dir")
group_total=0
for f in "$dir"*.list; do
[ -f "$f" ] || continue
name=$(basename "$f" .list)
count=$(wc -l < "$f")
group_total=$((group_total + count))
lines="${lines}route_lists,host=$HOST,proto=$proto,group=$group,list=$name count=${count}i
"
done
lines="${lines}route_lists,host=$HOST,proto=$proto,group=$group,list=_total count=${group_total}i
"
done
done
[ -z "$lines" ] && exit 0
# route-update total duration
duration_file="$BASEDIR/../route-web-api/update-duration"
if [ -f "$duration_file" ]; then
duration=$(cat "$duration_file" 2>/dev/null)
[ -n "$duration" ] && lines="${lines}route_update,host=$HOST duration=${duration}i
"
fi
# per-list duration
STATE_DIR="/var/lib/etersoft-router/state"
for subdir in routes.d routes6.d; do
state_base="$STATE_DIR/$subdir"
[ -d "$state_base" ] || continue
case "$subdir" in
routes.d) sproto="ipv4" ;;
routes6.d) sproto="ipv6" ;;
esac
for gdir in "$state_base"/*/; do
[ -d "$gdir" ] || continue
sgroup=$(basename "$gdir")
for ldir in "$gdir"*/; do
dur_file="$ldir/duration"
[ -f "$dur_file" ] || continue
slist=$(basename "$ldir")
dur=$(cat "$dur_file" 2>/dev/null)
[ -n "$dur" ] && lines="${lines}route_list_duration,host=$HOST,proto=$sproto,group=$sgroup,list=$slist duration=${dur}i
"
done
done
done
echo -n "$lines" | curl -sf --max-time 5 -XPOST "$INFLUX_URL/write?db=$INFLUX_DB" --data-binary @-
#!/bin/sh
# Unified route updater: reads directory-based config from routes.d/ and routes6.d/
umask 022
# Each subdirectory = route group with gateway/table files and .list symlinks
#
# Usage: route-update.sh [--force] [--resolve] [--show] [--verbose]
......@@ -808,6 +809,7 @@ for line in sys.stdin:
echo "$_pref" > "$STATE_DIR/$_state/pref"
cp "$_gwdir/gateway" "$STATE_DIR/$_state/gateway"
mv "$_resolved_new" "$STATE_DIR/$_state/resolved"
chmod g+r "$STATE_DIR/$_state/resolved" 2>/dev/null
return 0
}
......@@ -883,8 +885,12 @@ process_group()
fi
check_list_changed || continue
local _list_start=$(date +%s)
resolve_list_file
load_list_routes && log "$_tag$_label Done"
load_list_routes
local _list_duration=$(( $(date +%s) - _list_start ))
echo "$_list_duration" > "$STATE_DIR/$_state/duration"
log "$_tag$_label Done in ${_list_duration}s"
done
}
......@@ -1167,4 +1173,5 @@ generate_web_json
_duration=$(( $(date +%s) - _start_time ))
[ -n "$SHOW" ] || echo "$_duration" > "$DURATION_FILE" 2>/dev/null
[ -n "$SHOW" ] || chmod -R g+rX "$STATE_DIR" 2>/dev/null
[ -n "$SHOW" ] || log "All done in ${_duration}s"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment