ovtools 0.0.0-stage → 1.0.1
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 +486 -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 +2736 -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 +13 -0
- package/public/index.html +1083 -0
- package/public/styles.css +580 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# ===================================================================
|
|
2
|
+
# Octra Live Monitor - Nginx site
|
|
3
|
+
# -------------------------------------------------------------------
|
|
4
|
+
# Domain : node.example.com (the deploy script rewrites server_name from -Domain)
|
|
5
|
+
# SSL : Cloudflare Origin Certificate (wildcard *.example.com)
|
|
6
|
+
# Static : /opt/octra/dashboard
|
|
7
|
+
# Backend : python dashboard on 127.0.0.1:9090 (proxy_pass /api/)
|
|
8
|
+
# ===================================================================
|
|
9
|
+
|
|
10
|
+
server {
|
|
11
|
+
listen 443 ssl;
|
|
12
|
+
listen [::]:443 ssl;
|
|
13
|
+
http2 on;
|
|
14
|
+
server_name node.example.com;
|
|
15
|
+
|
|
16
|
+
ssl_certificate /etc/ssl/octra/fullchain.pem;
|
|
17
|
+
ssl_certificate_key /etc/ssl/octra/privkey.pem;
|
|
18
|
+
|
|
19
|
+
ssl_protocols TLSv1.2 TLSv1.3;
|
|
20
|
+
ssl_prefer_server_ciphers on;
|
|
21
|
+
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
22
|
+
ssl_session_cache shared:SSL:10m;
|
|
23
|
+
ssl_session_timeout 1d;
|
|
24
|
+
|
|
25
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
26
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
27
|
+
add_header Referrer-Policy "no-referrer" always;
|
|
28
|
+
|
|
29
|
+
root /opt/octra/dashboard;
|
|
30
|
+
index index.html;
|
|
31
|
+
|
|
32
|
+
# -- Dashboard shell --------------------------------------------
|
|
33
|
+
location = /index.html {
|
|
34
|
+
add_header Cache-Control "no-cache, must-revalidate" always;
|
|
35
|
+
try_files $uri =404;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
location / {
|
|
39
|
+
try_files $uri $uri/ /index.html;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# -- Framework + app assets (cacheable, but revalidated on release) --
|
|
43
|
+
location ~* \.(css|js|ttf|woff2?|svg|ico|png)$ {
|
|
44
|
+
expires 1d;
|
|
45
|
+
add_header Cache-Control "public, max-age=86400";
|
|
46
|
+
try_files $uri =404;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# -- Telemetry API -> Python backend -----------------------------
|
|
50
|
+
location /api/ {
|
|
51
|
+
proxy_pass http://127.0.0.1:9090/api/;
|
|
52
|
+
proxy_http_version 1.1;
|
|
53
|
+
proxy_set_header Host $host;
|
|
54
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
55
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
56
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
57
|
+
proxy_connect_timeout 5s;
|
|
58
|
+
proxy_read_timeout 25s;
|
|
59
|
+
proxy_send_timeout 10s;
|
|
60
|
+
proxy_buffering off; # live JSON should never be buffered
|
|
61
|
+
add_header Cache-Control "no-store" always;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
gzip on;
|
|
66
|
+
gzip_types text/plain text/css application/json application/javascript text/javascript image/svg+xml;
|
|
67
|
+
gzip_min_length 256;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# -- HTTP -> HTTPS ---------------------------------------------------
|
|
71
|
+
server {
|
|
72
|
+
listen 80;
|
|
73
|
+
listen [::]:80;
|
|
74
|
+
server_name node.example.com;
|
|
75
|
+
return 301 https://$host$request_uri;
|
|
76
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Octra Node Live Monitor (dashboard backend)
|
|
3
|
+
Documentation=https://github.com/octra-labs/lite_node
|
|
4
|
+
After=network-online.target
|
|
5
|
+
Wants=network-online.target
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=simple
|
|
9
|
+
User=root
|
|
10
|
+
WorkingDirectory=/opt/octra/dashboard
|
|
11
|
+
ExecStart=/usr/bin/python3 /opt/octra/dashboard/server.py --port 9090
|
|
12
|
+
Restart=always
|
|
13
|
+
RestartSec=3
|
|
14
|
+
# Read-only telemetry collector: keep it cheap and predictable
|
|
15
|
+
Nice=5
|
|
16
|
+
TimeoutStopSec=10
|
|
17
|
+
StandardOutput=journal
|
|
18
|
+
StandardError=journal
|
|
19
|
+
SyslogIdentifier=octra-dashboard
|
|
20
|
+
|
|
21
|
+
# Optional overrides
|
|
22
|
+
#Environment=OCTRA_API_PORT=8080
|
|
23
|
+
|
|
24
|
+
# Hardening (the backend only needs to read /proc, pm2 state and log files)
|
|
25
|
+
NoNewPrivileges=true
|
|
26
|
+
PrivateTmp=true
|
|
27
|
+
|
|
28
|
+
[Install]
|
|
29
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# /etc/cron.d/octra-node-prune - installed by dashboard/deploy-dashboard.sh
|
|
2
|
+
# Daily cleanup of node-generated junk (operator log rotation, core dumps,
|
|
3
|
+
# dashboard __pycache__, journal vacuum). The program itself carries the
|
|
4
|
+
# policy; this file only says "once a day".
|
|
5
|
+
# Keep this file ASCII-only and LF-only: Debian cron reads it as bytes and
|
|
6
|
+
# rejects a CRLF or non-ASCII line with "bad minute", ignoring the whole file.
|
|
7
|
+
SHELL=/bin/bash
|
|
8
|
+
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
9
|
+
MAILTO=""
|
|
10
|
+
|
|
11
|
+
# m h dom mon dow user command
|
|
12
|
+
40 3 * * * root /usr/local/sbin/octra-node-prune >> /var/log/octra-node-prune.cron.log 2>&1
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Prune Octra node logs and junk files (daily)
|
|
3
|
+
Documentation=file:/usr/local/sbin/octra-node-prune
|
|
4
|
+
After=local-fs.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=oneshot
|
|
8
|
+
ExecStart=/usr/local/sbin/octra-node-prune
|
|
9
|
+
Nice=10
|
|
10
|
+
IOSchedulingClass=idle
|
|
11
|
+
# The program never fails the unit for a skipped step; a non-zero exit means
|
|
12
|
+
# it could not even start (permissions / missing binary).
|
|
13
|
+
SuccessExitStatus=0
|
|
14
|
+
|
|
15
|
+
[Install]
|
|
16
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Daily Octra node prune (log rotation + junk cleanup)
|
|
3
|
+
Documentation=file:/usr/local/sbin/octra-node-prune
|
|
4
|
+
|
|
5
|
+
[Timer]
|
|
6
|
+
OnCalendar=*-*-* 03:40:00
|
|
7
|
+
RandomizedDelaySec=600
|
|
8
|
+
Persistent=true
|
|
9
|
+
Unit=octra-node-prune.service
|
|
10
|
+
|
|
11
|
+
[Install]
|
|
12
|
+
WantedBy=timers.target
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# octra-node-prune — daily cleanup of node-generated junk
|
|
4
|
+
#
|
|
5
|
+
# Installed as /usr/local/sbin/octra-node-prune by
|
|
6
|
+
# dashboard/deploy-dashboard.sh and scheduled once a day (cron, or a
|
|
7
|
+
# systemd timer on hosts without cron).
|
|
8
|
+
#
|
|
9
|
+
# What it prunes (safe, node-scoped):
|
|
10
|
+
# • the pm2 operator log <node>/data/operator_logs/node.log
|
|
11
|
+
# (rotated with copy-truncate once it exceeds OCTRA_NODE_LOG_MAX_MB;
|
|
12
|
+
# the node keeps its file handle, the dashboard re-indexes itself)
|
|
13
|
+
# • rotated copies older than OCTRA_PRUNE_KEEP_DAYS
|
|
14
|
+
# • the pm2 daemon log, core dumps, dashboard __pycache__ / stale *.tmp
|
|
15
|
+
# • old systemd journal entries (kept OCTRA_PRUNE_JOURNAL_DAYS days)
|
|
16
|
+
# • the preserved states of the previous epochs:
|
|
17
|
+
# node/controls/storage.sh --prune-prior --yes
|
|
18
|
+
# (the upstream README's "Remove preserved states"; the node keeps the current
|
|
19
|
+
# state and rebuilds what a lagging peer needs, so this only frees disk)
|
|
20
|
+
# • its own log file (trimmed to the last 2000 lines)
|
|
21
|
+
#
|
|
22
|
+
# What it NEVER touches: irmin_store, chaindata, pvac, vote_log,
|
|
23
|
+
# commit_journal.log, finality/state-sync data, .keys, node.env.
|
|
24
|
+
#
|
|
25
|
+
# Usage
|
|
26
|
+
# octra-node-prune # prune now
|
|
27
|
+
# octra-node-prune --dry-run # report only, change nothing
|
|
28
|
+
# octra-node-prune --status # show the schedule + last runs
|
|
29
|
+
# ===================================================================
|
|
30
|
+
set -uo pipefail
|
|
31
|
+
|
|
32
|
+
VERSION="1.0"
|
|
33
|
+
NODE_HOME="${OCTRA_NODE_HOME:-/opt/octra/libv_litecore}"
|
|
34
|
+
NODE_LOG="${OCTRA_NODE_LOG:-$NODE_HOME/data/operator_logs/node.log}"
|
|
35
|
+
NODE_LOG_DIR="$(dirname "$NODE_LOG")"
|
|
36
|
+
NODE_LOG_MAX_MB="${OCTRA_NODE_LOG_MAX_MB:-200}"
|
|
37
|
+
KEEP_DAYS="${OCTRA_PRUNE_KEEP_DAYS:-7}"
|
|
38
|
+
KEEP_ROTATIONS="${OCTRA_PRUNE_KEEP_ROTATIONS:-2}"
|
|
39
|
+
JOURNAL_DAYS="${OCTRA_PRUNE_JOURNAL_DAYS:-7}"
|
|
40
|
+
DASH_DIR="${OCTRA_DASH_DIR:-/opt/octra/dashboard}"
|
|
41
|
+
PM2_LOG="${OCTRA_PM2_LOG:-/home/octra/.pm2/pm2.log}"
|
|
42
|
+
PM2_LOG_MAX_MB="${OCTRA_PM2_LOG_MAX_MB:-20}"
|
|
43
|
+
PRUNE_LOG="${OCTRA_PRUNE_LOG:-/var/log/octra-node-prune.log}"
|
|
44
|
+
LOCK_FILE="${OCTRA_PRUNE_LOCK:-/run/octra-node-prune.lock}"
|
|
45
|
+
DATA_ROOT="${OCTRA_DATA_ROOT:-/var/lib/octra}"
|
|
46
|
+
CRON_FILE="/etc/cron.d/octra-node-prune"
|
|
47
|
+
TIMER_FILE="/etc/systemd/system/octra-node-prune.timer"
|
|
48
|
+
|
|
49
|
+
DRY_RUN=0
|
|
50
|
+
STATUS_ONLY=0
|
|
51
|
+
QUIET=0
|
|
52
|
+
# the node's own storage tool (upstream README: "Remove preserved states")
|
|
53
|
+
STORAGE_TOOL="${OCTRA_STORAGE_TOOL:-$NODE_HOME/controls/storage.sh}"
|
|
54
|
+
STORAGE_TIMEOUT="${OCTRA_STORAGE_TIMEOUT:-900}"
|
|
55
|
+
NODE_USER="${OCTRA_NODE_USER:-octra}"
|
|
56
|
+
|
|
57
|
+
usage() {
|
|
58
|
+
sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//'
|
|
59
|
+
exit 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for arg in "$@"; do
|
|
63
|
+
case "$arg" in
|
|
64
|
+
--dry-run) DRY_RUN=1 ;;
|
|
65
|
+
--status) STATUS_ONLY=1 ;;
|
|
66
|
+
-q|--quiet) QUIET=1 ;;
|
|
67
|
+
-h|--help) usage ;;
|
|
68
|
+
-V|--version) echo "octra-node-prune $VERSION"; exit 0 ;;
|
|
69
|
+
*) echo "octra-node-prune: unknown argument '$arg' (try --help)" >&2; exit 2 ;;
|
|
70
|
+
esac
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
say() {
|
|
74
|
+
local line
|
|
75
|
+
line="$(date -u +%Y-%m-%dT%H:%M:%SZ) $*"
|
|
76
|
+
if [ -w "$(dirname "$PRUNE_LOG")" ] 2>/dev/null; then
|
|
77
|
+
printf '%s\n' "$line" >>"$PRUNE_LOG" 2>/dev/null || true
|
|
78
|
+
fi
|
|
79
|
+
[ "$QUIET" -eq 1 ] || printf '%s\n' "$line"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
human() {
|
|
83
|
+
awk -v b="${1:-0}" 'BEGIN{
|
|
84
|
+
split("B KB MB GB TB", unit, " ");
|
|
85
|
+
i = 1;
|
|
86
|
+
while (b >= 1024 && i < 5) { b = b / 1024; i++ }
|
|
87
|
+
printf "%.1f %s", b, unit[i]
|
|
88
|
+
}'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
size_of() { stat -c %s "$1" 2>/dev/null || echo 0; }
|
|
92
|
+
|
|
93
|
+
free_bytes() {
|
|
94
|
+
df -B1 --output=avail "$NODE_LOG_DIR" 2>/dev/null | tail -1 | tr -d ' ' || echo 0
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
rotate_log() {
|
|
98
|
+
local path="$1" max_mb="$2" label="$3"
|
|
99
|
+
[ -f "$path" ] || return 0
|
|
100
|
+
local bytes mb stamp rotated
|
|
101
|
+
bytes=$(size_of "$path")
|
|
102
|
+
mb=$(( bytes / 1024 / 1024 ))
|
|
103
|
+
if [ "$mb" -lt "$max_mb" ]; then
|
|
104
|
+
say "keep $label: $(human "$bytes") (limit ${max_mb} MB)"
|
|
105
|
+
return 0
|
|
106
|
+
fi
|
|
107
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
108
|
+
say "would rotate $label: $(human "$bytes") >= ${max_mb} MB"
|
|
109
|
+
return 0
|
|
110
|
+
fi
|
|
111
|
+
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
|
112
|
+
rotated="$path.$stamp"
|
|
113
|
+
# copy-truncate: the node (via pm2) holds this file open in append mode, so
|
|
114
|
+
# the content is copied aside and the live file is truncated in place - the
|
|
115
|
+
# writer keeps working and the dashboard notices the truncation and
|
|
116
|
+
# re-indexes by itself.
|
|
117
|
+
if cp --reflink=auto --sparse=never "$path" "$rotated" 2>/dev/null || cp "$path" "$rotated" 2>/dev/null; then
|
|
118
|
+
if : > "$path"; then
|
|
119
|
+
local gz="${rotated}.gz" gz_size
|
|
120
|
+
gzip -1 -f "$rotated" 2>/dev/null || true
|
|
121
|
+
if [ -f "$gz" ]; then
|
|
122
|
+
gz_size=$(size_of "$gz")
|
|
123
|
+
RECLAIMED=$((RECLAIMED + bytes - gz_size))
|
|
124
|
+
say "rotated $label: $(human "$bytes") -> $(basename "$rotated").gz ($(human "$gz_size"))"
|
|
125
|
+
else
|
|
126
|
+
say "rotated $label: $(human "$bytes") -> $(basename "$rotated") (left uncompressed)"
|
|
127
|
+
fi
|
|
128
|
+
else
|
|
129
|
+
say "FAILED $label: copy kept, live file not truncated (left as is)"
|
|
130
|
+
fi
|
|
131
|
+
else
|
|
132
|
+
say "FAILED $label: could not copy the log, nothing changed"
|
|
133
|
+
fi
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
prune_rotations() {
|
|
137
|
+
local pattern="$1" label="$2" days="$3"
|
|
138
|
+
while IFS= read -r old; do
|
|
139
|
+
[ -n "$old" ] || continue
|
|
140
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
141
|
+
say "would drop $label rotation: $(basename "$old") ($(human "$(size_of "$old")"))"
|
|
142
|
+
else
|
|
143
|
+
local sz
|
|
144
|
+
sz=$(size_of "$old")
|
|
145
|
+
rm -f -- "$old" && RECLAIMED=$((RECLAIMED + sz)) && \
|
|
146
|
+
say "dropped $label rotation: $(basename "$old") ($(human "$sz"))"
|
|
147
|
+
fi
|
|
148
|
+
done < <(find "$NODE_LOG_DIR" -maxdepth 1 -type f -name "$pattern" -mtime +"$days" 2>/dev/null)
|
|
149
|
+
|
|
150
|
+
# Keep at most KEEP_ROTATIONS newest copies. Sort by mtime: a timestamped
|
|
151
|
+
# name ("node.log.20261031T0727Z.gz") sorts *below* "node.log.500.gz", so a
|
|
152
|
+
# plain name sort would drop the freshest rotation instead of the oldest.
|
|
153
|
+
local extra
|
|
154
|
+
extra=$(find "$NODE_LOG_DIR" -maxdepth 1 -type f -name "$pattern" -printf '%T@ %p\n' 2>/dev/null \
|
|
155
|
+
| sort -rn | tail -n +"$((KEEP_ROTATIONS + 1))" | cut -d' ' -f2-)
|
|
156
|
+
while IFS= read -r old; do
|
|
157
|
+
[ -n "$old" ] || continue
|
|
158
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
159
|
+
say "would drop $label rotation beyond ${KEEP_ROTATIONS}: $(basename "$old")"
|
|
160
|
+
else
|
|
161
|
+
local sz
|
|
162
|
+
sz=$(size_of "$old")
|
|
163
|
+
rm -f -- "$old" && RECLAIMED=$((RECLAIMED + sz)) && \
|
|
164
|
+
say "dropped $label rotation beyond ${KEEP_ROTATIONS}: $(basename "$old")"
|
|
165
|
+
fi
|
|
166
|
+
done <<<"$extra"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
trim_in_place() {
|
|
170
|
+
local path="$1" max_mb="$2" label="$3"
|
|
171
|
+
[ -f "$path" ] || return 0
|
|
172
|
+
local mb
|
|
173
|
+
mb=$(( $(size_of "$path") / 1024 / 1024 ))
|
|
174
|
+
[ "$mb" -ge "$max_mb" ] || return 0
|
|
175
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
176
|
+
say "would truncate $label (${mb} MB >= ${max_mb} MB)"
|
|
177
|
+
return 0
|
|
178
|
+
fi
|
|
179
|
+
if : > "$path"; then
|
|
180
|
+
say "truncated $label (${mb} MB)"
|
|
181
|
+
else
|
|
182
|
+
say "FAILED $label: not writable"
|
|
183
|
+
fi
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
show_status() {
|
|
187
|
+
echo "octra-node-prune $VERSION"
|
|
188
|
+
local self
|
|
189
|
+
self=$(command -v octra-node-prune 2>/dev/null || echo "$0")
|
|
190
|
+
echo " program : $self"
|
|
191
|
+
if [ -f "$CRON_FILE" ]; then
|
|
192
|
+
echo " schedule: cron ($CRON_FILE)"
|
|
193
|
+
grep -vE '^[[:space:]]*(#|$)' "$CRON_FILE" 2>/dev/null | sed 's/^/ /'
|
|
194
|
+
if command -v systemctl >/dev/null 2>&1; then
|
|
195
|
+
echo " cron : $(systemctl is-active cron 2>/dev/null || echo unknown)"
|
|
196
|
+
fi
|
|
197
|
+
elif [ -f "$TIMER_FILE" ]; then
|
|
198
|
+
echo " schedule: systemd timer ($TIMER_FILE)"
|
|
199
|
+
command -v systemctl >/dev/null 2>&1 && systemctl list-timers octra-node-prune.timer --no-pager 2>/dev/null | sed 's/^/ /'
|
|
200
|
+
else
|
|
201
|
+
echo " schedule: NOT INSTALLED (run dashboard/deploy-dashboard.sh)"
|
|
202
|
+
fi
|
|
203
|
+
echo " node log: $NODE_LOG ($(human "$(size_of "$NODE_LOG")"))"
|
|
204
|
+
echo " limits : rotate > ${NODE_LOG_MAX_MB} MB, keep ${KEEP_ROTATIONS} copies / ${KEEP_DAYS} days"
|
|
205
|
+
if [ -f "$STORAGE_TOOL" ]; then
|
|
206
|
+
echo " preserved: prune-prior --yes each run ($STORAGE_TOOL)"
|
|
207
|
+
else
|
|
208
|
+
echo " preserved: controls/storage.sh not found - the step is skipped"
|
|
209
|
+
fi
|
|
210
|
+
echo " log : $PRUNE_LOG"
|
|
211
|
+
if [ -s "$PRUNE_LOG" ]; then
|
|
212
|
+
echo " last runs:"
|
|
213
|
+
grep -E 'prune start|prune done' "$PRUNE_LOG" | tail -4 | sed 's/^/ /'
|
|
214
|
+
fi
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
if [ "$STATUS_ONLY" -eq 1 ]; then
|
|
219
|
+
show_status
|
|
220
|
+
exit 0
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# ── single instance ────────────────────────────────────────────────
|
|
224
|
+
if [ -n "$LOCK_FILE" ] && command -v flock >/dev/null 2>&1; then
|
|
225
|
+
exec 9>"$LOCK_FILE" 2>/dev/null || true
|
|
226
|
+
if ! flock -n 9; then
|
|
227
|
+
say "prune skip: another run is still active"
|
|
228
|
+
exit 0
|
|
229
|
+
fi
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
start_free=$(free_bytes)
|
|
233
|
+
RECLAIMED=0
|
|
234
|
+
say "prune start (dry-run=${DRY_RUN} max=${NODE_LOG_MAX_MB}MB keep=${KEEP_ROTATIONS}/${KEEP_DAYS}d)"
|
|
235
|
+
|
|
236
|
+
# ── operator log (the big one) ────────────────────────────────────
|
|
237
|
+
rotate_log "$NODE_LOG" "$NODE_LOG_MAX_MB" "operator log"
|
|
238
|
+
prune_rotations "$(basename "$NODE_LOG").*.gz" "operator log" "$KEEP_DAYS"
|
|
239
|
+
|
|
240
|
+
# ── pm2 daemon log ────────────────────────────────────────────────
|
|
241
|
+
trim_in_place "$PM2_LOG" "$PM2_LOG_MAX_MB" "pm2 daemon log"
|
|
242
|
+
|
|
243
|
+
# ── core dumps (kept 1 day in case someone wants to inspect them) ──
|
|
244
|
+
for dir in "$NODE_HOME" "$DATA_ROOT" /tmp; do
|
|
245
|
+
[ -d "$dir" ] || continue
|
|
246
|
+
while IFS= read -r dump; do
|
|
247
|
+
[ -n "$dump" ] || continue
|
|
248
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
249
|
+
say "would drop core dump: $dump ($(human "$(size_of "$dump")"))"
|
|
250
|
+
else
|
|
251
|
+
rm -f -- "$dump" && say "dropped core dump: $dump ($(human "$(size_of "$dump")"))"
|
|
252
|
+
fi
|
|
253
|
+
done < <(find "$dir" -maxdepth 3 -type f \( -name 'core' -o -name 'core.*' -o -name '*.core' \) -mtime +1 2>/dev/null | head -20)
|
|
254
|
+
done
|
|
255
|
+
|
|
256
|
+
# ── dashboard junk ────────────────────────────────────────────────
|
|
257
|
+
if [ -d "$DASH_DIR" ]; then
|
|
258
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
259
|
+
pyc=$(find "$DASH_DIR" -name '__pycache__' -type d 2>/dev/null | wc -l)
|
|
260
|
+
tmp=$(find "$DASH_DIR" -maxdepth 1 -name '*.tmp' -mtime +1 2>/dev/null | wc -l)
|
|
261
|
+
say "would drop dashboard junk: ${pyc} __pycache__ dir(s), ${tmp} stale .tmp file(s)"
|
|
262
|
+
else
|
|
263
|
+
find "$DASH_DIR" -name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null
|
|
264
|
+
find "$DASH_DIR" -maxdepth 1 -name '*.tmp' -mtime +1 -delete 2>/dev/null
|
|
265
|
+
say "cleaned dashboard __pycache__ / stale .tmp"
|
|
266
|
+
fi
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
# ── systemd journal (system hygiene, bounded) ─────────────────────
|
|
270
|
+
if [ "${OCTRA_PRUNE_SKIP_JOURNAL:-0}" != "1" ] && command -v journalctl >/dev/null 2>&1; then
|
|
271
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
272
|
+
say "would vacuum systemd journal beyond ${JOURNAL_DAYS}d"
|
|
273
|
+
else
|
|
274
|
+
before=$(du -sk /var/log/journal 2>/dev/null | cut -f1)
|
|
275
|
+
journalctl --vacuum-time="${JOURNAL_DAYS}d" --quiet >/dev/null 2>&1 || true
|
|
276
|
+
after=$(du -sk /var/log/journal 2>/dev/null | cut -f1)
|
|
277
|
+
if [ -n "${before:-}" ] && [ -n "${after:-}" ] && [ "$before" -gt "$after" ]; then
|
|
278
|
+
say "journal vacuumed ${JOURNAL_DAYS}d: $(( (before - after) / 1024 )) MB freed"
|
|
279
|
+
else
|
|
280
|
+
say "journal within ${JOURNAL_DAYS}d, nothing to vacuum"
|
|
281
|
+
fi
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
# ── preserved states (the node's own storage tool) ────────────────
|
|
286
|
+
# Upstream README: "Remove preserved states"
|
|
287
|
+
# sh controls/storage.sh
|
|
288
|
+
# sh controls/storage.sh --prune-prior --yes
|
|
289
|
+
# A node keeps the states of the previous epochs so it can serve peers that lag
|
|
290
|
+
# behind; they grow until the store is oversized. This is the documented
|
|
291
|
+
# maintenance and it only frees disk - the current state is untouched.
|
|
292
|
+
if [ "${OCTRA_PRUNE_SKIP_STORAGE:-0}" = "1" ]; then
|
|
293
|
+
say "storage preserved states: skipped (OCTRA_PRUNE_SKIP_STORAGE=1)"
|
|
294
|
+
elif [ ! -f "$STORAGE_TOOL" ]; then
|
|
295
|
+
say "storage preserved states: controls/storage.sh not found (old node version) - skipped"
|
|
296
|
+
elif [ "$DRY_RUN" -eq 1 ]; then
|
|
297
|
+
say "would run controls/storage.sh --prune-prior --yes (preserved states)"
|
|
298
|
+
else
|
|
299
|
+
before_storage=$(df -B1 --output=avail "$NODE_HOME" 2>/dev/null | tail -1 | tr -d ' ' || echo 0)
|
|
300
|
+
out=$(timeout "$STORAGE_TIMEOUT" su - "$NODE_USER" -c \
|
|
301
|
+
"cd $NODE_HOME && sh controls/storage.sh --prune-prior --yes" 2>&1)
|
|
302
|
+
rc=$?
|
|
303
|
+
if [ "$rc" -eq 0 ]; then
|
|
304
|
+
line=$(printf '%s\n' "$out" | grep -iE 'reclaim|freed|removed|pruned|preserved|prior' | tail -1)
|
|
305
|
+
after_storage=$(df -B1 --output=avail "$NODE_HOME" 2>/dev/null | tail -1 | tr -d ' ' || echo 0)
|
|
306
|
+
freed_disk=0
|
|
307
|
+
if [ -n "${before_storage:-}" ] && [ -n "${after_storage:-}" ] \
|
|
308
|
+
&& [ "${after_storage}" -gt "${before_storage}" ] 2>/dev/null; then
|
|
309
|
+
freed_disk=$(( after_storage - before_storage ))
|
|
310
|
+
fi
|
|
311
|
+
say "storage preserved states pruned${freed_disk:+ : $(human "$freed_disk") freed}${line:+ - $line}"
|
|
312
|
+
RECLAIMED=$(( RECLAIMED + freed_disk ))
|
|
313
|
+
else
|
|
314
|
+
# never fail the whole prune over the node's own tool: report and continue
|
|
315
|
+
say "storage prune-prior skipped (exit $rc): $(printf '%s\n' "$out" | tail -1)"
|
|
316
|
+
fi
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# ── keep our own log small ────────────────────────────────────────
|
|
320
|
+
if [ -f "$PRUNE_LOG" ] && [ "$(size_of "$PRUNE_LOG")" -gt 1048576 ] && [ "$DRY_RUN" -eq 0 ]; then
|
|
321
|
+
tail -n 2000 "$PRUNE_LOG" >"$PRUNE_LOG.tmp" 2>/dev/null && mv -f "$PRUNE_LOG.tmp" "$PRUNE_LOG"
|
|
322
|
+
fi
|
|
323
|
+
|
|
324
|
+
end_free=$(free_bytes)
|
|
325
|
+
freed=$(( end_free - start_free ))
|
|
326
|
+
[ "$freed" -lt 0 ] && freed=0
|
|
327
|
+
say "prune done: reclaimed $(human "$RECLAIMED") · operator log now $(human "$(size_of "$NODE_LOG")") · free $(human "$end_free")"
|
|
328
|
+
exit 0
|
|
329
|
+
|