ovtools 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +121 -2
- package/assets/common/bond-inspect.sh +368 -0
- package/assets/common/config.sh +263 -0
- package/assets/common/extract-data.sh +197 -0
- package/assets/common/harden-host.sh +37 -0
- package/assets/common/install-deps.sh +93 -0
- package/assets/common/node-enroll.sh +65 -0
- package/assets/common/node-setup.sh +150 -0
- package/assets/common/node-start.sh +90 -0
- package/assets/common/node-stop.sh +18 -0
- package/assets/common/node-upgrade.sh +45 -0
- package/assets/common/pre-run.sh +497 -0
- package/assets/common/promote-validator.sh +104 -0
- package/assets/deploy/10-prepare-host.sh +98 -0
- package/assets/deploy/20-install-toolchain.sh +93 -0
- package/assets/deploy/30-setup-node.sh +82 -0
- package/assets/deploy/40-start-node.sh +75 -0
- package/assets/deploy/50-verify.sh +259 -0
- package/assets/deploy/60-enroll.sh +304 -0
- package/assets/deploy/70-promote.sh +152 -0
- package/assets/deploy/lib.sh +398 -0
- package/assets/manage/10-bond-increase.sh +149 -0
- package/assets/manage/10-diagnose-node.sh +169 -0
- package/assets/manage/20-exit-validator.sh +259 -0
- package/assets/manage/25-recover-node.sh +291 -0
- package/assets/manage/30-status.sh +64 -0
- package/assets/manage/40-exit-withdraw.sh +133 -0
- package/assets/manage/bond_inc.sh +318 -0
- package/assets/manage/dashboard_monitor/app.js +1544 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/base.css +113 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/content.css +133 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/controls.css +229 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/data.css +374 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/icons.css +35 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/motion.css +57 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/shell.css +51 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/sidebar.css +242 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/topbar.css +138 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/fonts.css +30 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/synteq-ui-v2.css +25 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/theme-dark.css +38 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/theme-light.css +36 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/tokens.css +61 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-Medium.ttf +0 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-Regular.ttf +0 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-SemiBold.ttf +0 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/js/synteq-ui-v2.js +869 -0
- package/assets/manage/dashboard_monitor/deploy-dashboard.sh +230 -0
- package/assets/manage/dashboard_monitor/favicon.svg +13 -0
- package/assets/manage/dashboard_monitor/index.html +1197 -0
- package/assets/manage/dashboard_monitor/nginx-octra.conf +76 -0
- package/assets/manage/dashboard_monitor/octra-dashboard.service +29 -0
- package/assets/manage/dashboard_monitor/octra-node-prune.cron +12 -0
- package/assets/manage/dashboard_monitor/octra-node-prune.service +16 -0
- package/assets/manage/dashboard_monitor/octra-node-prune.timer +12 -0
- package/assets/manage/dashboard_monitor/prune-node-junk.sh +329 -0
- package/assets/manage/dashboard_monitor/server.py +1887 -0
- package/assets/manage/dashboard_monitor/ssl-certbot.sh +90 -0
- package/assets/manage/dashboard_monitor/styles.css +327 -0
- package/assets/manage/lib.sh +347 -0
- package/assets/manifest.json +143 -0
- package/assets/migrate/00-backup-identity.sh +67 -0
- package/assets/migrate/01-prepare-target.sh +145 -0
- package/assets/migrate/02-authorize-pull.sh +43 -0
- package/assets/migrate/03-pull-state.sh +102 -0
- package/assets/migrate/04-stop-source.sh +85 -0
- package/assets/migrate/05-finalize-target.sh +140 -0
- package/assets/migrate/06-start-target.sh +57 -0
- package/assets/migrate/07-verify.sh +237 -0
- package/assets/migrate/08-rollback.sh +54 -0
- package/assets/migrate/lib.sh +149 -0
- package/assets/upgrade/10-preflight.sh +120 -0
- package/assets/upgrade/20-upgrade.sh +142 -0
- package/assets/upgrade/30-manual-upgrade.sh +129 -0
- package/assets/upgrade/40-storage-prune.sh +65 -0
- package/assets/upgrade/50-status.sh +58 -0
- package/assets/upgrade/lib.sh +232 -0
- package/bin/ovtools.js +32 -0
- package/dist/api/checks.d.ts +6 -0
- package/dist/api/checks.d.ts.map +1 -0
- package/dist/api/checks.js +173 -0
- package/dist/api/checks.js.map +1 -0
- package/dist/api/hostkeys.d.ts +10 -0
- package/dist/api/hostkeys.d.ts.map +1 -0
- package/dist/api/hostkeys.js +58 -0
- package/dist/api/hostkeys.js.map +1 -0
- package/dist/api/jobs.d.ts +33 -0
- package/dist/api/jobs.d.ts.map +1 -0
- package/dist/api/jobs.js +483 -0
- package/dist/api/jobs.js.map +1 -0
- package/dist/api/nodes.d.ts +16 -0
- package/dist/api/nodes.d.ts.map +1 -0
- package/dist/api/nodes.js +126 -0
- package/dist/api/nodes.js.map +1 -0
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +443 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +301 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +76 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +150 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/base.d.ts +52 -0
- package/dist/orchestrator/base.d.ts.map +1 -0
- package/dist/orchestrator/base.js +219 -0
- package/dist/orchestrator/base.js.map +1 -0
- package/dist/orchestrator/deploy.d.ts +33 -0
- package/dist/orchestrator/deploy.d.ts.map +1 -0
- package/dist/orchestrator/deploy.js +313 -0
- package/dist/orchestrator/deploy.js.map +1 -0
- package/dist/orchestrator/manage.d.ts +28 -0
- package/dist/orchestrator/manage.d.ts.map +1 -0
- package/dist/orchestrator/manage.js +267 -0
- package/dist/orchestrator/manage.js.map +1 -0
- package/dist/orchestrator/migrate.d.ts +31 -0
- package/dist/orchestrator/migrate.d.ts.map +1 -0
- package/dist/orchestrator/migrate.js +442 -0
- package/dist/orchestrator/migrate.js.map +1 -0
- package/dist/ssh/client.d.ts +39 -0
- package/dist/ssh/client.d.ts.map +1 -0
- package/dist/ssh/client.js +418 -0
- package/dist/ssh/client.js.map +1 -0
- package/dist/test/core.test.d.ts +2 -0
- package/dist/test/core.test.d.ts.map +1 -0
- package/dist/test/core.test.js +188 -0
- package/dist/test/core.test.js.map +1 -0
- package/dist/types.d.ts +267 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/assets.d.ts +28 -0
- package/dist/utils/assets.d.ts.map +1 -0
- package/dist/utils/assets.js +583 -0
- package/dist/utils/assets.js.map +1 -0
- package/dist/utils/browser.d.ts +2 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +23 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/dashboard.d.ts +16 -0
- package/dist/utils/dashboard.d.ts.map +1 -0
- package/dist/utils/dashboard.js +177 -0
- package/dist/utils/dashboard.js.map +1 -0
- package/dist/utils/helpers.d.ts +29 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +204 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/scripts.d.ts +60 -0
- package/dist/utils/scripts.d.ts.map +1 -0
- package/dist/utils/scripts.js +174 -0
- package/dist/utils/scripts.js.map +1 -0
- package/dist/utils/state.d.ts +12 -0
- package/dist/utils/state.d.ts.map +1 -0
- package/dist/utils/state.js +44 -0
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/tar.d.ts +11 -0
- package/dist/utils/tar.d.ts.map +1 -0
- package/dist/utils/tar.js +93 -0
- package/dist/utils/tar.js.map +1 -0
- package/dist/utils/update.d.ts +9 -0
- package/dist/utils/update.d.ts.map +1 -0
- package/dist/utils/update.js +134 -0
- package/dist/utils/update.js.map +1 -0
- package/dist/utils/websocket.d.ts +10 -0
- package/dist/utils/websocket.d.ts.map +1 -0
- package/dist/utils/websocket.js +203 -0
- package/dist/utils/websocket.js.map +1 -0
- package/package.json +55 -4
- package/public/app.js +2712 -0
- package/public/assets/v2.2/css/base.css +113 -0
- package/public/assets/v2.2/css/components/content.css +133 -0
- package/public/assets/v2.2/css/components/controls.css +229 -0
- package/public/assets/v2.2/css/components/data.css +373 -0
- package/public/assets/v2.2/css/components/icons.css +35 -0
- package/public/assets/v2.2/css/components/motion.css +57 -0
- package/public/assets/v2.2/css/components/shell.css +51 -0
- package/public/assets/v2.2/css/components/sidebar.css +242 -0
- package/public/assets/v2.2/css/components/topbar.css +138 -0
- package/public/assets/v2.2/css/fonts.css +30 -0
- package/public/assets/v2.2/css/synteq-ui-v2.css +25 -0
- package/public/assets/v2.2/css/theme-dark.css +38 -0
- package/public/assets/v2.2/css/theme-light.css +36 -0
- package/public/assets/v2.2/css/tokens.css +61 -0
- package/public/assets/v2.2/fonts/GoogleSansCode-Medium.ttf +0 -0
- package/public/assets/v2.2/fonts/GoogleSansCode-Regular.ttf +0 -0
- package/public/assets/v2.2/fonts/GoogleSansCode-SemiBold.ttf +0 -0
- package/public/assets/v2.2/js/synteq-ui-v2.js +869 -0
- package/public/favicon.svg +7 -0
- package/public/index.html +1080 -0
- package/public/styles.css +552 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 07-verify.sh - prove the migrated node is a live voting validator
|
|
4
|
+
#
|
|
5
|
+
# Checks (all must pass):
|
|
6
|
+
# process = online, rpc = ready
|
|
7
|
+
# role = validator, voting = enabled
|
|
8
|
+
# validator_active = true, validator_enrollment = ready
|
|
9
|
+
# epoch advances between samples (node follows the network head)
|
|
10
|
+
# p2p_connected > 0
|
|
11
|
+
#
|
|
12
|
+
# Usage (on the NEW vps):
|
|
13
|
+
# EXPECTED_ADDRESS=oct... bash 07-verify.sh # 3 samples / 30s
|
|
14
|
+
# SAMPLES=10 INTERVAL=30 bash 07-verify.sh # longer watch
|
|
15
|
+
# ===================================================================
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
source "$(dirname "$0")/lib.sh"
|
|
19
|
+
|
|
20
|
+
require_root
|
|
21
|
+
SAMPLES="${SAMPLES:-3}"
|
|
22
|
+
INTERVAL="${INTERVAL:-30}"
|
|
23
|
+
EXPECTED_ADDRESS="${EXPECTED_ADDRESS:-}"
|
|
24
|
+
if [[ -z "${EXPECTED_ADDRESS}" && -s "${MIG_DIR}/source-address.txt" ]]; then
|
|
25
|
+
EXPECTED_ADDRESS=$(cat "${MIG_DIR}/source-address.txt")
|
|
26
|
+
fi
|
|
27
|
+
if [[ -z "${EXPECTED_ADDRESS}" && -f "${MIG_DIR}/source-final-stat.txt" ]]; then
|
|
28
|
+
EXPECTED_ADDRESS=$(grep -m1 '^address = ' "${MIG_DIR}/source-final-stat.txt" | awk -F' = ' '{print $2}' || true)
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
set_status RUNNING "verify"
|
|
32
|
+
log "=== verifying validator on $(hostname) - ${SAMPLES} samples every ${INTERVAL}s ==="
|
|
33
|
+
|
|
34
|
+
# --- wait for the node to finish opening its store -------------------
|
|
35
|
+
# A freshly started node must open a ~38 GB irmin-pack store, replay the
|
|
36
|
+
# epochlog/rule graph and rejoin consensus before its RPC answers. On 2
|
|
37
|
+
# vCPU that takes minutes, so poll instead of failing straight away.
|
|
38
|
+
WAIT_SEC="${WAIT_SEC:-900}"
|
|
39
|
+
DEADLINE=$(( $(date +%s) + WAIT_SEC ))
|
|
40
|
+
WAIT_START=$(date +%s)
|
|
41
|
+
LAST_LOG=$WAIT_START
|
|
42
|
+
log "waiting for rpc = ready (up to ${WAIT_SEC}s)..."
|
|
43
|
+
while (( $(date +%s) < DEADLINE )); do
|
|
44
|
+
SNAP=$(node_stat)
|
|
45
|
+
RPC_NOW=$(printf '%s\n' "${SNAP}" | stat_field rpc)
|
|
46
|
+
PROC_NOW=$(printf '%s\n' "${SNAP}" | stat_field process)
|
|
47
|
+
HEAD_NOW=$(printf '%s\n' "${SNAP}" | stat_field head_epoch)
|
|
48
|
+
if [[ "${RPC_NOW}" == "ready" ]]; then
|
|
49
|
+
log "rpc ready after $(( $(date +%s) - WAIT_START ))s (head_epoch=${HEAD_NOW})"
|
|
50
|
+
break
|
|
51
|
+
fi
|
|
52
|
+
# One line a minute instead of a carriage-return animation: every \r update
|
|
53
|
+
# arrives as its own log row in the console, which buried the real steps.
|
|
54
|
+
if (( $(date +%s) - LAST_LOG >= 60 )); then
|
|
55
|
+
log " waiting for rpc: $(( $(date +%s) - WAIT_START ))s of ${WAIT_SEC}s (rpc=${RPC_NOW:-?} process=${PROC_NOW:-?} head=${HEAD_NOW:-?})"
|
|
56
|
+
LAST_LOG=$(date +%s)
|
|
57
|
+
fi
|
|
58
|
+
sleep 10
|
|
59
|
+
done
|
|
60
|
+
RPC_NOW=$(node_stat | stat_field rpc)
|
|
61
|
+
if [[ "${RPC_NOW}" != "ready" ]]; then
|
|
62
|
+
warn "rpc still unavailable after ${WAIT_SEC}s - running the checks anyway"
|
|
63
|
+
fi
|
|
64
|
+
echo
|
|
65
|
+
|
|
66
|
+
# --- wait for the set fold to re-admit the validator -----------------
|
|
67
|
+
# After any downtime the network drops a validator from the active set
|
|
68
|
+
# (`set_fold_refused ... reason = marks_short`). It is re-admitted by the
|
|
69
|
+
# fold once its duty pulses span `rejoin_span = 64` epochs - measured at
|
|
70
|
+
# ~27 minutes on this devnet. Nothing has to be re-enrolled: the bond and
|
|
71
|
+
# the committed `ready` state stay valid, the node pulses automatically.
|
|
72
|
+
MEMBER_WAIT_SEC="${MEMBER_WAIT_SEC:-2700}"
|
|
73
|
+
ACTIVE_NOW=$(node_stat | stat_field validator_active)
|
|
74
|
+
if [[ "${ACTIVE_NOW}" != "true" ]]; then
|
|
75
|
+
log "validator is online but not a member yet - waiting up to ${MEMBER_WAIT_SEC}s for the set fold"
|
|
76
|
+
MEMBER_DEADLINE=$(( $(date +%s) + MEMBER_WAIT_SEC ))
|
|
77
|
+
MEMBER_START=$(date +%s)
|
|
78
|
+
LAST_LOG=$MEMBER_START
|
|
79
|
+
LAST_SCHED=""
|
|
80
|
+
FIRST_EPOCH=""
|
|
81
|
+
while (( $(date +%s) < MEMBER_DEADLINE )); do
|
|
82
|
+
SNAP=$(node_stat)
|
|
83
|
+
ACTIVE_NOW=$(printf '%s\n' "${SNAP}" | stat_field validator_active)
|
|
84
|
+
SCHED_NOW=$(printf '%s\n' "${SNAP}" | stat_field validator_scheduled)
|
|
85
|
+
EPOCH_NOW=$(printf '%s\n' "${SNAP}" | stat_field epoch)
|
|
86
|
+
if [[ "${ACTIVE_NOW}" == "true" ]]; then
|
|
87
|
+
log "validator re-admitted to the active set at epoch ${EPOCH_NOW} (after $(( $(date +%s) - MEMBER_START ))s)"
|
|
88
|
+
break
|
|
89
|
+
fi
|
|
90
|
+
if [[ -z "${FIRST_EPOCH}" && -n "${EPOCH_NOW}" ]]; then
|
|
91
|
+
FIRST_EPOCH="${EPOCH_NOW}"
|
|
92
|
+
fi
|
|
93
|
+
NOW=$(date +%s)
|
|
94
|
+
# A state change is news, so it is logged immediately; otherwise one status
|
|
95
|
+
# line every two minutes. (A \r progress line becomes its own log row in the
|
|
96
|
+
# console, which produced ~50 identical "epoch=... active=false" rows.)
|
|
97
|
+
if [[ -n "${SCHED_NOW}" && "${SCHED_NOW}" != "${LAST_SCHED}" ]]; then
|
|
98
|
+
log " validator_scheduled=${SCHED_NOW} at epoch ${EPOCH_NOW} - the fold re-admits after ~64 duty epochs"
|
|
99
|
+
LAST_SCHED="${SCHED_NOW}"
|
|
100
|
+
LAST_LOG=$NOW
|
|
101
|
+
elif (( NOW - LAST_LOG >= 120 )); then
|
|
102
|
+
log " still waiting: $(( (NOW - MEMBER_START) / 60 ))m of $(( MEMBER_WAIT_SEC / 60 ))m, epoch +$(( EPOCH_NOW - FIRST_EPOCH )) since the wait began (active=${ACTIVE_NOW:-?} scheduled=${SCHED_NOW:-false})"
|
|
103
|
+
LAST_LOG=$NOW
|
|
104
|
+
fi
|
|
105
|
+
sleep 20
|
|
106
|
+
done
|
|
107
|
+
[[ "${ACTIVE_NOW}" == "true" ]] || \
|
|
108
|
+
warn "still not active after ${MEMBER_WAIT_SEC}s - the fold re-admits on its own, keep monitoring"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# The enrollment projection can lag behind the membership view right after the
|
|
112
|
+
# RPC opens (observed on a live migration: active=true and voting=enabled for
|
|
113
|
+
# three 30 s samples while validator_enrollment still read 'unavailable', then
|
|
114
|
+
# 'ready'). Wait for the projection before the strict samples instead of turning
|
|
115
|
+
# that race into a false "migration failed".
|
|
116
|
+
ENROLL_WAIT_SEC="${ENROLL_WAIT_SEC:-900}"
|
|
117
|
+
ACTIVE_NOW=$(node_stat | stat_field validator_active)
|
|
118
|
+
ENROLL_NOW=$(node_stat | stat_field validator_enrollment)
|
|
119
|
+
if [[ "${ACTIVE_NOW}" == "true" && "${ENROLL_NOW}" != "ready" && "${ENROLL_NOW}" != "bonded" ]]; then
|
|
120
|
+
log "validator is active but enrollment reads '${ENROLL_NOW:-unavailable}' - waiting up to ${ENROLL_WAIT_SEC}s for the projection"
|
|
121
|
+
ENROLL_DEADLINE=$(( $(date +%s) + ENROLL_WAIT_SEC ))
|
|
122
|
+
ENROLL_START=$(date +%s)
|
|
123
|
+
LAST_LOG=$ENROLL_START
|
|
124
|
+
while (( $(date +%s) < ENROLL_DEADLINE )); do
|
|
125
|
+
SNAP=$(node_stat)
|
|
126
|
+
ACTIVE_NOW=$(printf '%s\n' "${SNAP}" | stat_field validator_active)
|
|
127
|
+
ENROLL_NOW=$(printf '%s\n' "${SNAP}" | stat_field validator_enrollment)
|
|
128
|
+
if [[ "${ENROLL_NOW}" == "ready" || "${ENROLL_NOW}" == "bonded" ]]; then
|
|
129
|
+
log "enrollment projection ready after $(( $(date +%s) - ENROLL_START ))s (${ENROLL_NOW})"
|
|
130
|
+
break
|
|
131
|
+
fi
|
|
132
|
+
if (( $(date +%s) - LAST_LOG >= 60 )); then
|
|
133
|
+
log " waiting for enrollment: $(( $(date +%s) - ENROLL_START ))s of ${ENROLL_WAIT_SEC}s (active=${ACTIVE_NOW:-?} enrollment=${ENROLL_NOW:-?})"
|
|
134
|
+
LAST_LOG=$(date +%s)
|
|
135
|
+
fi
|
|
136
|
+
sleep 10
|
|
137
|
+
done
|
|
138
|
+
if [[ "${ENROLL_NOW}" != "ready" && "${ENROLL_NOW}" != "bonded" ]]; then
|
|
139
|
+
warn "enrollment still reads '${ENROLL_NOW:-unavailable}' after ${ENROLL_WAIT_SEC}s - active membership is authoritative, the samples below decide"
|
|
140
|
+
fi
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# A migrated STUCK validator is in sync and healthy but the chain has not admitted
|
|
144
|
+
# it yet: that is a schedule, not a failure (2026-11-06, .99 -> .231). Ending the
|
|
145
|
+
# phase with FAILED there made the operator think the migration broke, while the
|
|
146
|
+
# bond and the readiness were fine and only the fold was missing.
|
|
147
|
+
if [[ "${ACTIVE_NOW}" != "true" ]]; then
|
|
148
|
+
MEM_STAT=$(node_stat)
|
|
149
|
+
MEM_ENROLL=$(printf "%s\n" "${MEM_STAT}" | stat_field validator_enrollment)
|
|
150
|
+
MEM_PROC=$(printf "%s\n" "${MEM_STAT}" | stat_field process)
|
|
151
|
+
MEM_RPC=$(printf "%s\n" "${MEM_STAT}" | stat_field rpc)
|
|
152
|
+
MEM_EPOCH=$(printf "%s\n" "${MEM_STAT}" | stat_field epoch)
|
|
153
|
+
if [[ "${MEM_ENROLL}" =~ ^(ready|bonded)$ && "${MEM_PROC}" == "online" && "${MEM_RPC}" == "ready" ]]; then
|
|
154
|
+
log "=== RESULT: WAITING-FOLD - the node runs, is in sync and holds the bond (enrollment=${MEM_ENROLL}) ==="
|
|
155
|
+
log "the set has not admitted it yet: the fold does that on its own (~64 duty epochs), nothing was re-enrolled"
|
|
156
|
+
log "nothing to fix: the console keeps watching and opens the activation popup when it happens"
|
|
157
|
+
set_status WAITING-FOLD "migrated and in sync; waiting for the set fold (enrollment=${MEM_ENROLL} epoch=${MEM_EPOCH})"
|
|
158
|
+
echo "MIGRATE_VERIFY state=waiting_fold enrollment=${MEM_ENROLL} epoch=${MEM_EPOCH}"
|
|
159
|
+
exit 2
|
|
160
|
+
fi
|
|
161
|
+
warn "the node is not active AND not ready (process=${MEM_PROC:-?} rpc=${MEM_RPC:-?} enrollment=${MEM_ENROLL:-?}) - the samples below show why"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
FAIL=0
|
|
165
|
+
PREV_EPOCH=""
|
|
166
|
+
|
|
167
|
+
for ((i = 1; i <= SAMPLES; i++)); do
|
|
168
|
+
STAT=$(node_stat)
|
|
169
|
+
printf '%s\n' "${STAT}" > "${MIG_DIR}/verify-sample-${i}.txt"
|
|
170
|
+
|
|
171
|
+
ROLE=$(printf '%s\n' "${STAT}" | stat_field role)
|
|
172
|
+
ADDR=$(printf '%s\n' "${STAT}" | stat_field address)
|
|
173
|
+
PROC=$(printf '%s\n' "${STAT}" | stat_field process)
|
|
174
|
+
RPC=$(printf '%s\n' "${STAT}" | stat_field rpc)
|
|
175
|
+
EPOCH=$(printf '%s\n' "${STAT}" | stat_field epoch)
|
|
176
|
+
HEAD=$(printf '%s\n' "${STAT}" | stat_field head_epoch)
|
|
177
|
+
VOTING=$(printf '%s\n' "${STAT}" | stat_field voting)
|
|
178
|
+
ACTIVE=$(printf '%s\n' "${STAT}" | stat_field validator_active)
|
|
179
|
+
ENROLL=$(printf '%s\n' "${STAT}" | stat_field validator_enrollment)
|
|
180
|
+
P2P=$(printf '%s\n' "${STAT}" | stat_field p2p_connected)
|
|
181
|
+
AGREED=$(printf '%s\n' "${STAT}"| stat_field round_agreed)
|
|
182
|
+
STEP=$(printf '%s\n' "${STAT}" | stat_field round_step)
|
|
183
|
+
PEERS=$(printf '%s\n' "${STAT}" | stat_field round_peers)
|
|
184
|
+
UPTIME=$(printf '%s\n' "${STAT}"| stat_field uptime)
|
|
185
|
+
RESTARTS=$(printf '%s\n' "${STAT}" | stat_field restarts)
|
|
186
|
+
|
|
187
|
+
log "sample ${i}/${SAMPLES}: epoch=${EPOCH} head=${HEAD} role=${ROLE} voting=${VOTING} active=${ACTIVE} p2p=${P2P} round_peers=${PEERS} agreed=${AGREED} step=${STEP} uptime=${UPTIME} restarts=${RESTARTS}"
|
|
188
|
+
|
|
189
|
+
[[ "${PROC}" == "online" ]] || { err "process is not online: ${PROC}"; FAIL=1; }
|
|
190
|
+
[[ "${RPC}" == "ready" ]] || { err "rpc is not ready: ${RPC}"; FAIL=1; }
|
|
191
|
+
[[ "${ROLE}" == "validator" ]] || { err "role is not validator: ${ROLE}"; FAIL=1; }
|
|
192
|
+
[[ "${VOTING}" == "enabled" ]] || { err "voting is not enabled: ${VOTING}"; FAIL=1; }
|
|
193
|
+
[[ "${ACTIVE}" == "true" ]] || { err "validator_active is not true: ${ACTIVE}"; FAIL=1; }
|
|
194
|
+
if [[ "${ENROLL}" != "ready" ]]; then
|
|
195
|
+
if [[ "${ACTIVE}" == "true" && "${VOTING}" == "enabled" ]]; then
|
|
196
|
+
# active + voting is stronger evidence than the enrollment projection; the
|
|
197
|
+
# projection can answer 'unavailable' while the RPC warms up.
|
|
198
|
+
warn "enrollment projection reads '${ENROLL:-unavailable}' while the validator is active and voting - accepting active membership"
|
|
199
|
+
else
|
|
200
|
+
err "validator_enrollment is not ready: ${ENROLL}"
|
|
201
|
+
FAIL=1
|
|
202
|
+
fi
|
|
203
|
+
fi
|
|
204
|
+
[[ -z "${EXPECTED_ADDRESS}" || "${ADDR}" == "${EXPECTED_ADDRESS}" ]] || { err "address mismatch: ${ADDR} != ${EXPECTED_ADDRESS}"; FAIL=1; }
|
|
205
|
+
[[ "${P2P:-0}" -gt 0 ]] || { err "no p2p peers connected"; FAIL=1; }
|
|
206
|
+
|
|
207
|
+
if [[ -n "${PREV_EPOCH}" ]]; then
|
|
208
|
+
if (( EPOCH > PREV_EPOCH )); then
|
|
209
|
+
log " epoch advanced ${PREV_EPOCH} -> ${EPOCH} (+$((EPOCH - PREV_EPOCH)))"
|
|
210
|
+
else
|
|
211
|
+
warn " epoch did not advance (${PREV_EPOCH} -> ${EPOCH}) - node may still be catching up"
|
|
212
|
+
fi
|
|
213
|
+
fi
|
|
214
|
+
PREV_EPOCH="${EPOCH}"
|
|
215
|
+
|
|
216
|
+
if (( i < SAMPLES )); then
|
|
217
|
+
log " sleeping ${INTERVAL}s before the next sample"
|
|
218
|
+
sleep "${INTERVAL}"
|
|
219
|
+
fi
|
|
220
|
+
done
|
|
221
|
+
|
|
222
|
+
echo
|
|
223
|
+
if (( FAIL == 0 )); then
|
|
224
|
+
log "=== RESULT: PASS - validator is online, voting and following the chain ==="
|
|
225
|
+
if [[ -s "${MIG_DIR}/downtime-start.txt" ]]; then
|
|
226
|
+
START_TS=$(date -u -d "$(cat "${MIG_DIR}/downtime-start.txt")" +%s 2>/dev/null || true)
|
|
227
|
+
if [[ -n "${START_TS}" ]]; then
|
|
228
|
+
DOWN=$(( $(date +%s) - START_TS ))
|
|
229
|
+
log "total time from source stop to verified voting: $((DOWN / 60))m$((DOWN % 60))s"
|
|
230
|
+
fi
|
|
231
|
+
fi
|
|
232
|
+
set_status DONE "verify-pass" "epoch=${PREV_EPOCH} address=${ADDR}"
|
|
233
|
+
else
|
|
234
|
+
err "=== RESULT: FAIL - see the errors above ==="
|
|
235
|
+
set_status FAILED "verify-failed"
|
|
236
|
+
exit 1
|
|
237
|
+
fi
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 08-rollback.sh — abort the migration and hand the validator back
|
|
4
|
+
#
|
|
5
|
+
# Only valid while the NEW node is fenced: the target node is stopped
|
|
6
|
+
# first, and only then is the source node started again. Both nodes
|
|
7
|
+
# must never run at the same time (same wallet = equivocation).
|
|
8
|
+
#
|
|
9
|
+
# Usage (on the NEW vps):
|
|
10
|
+
# OLD_HOST=203.0.113.27 bash 08-rollback.sh
|
|
11
|
+
# ===================================================================
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
source "$(dirname "$0")/lib.sh"
|
|
15
|
+
|
|
16
|
+
require_root
|
|
17
|
+
require_old_host
|
|
18
|
+
set_status RUNNING "rollback"
|
|
19
|
+
|
|
20
|
+
log "=== ROLLBACK — stopping the new node first ==="
|
|
21
|
+
if [[ -f "${NODE_ENV}" ]] && [[ -n "$(node_processes)" ]]; then
|
|
22
|
+
as_octra "cd ${NODE_SRC_DIR} && sh controls/stop.sh" 2>&1 | tee -a "${MIG_LOG}" || true
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
LEFT=$(node_processes)
|
|
26
|
+
for i in {1..20}; do
|
|
27
|
+
[[ -z "${LEFT}" ]] && break
|
|
28
|
+
sleep 1
|
|
29
|
+
LEFT=$(node_processes)
|
|
30
|
+
done
|
|
31
|
+
if [[ -n "${LEFT}" ]]; then
|
|
32
|
+
warn "target processes still alive — SIGTERM"
|
|
33
|
+
pkill -TERM -f "${NODE_PROC_RE}" || true
|
|
34
|
+
sleep 5
|
|
35
|
+
LEFT=$(node_processes)
|
|
36
|
+
fi
|
|
37
|
+
[[ -z "${LEFT}" ]] || die "cannot stop the target node — aborting rollback (source stays down)"
|
|
38
|
+
log "Target node stopped."
|
|
39
|
+
|
|
40
|
+
log "=== restarting the node on the old host ${OLD_HOST} ==="
|
|
41
|
+
if on_old "systemctl is-enabled --quiet pm2-${NODE_USER} 2>/dev/null"; then
|
|
42
|
+
log "pm2 boot unit already enabled on the source"
|
|
43
|
+
else
|
|
44
|
+
on_old "systemctl enable pm2-${NODE_USER} >/dev/null 2>&1 || true"
|
|
45
|
+
log "pm2 boot unit re-enabled on the source"
|
|
46
|
+
fi
|
|
47
|
+
on_old "su - ${NODE_USER} -c 'cd ${NODE_SRC_DIR} && sh controls/run.sh'" 2>&1 | tee -a "${MIG_LOG}"
|
|
48
|
+
|
|
49
|
+
sleep 10
|
|
50
|
+
log "=== source status after rollback ==="
|
|
51
|
+
on_old "su - ${NODE_USER} -c 'cd ${NODE_SRC_DIR} && sh controls/stat.sh'" 2>&1 | \
|
|
52
|
+
grep -E '^(role|process|epoch|voting|validator_active) =' || true
|
|
53
|
+
|
|
54
|
+
set_status DONE "rollback"
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# lib.sh — shared helpers for the Octra node migration toolkit
|
|
4
|
+
#
|
|
5
|
+
# Sourced by every script in scripts/migrate/.
|
|
6
|
+
# ===================================================================
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# --- Paths ---------------------------------------------------------
|
|
11
|
+
MIG_DIR="${MIG_DIR:-/root/migration}"
|
|
12
|
+
MIG_LOG="${MIG_LOG:-${MIG_DIR}/migrate.log}"
|
|
13
|
+
MIG_STATUS="${MIG_STATUS:-${MIG_DIR}/status}"
|
|
14
|
+
|
|
15
|
+
# --- Node paths (must match scripts/config.sh) ----------------------
|
|
16
|
+
NODE_SRC_DIR="${NODE_SRC_DIR:-/opt/octra/libv_litecore}"
|
|
17
|
+
NODE_DATA_ROOT="${NODE_DATA_ROOT:-/var/lib/octra}"
|
|
18
|
+
NODE_DATA_DIR="${NODE_DATA_DIR:-${NODE_DATA_ROOT}/devnet}"
|
|
19
|
+
NODE_SYNC_STAGE="${NODE_SYNC_STAGE:-${NODE_DATA_ROOT}/devnet.state_sync}"
|
|
20
|
+
NODE_ENV="${NODE_ENV:-${NODE_SRC_DIR}/.keys/validator/node.env}"
|
|
21
|
+
NODE_USER="${NODE_USER:-octra}"
|
|
22
|
+
CONSENSUS_PORT="${CONSENSUS_PORT:-19000}"
|
|
23
|
+
|
|
24
|
+
mkdir -p "${MIG_DIR}"
|
|
25
|
+
|
|
26
|
+
# --- Tool policy: check -> install -> continue ----------------------
|
|
27
|
+
# The shared implementation lives in scripts/config.sh, which the push puts at
|
|
28
|
+
# /root/config.sh on BOTH hosts. A host that still carries an old copy gets a
|
|
29
|
+
# loud warning instead of a bare "command not found" half way through a pull.
|
|
30
|
+
# Sourced BEFORE the logging helpers below, so migrate/log/err keep tee-ing into
|
|
31
|
+
# /root/migration/migrate.log.
|
|
32
|
+
if [[ -f /root/config.sh ]]; then
|
|
33
|
+
# shellcheck disable=SC1091
|
|
34
|
+
source /root/config.sh
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# --- Logging -------------------------------------------------------
|
|
38
|
+
log() { echo -e "\033[1;32m[migrate]\033[0m $*" | tee -a "${MIG_LOG}"; }
|
|
39
|
+
warn() { echo -e "\033[1;33m[warn]\033[0m $*" | tee -a "${MIG_LOG}" >&2; }
|
|
40
|
+
err() { echo -e "\033[1;31m[err]\033[0m $*" | tee -a "${MIG_LOG}" >&2; }
|
|
41
|
+
|
|
42
|
+
if ! declare -F ensure_tool >/dev/null 2>&1; then
|
|
43
|
+
warn "/root/config.sh has no ensure_tool (old copy on the host) - pushing the scripts again fixes it"
|
|
44
|
+
ensure_tool() {
|
|
45
|
+
command -v "$1" >/dev/null 2>&1 || die "tool $1 is missing and /root/config.sh cannot install it (push the scripts again)"
|
|
46
|
+
}
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
die() { err "$*"; set_status FAILED "$*"; exit 1; }
|
|
50
|
+
|
|
51
|
+
# --- Status markers (read by the Windows orchestrator) -------------
|
|
52
|
+
set_status() { # set_status <STATE> [detail...]
|
|
53
|
+
# all arguments after the state form the detail (a 3rd argument used to be
|
|
54
|
+
# dropped, which is why the console showed the step label instead of the result)
|
|
55
|
+
local state="$1" detail
|
|
56
|
+
shift || true
|
|
57
|
+
detail="$*"
|
|
58
|
+
printf '%s %s %s\n' "${state}" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${detail}" > "${MIG_STATUS}"
|
|
59
|
+
log "status = ${state} ${detail}"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
require_root() {
|
|
63
|
+
[[ $EUID -eq 0 ]] || die "run as root"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# --- a step must never die leaving status = RUNNING -------------------
|
|
67
|
+
# The migration orchestrator follows /root/migration/status (Wait-RemoteState).
|
|
68
|
+
# A script that died from an unexpected error - a helper that no longer exists, an
|
|
69
|
+
# unset variable with `set -u`, a full disk - used to leave that file at RUNNING,
|
|
70
|
+
# so the step looked hung until its timeout. This trap writes FAILED immediately.
|
|
71
|
+
_migrate_on_err() {
|
|
72
|
+
local rc="$1" line="$2" cmd="$3" state
|
|
73
|
+
state=$(awk 'NR==1 {print $1}' "${MIG_STATUS}" 2>/dev/null || true)
|
|
74
|
+
case "${state}" in
|
|
75
|
+
DONE|FAILED|WAITING-*) return 0 ;; # the script already decided
|
|
76
|
+
esac
|
|
77
|
+
err "unexpected failure (exit ${rc}) at line ${line}: ${cmd}"
|
|
78
|
+
set_status FAILED "line ${line}: ${cmd} (exit ${rc})"
|
|
79
|
+
}
|
|
80
|
+
trap '_migrate_on_err "$?" "$LINENO" "$BASH_COMMAND"' ERR
|
|
81
|
+
|
|
82
|
+
# Create every directory the node and the migration write into. A fresh host
|
|
83
|
+
# has none of them, and "no such file or directory" half-way through a pull is
|
|
84
|
+
# exactly the kind of failure this toolkit must not have.
|
|
85
|
+
ensure_dirs() {
|
|
86
|
+
local dir
|
|
87
|
+
for dir in "${MIG_DIR}" "${MIG_DIR}/backups" "${NODE_SRC_DIR}" \
|
|
88
|
+
"${NODE_DATA_ROOT}" "${NODE_DATA_DIR}" "${NODE_SYNC_STAGE}" \
|
|
89
|
+
"${NODE_DATA_ROOT}/validator-control"; do
|
|
90
|
+
mkdir -p "${dir}"
|
|
91
|
+
done
|
|
92
|
+
if id "${NODE_USER}" >/dev/null 2>&1; then
|
|
93
|
+
chown "${NODE_USER}:${NODE_USER}" "${NODE_DATA_DIR}" "${NODE_SYNC_STAGE}" 2>/dev/null || true
|
|
94
|
+
fi
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
require_cmd() {
|
|
98
|
+
command -v "$1" >/dev/null 2>&1 || die "missing command: $1"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# --- Node helpers --------------------------------------------------
|
|
102
|
+
as_octra() { su - "${NODE_USER}" -c "$*"; }
|
|
103
|
+
|
|
104
|
+
node_stat() {
|
|
105
|
+
as_octra "cd ${NODE_SRC_DIR} && sh controls/stat.sh 2>/dev/null" || true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
stat_field() { # stat_field <name> (reads node_stat from stdin when piped)
|
|
109
|
+
# the node prints Python's `None` for a field it cannot read: that is an EMPTY
|
|
110
|
+
# value, not a value (see scripts/deploy/lib.sh for the incident)
|
|
111
|
+
awk -F' = ' -v k="$1" '$1==k { v=$2; if (v=="None" || v=="null") v=""; print v; exit }'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
countdown() { # countdown <seconds> <message>
|
|
115
|
+
local n="$1" msg="${2:-waiting}"
|
|
116
|
+
while (( n > 0 )); do
|
|
117
|
+
printf '\r%s %ds ' "${msg}" "${n}"
|
|
118
|
+
sleep 1
|
|
119
|
+
n=$((n - 1))
|
|
120
|
+
done
|
|
121
|
+
printf '\r%s done\n' "${msg}"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# --- Remote (source VPS) helpers ----------------------------------
|
|
125
|
+
OLD_HOST="${OLD_HOST:-}"
|
|
126
|
+
OLD_USER="${OLD_USER:-root}"
|
|
127
|
+
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10)
|
|
128
|
+
|
|
129
|
+
require_old_host() {
|
|
130
|
+
[[ -n "${OLD_HOST}" ]] || die "OLD_HOST is not set (export OLD_HOST=<old-vps-ip>)"
|
|
131
|
+
ssh "${SSH_OPTS[@]}" "${OLD_USER}@${OLD_HOST}" "echo ok" >/dev/null 2>&1 \
|
|
132
|
+
|| die "cannot ssh ${OLD_USER}@${OLD_HOST} as root (key not installed?)"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
on_old() { ssh "${SSH_OPTS[@]}" "${OLD_USER}@${OLD_HOST}" "$*"; }
|
|
136
|
+
|
|
137
|
+
# --- Node process detection ----------------------------------------
|
|
138
|
+
# IMPORTANT: pgrep/pkill -f patterns would also match the shell that runs
|
|
139
|
+
# the check itself (its command line contains the pattern), which makes a
|
|
140
|
+
# dead node look alive. The bracket trick ([o]ctra_node) avoids that.
|
|
141
|
+
NODE_PROC_RE='[o]ctra_node\.exe|[o]ctra_pvac_worker\.exe'
|
|
142
|
+
|
|
143
|
+
node_processes() { # processes of the node on THIS host
|
|
144
|
+
ps -eo pid,args --no-headers 2>/dev/null | grep -E "${NODE_PROC_RE}" || true
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
on_old_node_processes() { # processes of the node on the SOURCE host
|
|
148
|
+
on_old "ps -eo pid,args --no-headers 2>/dev/null | grep -E '${NODE_PROC_RE}' || true"
|
|
149
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 10-preflight.sh - is this host a node we may update, and what is on it?
|
|
4
|
+
#
|
|
5
|
+
# Read-only. Nothing is pulled, built or restarted here: the step reports the
|
|
6
|
+
# checkout (commit/branch/dirty), the running binary, the chain role and the disk
|
|
7
|
+
# head-room, and refuses clearly when this is not an Octra node at all.
|
|
8
|
+
#
|
|
9
|
+
# Upstream rules this step reminds the operator of (README, "How to update"):
|
|
10
|
+
# never run `--sync` again, never change config/network.env, never re-enroll.
|
|
11
|
+
#
|
|
12
|
+
# Usage: sudo bash 10-preflight.sh
|
|
13
|
+
# Env: MIN_FREE_GB (20) - warn below that much free space on the data volume
|
|
14
|
+
# STATE_TIMEOUT (60)
|
|
15
|
+
# ===================================================================
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
source "$(dirname "$0")/lib.sh"
|
|
19
|
+
|
|
20
|
+
require_root
|
|
21
|
+
set_status RUNNING "preflight"
|
|
22
|
+
|
|
23
|
+
MIN_FREE_GB="${MIN_FREE_GB:-20}"
|
|
24
|
+
WARNINGS=0
|
|
25
|
+
|
|
26
|
+
[[ -d "${NODE_SRC_DIR}" ]] || die "no node at ${NODE_SRC_DIR} - this host does not run an Octra node"
|
|
27
|
+
[[ -d "${NODE_SRC_DIR}/.git" ]] || die "${NODE_SRC_DIR} is not a git checkout - the node cannot be updated from the source tree"
|
|
28
|
+
[[ -f "${NODE_WALLET}" ]] || die "wallet.json is missing (${NODE_WALLET}) - this is not the validator host"
|
|
29
|
+
[[ -f "${NODE_ENV}" ]] || die "node.env is missing (${NODE_ENV}) - the node has no configuration to update"
|
|
30
|
+
[[ -d "${NODE_DATA_DIR}" ]] || die "the data directory is missing (${NODE_DATA_DIR}) - nothing to keep in sync"
|
|
31
|
+
|
|
32
|
+
for tool in git make opam; do
|
|
33
|
+
if command -v "${tool}" >/dev/null 2>&1; then
|
|
34
|
+
log "tool ${tool}: present ($(command -v "${tool}"))"
|
|
35
|
+
else
|
|
36
|
+
warn "tool ${tool}: MISSING - the update needs it (the Apply step installs it via apt)"
|
|
37
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
# --- what the checkout says -------------------------------------------
|
|
42
|
+
COMMIT=$(source_commit)
|
|
43
|
+
HEAD=$(git_head)
|
|
44
|
+
BRANCH=$(git_branch)
|
|
45
|
+
DIRTY=$(git_dirty)
|
|
46
|
+
REMOTE=$(git_remote)
|
|
47
|
+
log "repository: ${REMOTE}"
|
|
48
|
+
log "checkout : SOURCE_COMMIT=${COMMIT:-unknown} git HEAD=${HEAD:-unknown}${BRANCH:+ on ${BRANCH}}"
|
|
49
|
+
if [[ -n "${DIRTY}" ]]; then
|
|
50
|
+
warn "the working tree has local changes - the official tool refuses to update a dirty checkout"
|
|
51
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# --- the control scripts the update needs -----------------------------
|
|
55
|
+
[[ -f "${CHECK_SH}" ]] || warn "controls/check.sh is missing (the package cannot be verified)"
|
|
56
|
+
if [[ -f "${UPGRADE_SH}" ]]; then
|
|
57
|
+
log "official tool: controls/upgrade.sh present"
|
|
58
|
+
else
|
|
59
|
+
warn "controls/upgrade.sh is missing - use the manual update (fallback) on this node version"
|
|
60
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
61
|
+
fi
|
|
62
|
+
if [[ -f "${STORAGE_SH}" ]]; then
|
|
63
|
+
log "storage tool : controls/storage.sh present"
|
|
64
|
+
else
|
|
65
|
+
warn "controls/storage.sh is missing - 'Remove preserved states' is not available on this node"
|
|
66
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
67
|
+
fi
|
|
68
|
+
[[ -f "${STOP_SH}" && -f "${RUN_SH}" ]] || warn "controls/stop.sh or controls/run.sh is missing - the restart may fail"
|
|
69
|
+
|
|
70
|
+
# --- the running node --------------------------------------------------
|
|
71
|
+
read_state
|
|
72
|
+
log "node : role=${ROLE:-?} process=${PROCESS:-?} rpc=${RPC:-?} voting=${VOTING:-?} epoch=${EPOCH:-?} uptime=${PROCESS_UP:-?}"
|
|
73
|
+
log "membership: active=${ACTIVE:-?} scheduled=${SCHED:-?} enrollment=${ENROLL:-?}"
|
|
74
|
+
log "binary : ${BINARY:-unknown}${PVAC:+ pvac ${PVAC}}"
|
|
75
|
+
if [[ "${PROCESS}" != "online" ]]; then
|
|
76
|
+
warn "the node process is ${PROCESS:-unknown} - the update restarts it (controls/run.sh)"
|
|
77
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
78
|
+
fi
|
|
79
|
+
if [[ "${RPC}" != "ready" ]]; then
|
|
80
|
+
warn "the node rpc is ${RPC:-unknown} - the upgrade tool waits for it, so the update may take longer"
|
|
81
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# a join is running: the upgrade restarts the node and could interrupt it
|
|
85
|
+
if systemctl is-active --quiet octra-enroll-join 2>/dev/null; then
|
|
86
|
+
warn "an enrollment join is running (unit octra-enroll-join) - finish the enrollment before updating"
|
|
87
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
88
|
+
fi
|
|
89
|
+
if [[ -f "${NODE_DATA_DIR}/validator-control/bond-increase.json" ]]; then
|
|
90
|
+
warn "a signed bond increase is stored on this host - make sure it is confirmed before restarting the node"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# --- disk head-room (a build + a state sync need room) -----------------
|
|
94
|
+
FREE_GB=""
|
|
95
|
+
if command -v df >/dev/null 2>&1; then
|
|
96
|
+
FREE_GB=$(df -B1 --output=avail "${NODE_DATA_ROOT}" 2>/dev/null | tail -1 | tr -d ' ' || true)
|
|
97
|
+
if [[ "${FREE_GB}" =~ ^[0-9]+$ ]]; then
|
|
98
|
+
FREE_H=$(awk -v b="${FREE_GB}" 'BEGIN { printf "%.1f GiB", b / 1073741824 }')
|
|
99
|
+
log "data volume: ${FREE_H} free on ${NODE_DATA_ROOT}"
|
|
100
|
+
if (( FREE_GB < MIN_FREE_GB * 1073741824 )); then
|
|
101
|
+
warn "less than ${MIN_FREE_GB} GiB free - free space before updating (controls/storage.sh --prune-prior --yes)"
|
|
102
|
+
WARNINGS=$(( WARNINGS + 1 ))
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
if command -v du >/dev/null 2>&1; then
|
|
107
|
+
USED=$(du -sh "${NODE_DATA_DIR}" 2>/dev/null | cut -f1 || true)
|
|
108
|
+
[[ -n "${USED}" ]] && log "data store : ${USED} in ${NODE_DATA_DIR}"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if [[ -f "${STORAGE_SH}" ]]; then
|
|
112
|
+
SUMMARY=$(storage_summary)
|
|
113
|
+
[[ -n "${SUMMARY}" ]] && log "storage : ${SUMMARY}"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
log "upstream rules: do NOT run --sync again, do NOT change config/network.env, do NOT re-enroll"
|
|
117
|
+
if (( WARNINGS > 0 )); then
|
|
118
|
+
log "${WARNINGS} warning(s) above - the update can still run"
|
|
119
|
+
fi
|
|
120
|
+
set_status DONE "commit=${HEAD:-unknown}${BRANCH:+ on ${BRANCH}} running=${ROLE:-?} active=${ACTIVE:-?} warnings=${WARNINGS}"
|