Compare commits
2 commits
c6e1a01167
...
ba4ba504d0
| Author | SHA1 | Date | |
|---|---|---|---|
| ba4ba504d0 | |||
| 0736f29f79 |
6 changed files with 982 additions and 1 deletions
69
03_vm_disk_enlargement.md
Normal file
69
03_vm_disk_enlargement.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# 03 VM Disk Enlargement
|
||||
|
||||
How to enlarge the disk of an existing Proxmox VM that was provisioned via the tofu project.
|
||||
|
||||
## Overview
|
||||
|
||||
Since the tofu VM resource uses `ignore_changes = all` in its lifecycle block, disk resizes are performed manually through the Proxmox UI and then expanded inside the guest OS. You should still update the tofu vars to keep them in sync with reality.
|
||||
|
||||
## Step 1: Update disk size in tofu vars
|
||||
|
||||
* Edit `tofu/nodito/terraform.tfvars` and bump the `disk_size_gb` value for the VM you want to resize.
|
||||
* This won't trigger any actual change (because of `ignore_changes = all`), but keeps the declared state consistent so future readers know the real disk size.
|
||||
|
||||
## Step 2: Resize the disk in Proxmox
|
||||
|
||||
* Open the Proxmox web UI and navigate to the VM.
|
||||
* Go to the **Hardware** tab.
|
||||
* Select the **Hard Disk** entry (scsi0).
|
||||
* Click **Disk Action** > **Resize**.
|
||||
* Enter the amount of additional space you want to add (this is the increment, not the total). For example, to go from 10 GB to 30 GB, enter `20`.
|
||||
* Click **Resize disk**.
|
||||
|
||||
The VM does **not** need to be stopped for this operation. Proxmox will grow the underlying block device while the VM is running.
|
||||
|
||||
## Step 3: Expand the partition and filesystem inside the VM
|
||||
|
||||
SSH into the VM and run these steps as root (or with `sudo`).
|
||||
|
||||
### Check the current layout
|
||||
|
||||
```bash
|
||||
lsblk
|
||||
```
|
||||
|
||||
You should see that the disk (`sda`) now shows the new total size, but the root partition (`sda1`) still has the old size.
|
||||
|
||||
### Grow the partition
|
||||
|
||||
Install `cloud-guest-utils` if `growpart` is not available:
|
||||
|
||||
```bash
|
||||
apt install -y cloud-guest-utils
|
||||
```
|
||||
|
||||
Then grow the root partition to fill all available space:
|
||||
|
||||
```bash
|
||||
growpart /dev/sda 1
|
||||
```
|
||||
|
||||
> Note the space between the device and the partition number.
|
||||
|
||||
### Resize the filesystem
|
||||
|
||||
For ext4 (the default for Debian cloud images):
|
||||
|
||||
```bash
|
||||
resize2fs /dev/sda1
|
||||
```
|
||||
|
||||
This works online; no reboot is required.
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
df -h /
|
||||
```
|
||||
|
||||
The root filesystem should now reflect the new size.
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
retries = int(sys.argv[8])
|
||||
ntfy_topic = sys.argv[9] if len(sys.argv) > 9 else "alerts"
|
||||
|
||||
api = UptimeKumaApi(api_url, timeout=60, wait_events=2.0)
|
||||
api = UptimeKumaApi(api_url, timeout=120, wait_events=2.0)
|
||||
api.login(username, password)
|
||||
|
||||
# Get all monitors
|
||||
|
|
|
|||
|
|
@ -31,3 +31,10 @@ mariadb_mempool_password: "CHANGE_ME_TO_SECURE_PASSWORD"
|
|||
# Used by: services/forgejo-runner/deploy_forgejo_runner_playbook.yml
|
||||
# See: services/forgejo-runner/SETUP.md for how to obtain this token
|
||||
forgejo_runner_registration_token: "YOUR_RUNNER_TOKEN_HERE"
|
||||
|
||||
# DATUM Gateway secrets
|
||||
# Used by: services/datum-gateway/deploy_datum_gateway_playbook.yml
|
||||
datum_mining_address: "YOUR_BITCOIN_ADDRESS_FOR_BLOCK_REWARDS"
|
||||
datum_gateway_admin_password: "CHANGE_ME_TO_SECURE_PASSWORD"
|
||||
datum_dashboard_username: "admin"
|
||||
datum_dashboard_password_hash: "$2a$14$..." # Generate with: caddy hash-password
|
||||
|
|
|
|||
43
ansible/services/datum-gateway/datum_gateway_vars.yml
Normal file
43
ansible/services/datum-gateway/datum_gateway_vars.yml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# DATUM Gateway Configuration Variables
|
||||
# https://github.com/OCEAN-xyz/datum_gateway
|
||||
|
||||
# Version - pin to a specific tag
|
||||
datum_gateway_version: "v0.4.1beta"
|
||||
|
||||
# Directories
|
||||
datum_gateway_dir: /opt/datum-gateway
|
||||
datum_gateway_source_dir: "{{ datum_gateway_dir }}/source"
|
||||
datum_gateway_config_dir: /etc/datum-gateway
|
||||
datum_gateway_log_dir: /var/log/datum-gateway
|
||||
|
||||
# Binary
|
||||
datum_gateway_bin_path: /usr/local/bin/datum_gateway
|
||||
|
||||
# Ports
|
||||
datum_gateway_stratum_port: 23334 # Miners connect here via Stratum v1
|
||||
datum_gateway_api_port: 7152 # Web dashboard / API
|
||||
|
||||
# Stratum settings
|
||||
datum_vardiff_min: 524288 # Minimum share difficulty (must be power of 2; OCEAN floor overrides if higher)
|
||||
|
||||
# Service user
|
||||
datum_gateway_user: datum
|
||||
datum_gateway_group: datum
|
||||
|
||||
# Build options
|
||||
datum_gateway_build_jobs: 4
|
||||
|
||||
# Bitcoin node connection
|
||||
# The gateway runs on the same host as Bitcoin Knots so localhost RPC works.
|
||||
# datum_bitcoin_rpc_url should include http:// and port.
|
||||
datum_bitcoin_rpc_url: "http://127.0.0.1:8332"
|
||||
# Note: bitcoin_rpc_user and bitcoin_rpc_password come from infra_secrets.yml
|
||||
|
||||
# Mining config
|
||||
datum_coinbase_tag_primary: "DATUM"
|
||||
datum_coinbase_tag_secondary: "BY ORDER OF BIP110"
|
||||
|
||||
datum_pool_pass_workers: true
|
||||
datum_pool_pass_full_users: true
|
||||
datum_pooled_mining_only: true
|
||||
|
||||
859
ansible/services/datum-gateway/deploy_datum_gateway_playbook.yml
Normal file
859
ansible/services/datum-gateway/deploy_datum_gateway_playbook.yml
Normal file
|
|
@ -0,0 +1,859 @@
|
|||
---
|
||||
# DATUM Gateway Deployment Playbook
|
||||
#
|
||||
# Deploys DATUM Gateway (https://github.com/OCEAN-xyz/datum_gateway) on the
|
||||
# Bitcoin Knots host so it has direct localhost RPC access to bitcoind.
|
||||
#
|
||||
# What this does:
|
||||
# 1. Installs build deps and compiles datum_gateway from source
|
||||
# 2. Creates a dedicated system user and config/log directories
|
||||
# 3. Writes /etc/datum-gateway/config.json from vars/secrets
|
||||
# 4. Patches bitcoin.conf with the required blockmaxsize/blocknotify lines
|
||||
# 5. Creates and enables a systemd service
|
||||
# 6. Creates a push-monitor health check script + systemd timer
|
||||
# 7. Registers a push monitor in Uptime Kuma
|
||||
#
|
||||
# Separate play: adds a Caddy reverse proxy on vipy for the dashboard.
|
||||
#
|
||||
# Stratum port (23334) is bound on knots_box_local. Expose it to miners via
|
||||
# a firewall rule, Tailscale, or a socket proxy on vipy — not handled here.
|
||||
#
|
||||
# Required secrets in infra_secrets.yml:
|
||||
# datum_mining_address - Bitcoin address for block rewards
|
||||
# datum_gateway_admin_password - Password for the /api admin endpoint
|
||||
# bitcoin_rpc_user - Shared with the bitcoin-knots deployment
|
||||
# bitcoin_rpc_password - Shared with the bitcoin-knots deployment
|
||||
|
||||
- name: Deploy DATUM Gateway on knots_box_local
|
||||
hosts: knots_box_local
|
||||
become: yes
|
||||
vars_files:
|
||||
- ../../infra_vars.yml
|
||||
- ../../services_config.yml
|
||||
- ../../infra_secrets.yml
|
||||
- ./datum_gateway_vars.yml
|
||||
vars:
|
||||
datum_gateway_subdomain: "{{ subdomains.datum_gateway }}"
|
||||
datum_gateway_domain: "{{ datum_gateway_subdomain }}.{{ root_domain }}"
|
||||
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
|
||||
|
||||
tasks:
|
||||
# ===========================================
|
||||
# Build Dependencies
|
||||
# ===========================================
|
||||
- name: Install DATUM Gateway build dependencies
|
||||
apt:
|
||||
name:
|
||||
- cmake
|
||||
- build-essential
|
||||
- git
|
||||
- libjansson-dev
|
||||
- libmicrohttpd-dev
|
||||
- libsodium-dev
|
||||
- libcurl4-openssl-dev
|
||||
# Runtime-only (netcat for health check)
|
||||
- netcat-openbsd
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
# ===========================================
|
||||
# System User and Directories
|
||||
# ===========================================
|
||||
- name: Create datum system user
|
||||
user:
|
||||
name: "{{ datum_gateway_user }}"
|
||||
system: yes
|
||||
shell: /usr/sbin/nologin
|
||||
home: "{{ datum_gateway_dir }}"
|
||||
create_home: no
|
||||
comment: "DATUM Gateway"
|
||||
|
||||
- name: Create DATUM Gateway directories
|
||||
file:
|
||||
path: "{{ item.path }}"
|
||||
state: directory
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ datum_gateway_group }}"
|
||||
mode: "{{ item.mode }}"
|
||||
loop:
|
||||
- { path: "{{ datum_gateway_dir }}", owner: root, mode: "0755" }
|
||||
- { path: "{{ datum_gateway_source_dir }}", owner: root, mode: "0755" }
|
||||
- { path: "{{ datum_gateway_config_dir }}", owner: "{{ datum_gateway_user }}", mode: "0750" }
|
||||
- { path: "{{ datum_gateway_log_dir }}", owner: "{{ datum_gateway_user }}", mode: "0750" }
|
||||
|
||||
# ===========================================
|
||||
# Build from Source
|
||||
# ===========================================
|
||||
- name: Clone DATUM Gateway repository at {{ datum_gateway_version }}
|
||||
git:
|
||||
repo: https://github.com/OCEAN-xyz/datum_gateway.git
|
||||
dest: "{{ datum_gateway_source_dir }}"
|
||||
version: "{{ datum_gateway_version }}"
|
||||
force: yes
|
||||
register: git_clone
|
||||
|
||||
- name: Configure cmake build
|
||||
command: cmake . -DCMAKE_BUILD_TYPE=Release
|
||||
args:
|
||||
chdir: "{{ datum_gateway_source_dir }}"
|
||||
|
||||
- name: Compile datum_gateway
|
||||
command: make -j{{ datum_gateway_build_jobs }}
|
||||
args:
|
||||
chdir: "{{ datum_gateway_source_dir }}"
|
||||
|
||||
- name: Install datum_gateway binary
|
||||
copy:
|
||||
src: "{{ datum_gateway_source_dir }}/datum_gateway"
|
||||
dest: "{{ datum_gateway_bin_path }}"
|
||||
remote_src: yes
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
notify: Restart datum-gateway
|
||||
|
||||
# ===========================================
|
||||
# Configuration
|
||||
# ===========================================
|
||||
- name: Write DATUM Gateway config.json
|
||||
copy:
|
||||
dest: "{{ datum_gateway_config_dir }}/config.json"
|
||||
content: |
|
||||
{
|
||||
"bitcoind": {
|
||||
"rpcuser": "{{ bitcoin_rpc_user }}",
|
||||
"rpcpassword": "{{ bitcoin_rpc_password }}",
|
||||
"rpcurl": "{{ datum_bitcoin_rpc_url }}",
|
||||
"notify_fallback": true
|
||||
},
|
||||
"stratum": {
|
||||
"listen_port": {{ datum_gateway_stratum_port }},
|
||||
"vardiff_min": {{ datum_vardiff_min }}
|
||||
},
|
||||
"mining": {
|
||||
"pool_address": "{{ datum_mining_address }}",
|
||||
"coinbase_tag_primary": "{{ datum_coinbase_tag_primary }}",
|
||||
"coinbase_tag_secondary": "{{ datum_coinbase_tag_secondary }}"
|
||||
},
|
||||
"api": {
|
||||
"admin_password": "{{ datum_gateway_admin_password }}",
|
||||
"listen_port": {{ datum_gateway_api_port }},
|
||||
"modify_conf": false
|
||||
},
|
||||
"logger": {
|
||||
"log_to_console": true,
|
||||
"log_to_file": true,
|
||||
"log_file": "{{ datum_gateway_log_dir }}/datum_gateway.log",
|
||||
"log_rotate_daily": true,
|
||||
"log_level_console": 2,
|
||||
"log_level_file": 1
|
||||
},
|
||||
"datum": {
|
||||
"pool_pass_workers": {{ datum_pool_pass_workers | lower }},
|
||||
"pool_pass_full_users": {{ datum_pool_pass_full_users | lower }},
|
||||
"pooled_mining_only": {{ datum_pooled_mining_only | lower }}
|
||||
}
|
||||
}
|
||||
owner: "{{ datum_gateway_user }}"
|
||||
group: "{{ datum_gateway_group }}"
|
||||
mode: "0640"
|
||||
notify: Restart datum-gateway
|
||||
|
||||
# ===========================================
|
||||
# Systemd Service
|
||||
# ===========================================
|
||||
- name: Create datum-gateway systemd service
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-gateway.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Gateway - Bitcoin Mining Gateway
|
||||
Documentation=https://github.com/OCEAN-xyz/datum_gateway
|
||||
After=network.target bitcoind.service
|
||||
Wants=bitcoind.service
|
||||
|
||||
[Service]
|
||||
User={{ datum_gateway_user }}
|
||||
Group={{ datum_gateway_group }}
|
||||
Type=simple
|
||||
ExecStart={{ datum_gateway_bin_path }} --config {{ datum_gateway_config_dir }}/config.json
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
# Prevent config from being read by other users
|
||||
ReadWritePaths={{ datum_gateway_log_dir }}
|
||||
ReadOnlyPaths={{ datum_gateway_config_dir }}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart datum-gateway
|
||||
|
||||
- name: Reload systemd daemon
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Enable and start datum-gateway
|
||||
systemd:
|
||||
name: datum-gateway
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
# ===========================================
|
||||
# Health Check Script + Systemd Timer
|
||||
# ===========================================
|
||||
- name: Create DATUM Gateway health check script
|
||||
copy:
|
||||
dest: /usr/local/bin/datum-gateway-healthcheck-push.sh
|
||||
content: |
|
||||
#!/bin/bash
|
||||
UPTIME_KUMA_PUSH_URL="${UPTIME_KUMA_PUSH_URL}"
|
||||
STRATUM_PORT={{ datum_gateway_stratum_port }}
|
||||
|
||||
check_datum() {
|
||||
# Service must be active and stratum port must be listening
|
||||
systemctl is-active --quiet datum-gateway && \
|
||||
nc -z 127.0.0.1 "${STRATUM_PORT}"
|
||||
}
|
||||
|
||||
push_to_uptime_kuma() {
|
||||
local status=$1
|
||||
local msg=$2
|
||||
if [ -z "$UPTIME_KUMA_PUSH_URL" ]; then
|
||||
echo "ERROR: UPTIME_KUMA_PUSH_URL not set"
|
||||
return 1
|
||||
fi
|
||||
curl -s --max-time 10 --retry 2 -o /dev/null \
|
||||
"${UPTIME_KUMA_PUSH_URL}?status=${status}&msg=${msg// /%20}&ping=" || true
|
||||
}
|
||||
|
||||
if check_datum; then
|
||||
push_to_uptime_kuma "up" "OK"
|
||||
exit 0
|
||||
else
|
||||
push_to_uptime_kuma "down" "DATUM Gateway not responding"
|
||||
exit 1
|
||||
fi
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Create datum-gateway health check systemd service
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-gateway-healthcheck.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Gateway Health Check
|
||||
After=network.target datum-gateway.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/datum-gateway-healthcheck-push.sh
|
||||
Environment=UPTIME_KUMA_PUSH_URL=
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Create datum-gateway health check systemd timer
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-gateway-healthcheck.timer
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Gateway Health Check Timer
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=1min
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Reload systemd daemon after health check units
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Enable and start datum-gateway health check timer
|
||||
systemd:
|
||||
name: datum-gateway-healthcheck.timer
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
# ===========================================
|
||||
# Uptime Kuma Push Monitor Setup
|
||||
# ===========================================
|
||||
- name: Create Uptime Kuma push monitor setup script for DATUM Gateway
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/setup_datum_gateway_monitor.py
|
||||
content: |
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
try:
|
||||
import socketio.exceptions
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
||||
|
||||
try:
|
||||
with open('/tmp/ansible_datum_gateway_config.yml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
url = config['uptime_kuma_url']
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
monitor_name = config['monitor_name']
|
||||
|
||||
api = UptimeKumaApi(url, timeout=30)
|
||||
api.login(username, password)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
|
||||
# Find or create "services" group
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
if not group:
|
||||
try:
|
||||
api.add_monitor(type='group', name='services')
|
||||
except Exception:
|
||||
time.sleep(2)
|
||||
monitors = api.get_monitors()
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
|
||||
# Get ntfy notification ID
|
||||
notifications = api.get_notifications()
|
||||
ntfy_notification_id = None
|
||||
for notif in notifications:
|
||||
if notif.get('type') == 'ntfy':
|
||||
ntfy_notification_id = notif.get('id')
|
||||
break
|
||||
|
||||
# Check if monitor already exists
|
||||
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
|
||||
push_url = None
|
||||
|
||||
if existing:
|
||||
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
|
||||
push_token = existing.get('pushToken') or existing.get('push_token')
|
||||
if push_token:
|
||||
push_url = f"{url}/api/push/{push_token}"
|
||||
else:
|
||||
print(f"Creating push monitor '{monitor_name}'...")
|
||||
try:
|
||||
api.add_monitor(
|
||||
type=MonitorType.PUSH,
|
||||
name=monitor_name,
|
||||
parent=group['id'],
|
||||
interval=90,
|
||||
maxretries=3,
|
||||
retryInterval=60,
|
||||
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
|
||||
)
|
||||
except Exception as e:
|
||||
# socketio timeout: add_monitor may have succeeded server-side
|
||||
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
new_monitor = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
if new_monitor:
|
||||
push_token = new_monitor.get('pushToken') or new_monitor.get('push_token')
|
||||
if push_token:
|
||||
push_url = f"{url}/api/push/{push_token}"
|
||||
|
||||
api.disconnect()
|
||||
|
||||
if push_url:
|
||||
print(f"PUSH_URL={push_url}")
|
||||
with open('/tmp/datum_gateway_push_url.txt', 'w') as f:
|
||||
f.write(push_url)
|
||||
|
||||
print("SUCCESS")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: {str(e)}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
mode: "0755"
|
||||
|
||||
- name: Create temporary config for push monitor setup
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/ansible_datum_gateway_config.yml
|
||||
content: |
|
||||
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
|
||||
username: "{{ uptime_kuma_username }}"
|
||||
password: "{{ uptime_kuma_password }}"
|
||||
monitor_name: "DATUM Gateway"
|
||||
mode: "0644"
|
||||
|
||||
- name: Run Uptime Kuma push monitor setup
|
||||
command: python3 /tmp/setup_datum_gateway_monitor.py
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: monitor_setup
|
||||
changed_when: "'SUCCESS' in monitor_setup.stdout"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display monitor setup output
|
||||
debug:
|
||||
msg: "{{ monitor_setup.stdout_lines }}"
|
||||
when: monitor_setup.stdout is defined
|
||||
|
||||
- name: Read push URL from file
|
||||
slurp:
|
||||
src: /tmp/datum_gateway_push_url.txt
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: push_url_file
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Parse push URL
|
||||
set_fact:
|
||||
datum_push_url: "{{ push_url_file.content | b64decode | trim }}"
|
||||
when: push_url_file.content is defined
|
||||
|
||||
- name: Update health check service with push URL
|
||||
lineinfile:
|
||||
path: /etc/systemd/system/datum-gateway-healthcheck.service
|
||||
regexp: "^Environment=UPTIME_KUMA_PUSH_URL="
|
||||
line: "Environment=UPTIME_KUMA_PUSH_URL={{ datum_push_url }}"
|
||||
when: datum_push_url is defined
|
||||
notify: Restart datum-gateway health check timer
|
||||
|
||||
- name: Clean up temporary files
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/setup_datum_gateway_monitor.py
|
||||
- /tmp/ansible_datum_gateway_config.yml
|
||||
- /tmp/datum_gateway_push_url.txt
|
||||
|
||||
handlers:
|
||||
- name: Restart datum-gateway
|
||||
systemd:
|
||||
name: datum-gateway
|
||||
state: restarted
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Restart datum-gateway health check timer
|
||||
systemd:
|
||||
name: datum-gateway-healthcheck.timer
|
||||
state: restarted
|
||||
daemon_reload: yes
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Caddy Reverse Proxy for DATUM Dashboard (on vipy)
|
||||
# ===========================================
|
||||
- name: Configure Caddy reverse proxy for DATUM Gateway dashboard on vipy
|
||||
hosts: vipy
|
||||
become: yes
|
||||
vars_files:
|
||||
- ../../infra_vars.yml
|
||||
- ../../services_config.yml
|
||||
- ../../infra_secrets.yml
|
||||
- ./datum_gateway_vars.yml
|
||||
vars:
|
||||
datum_gateway_subdomain: "{{ subdomains.datum_gateway }}"
|
||||
datum_gateway_domain: "{{ datum_gateway_subdomain }}.{{ root_domain }}"
|
||||
caddy_sites_dir: "{{ caddy_sites_dir }}"
|
||||
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
|
||||
|
||||
tasks:
|
||||
- name: Ensure Caddy sites-enabled directory exists
|
||||
file:
|
||||
path: "{{ caddy_sites_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Ensure Caddyfile includes import directive for sites-enabled
|
||||
lineinfile:
|
||||
path: /etc/caddy/Caddyfile
|
||||
line: "import sites-enabled/*"
|
||||
insertafter: EOF
|
||||
state: present
|
||||
backup: yes
|
||||
create: yes
|
||||
mode: "0644"
|
||||
|
||||
- name: Create Caddy reverse proxy config for DATUM Gateway dashboard
|
||||
copy:
|
||||
dest: "{{ caddy_sites_dir }}/datum-gateway.conf"
|
||||
content: |
|
||||
{{ datum_gateway_domain }} {
|
||||
basic_auth {
|
||||
{{ datum_dashboard_username }} {{ datum_dashboard_password_hash }}
|
||||
}
|
||||
reverse_proxy knots-box:{{ datum_gateway_api_port }} {
|
||||
# Resolve via Tailscale MagicDNS
|
||||
transport http {
|
||||
resolvers 100.100.100.100
|
||||
}
|
||||
}
|
||||
}
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Validate Caddy config
|
||||
command: caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
register: caddy_validate
|
||||
changed_when: false
|
||||
|
||||
- name: Display Caddy validation output
|
||||
debug:
|
||||
msg: "{{ caddy_validate.stdout_lines + caddy_validate.stderr_lines }}"
|
||||
|
||||
- name: Reload Caddy
|
||||
command: systemctl reload caddy
|
||||
register: caddy_reload
|
||||
|
||||
- name: Display Caddy reload output
|
||||
debug:
|
||||
msg: "{{ caddy_reload.stdout_lines + caddy_reload.stderr_lines }}"
|
||||
|
||||
- name: Display DATUM Gateway dashboard URL
|
||||
debug:
|
||||
msg: "DATUM Gateway dashboard: https://{{ datum_gateway_domain }}"
|
||||
|
||||
# ===========================================
|
||||
# Uptime Kuma HTTP Monitor for Public Dashboard
|
||||
# ===========================================
|
||||
- name: Create Uptime Kuma HTTP monitor setup script for DATUM dashboard
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/setup_datum_http_monitor.py
|
||||
content: |
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
||||
|
||||
try:
|
||||
with open('/tmp/ansible_datum_http_config.yml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
url = config['uptime_kuma_url']
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
monitor_url = config['monitor_url']
|
||||
monitor_name = config['monitor_name']
|
||||
|
||||
api = UptimeKumaApi(url, timeout=30)
|
||||
api.login(username, password)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
if not group:
|
||||
try:
|
||||
api.add_monitor(type='group', name='services')
|
||||
except Exception:
|
||||
time.sleep(2)
|
||||
monitors = api.get_monitors()
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
|
||||
notifications = api.get_notifications()
|
||||
ntfy_notification_id = None
|
||||
for notif in notifications:
|
||||
if notif.get('type') == 'ntfy':
|
||||
ntfy_notification_id = notif.get('id')
|
||||
break
|
||||
|
||||
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
|
||||
if existing:
|
||||
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
|
||||
else:
|
||||
print(f"Creating HTTP monitor '{monitor_name}'...")
|
||||
try:
|
||||
api.add_monitor(
|
||||
type=MonitorType.HTTP,
|
||||
name=monitor_name,
|
||||
url=monitor_url,
|
||||
parent=group['id'],
|
||||
interval=60,
|
||||
maxretries=3,
|
||||
retryInterval=60,
|
||||
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
api.disconnect()
|
||||
print("SUCCESS")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: {str(e)}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
mode: "0755"
|
||||
|
||||
- name: Create temporary config for HTTP monitor
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/ansible_datum_http_config.yml
|
||||
content: |
|
||||
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
|
||||
username: "{{ uptime_kuma_username }}"
|
||||
password: "{{ uptime_kuma_password }}"
|
||||
monitor_url: "https://{{ datum_gateway_domain }}"
|
||||
monitor_name: "DATUM Gateway Dashboard"
|
||||
mode: "0644"
|
||||
|
||||
- name: Run Uptime Kuma HTTP monitor setup
|
||||
command: python3 /tmp/setup_datum_http_monitor.py
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: http_monitor_setup
|
||||
changed_when: "'SUCCESS' in http_monitor_setup.stdout"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display HTTP monitor setup output
|
||||
debug:
|
||||
msg: "{{ http_monitor_setup.stdout_lines }}"
|
||||
when: http_monitor_setup.stdout is defined
|
||||
|
||||
- name: Clean up HTTP monitor temporary files
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/setup_datum_http_monitor.py
|
||||
- /tmp/ansible_datum_http_config.yml
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Stratum Port Forwarding on vipy via systemd-socket-proxyd
|
||||
# Miners connect to vipy:23334; traffic is forwarded to knots-box:23334
|
||||
# over the Tailscale network, matching the Bitcoin P2P proxy pattern.
|
||||
# ===========================================
|
||||
- name: Setup public Stratum port forwarding on vipy via systemd-socket-proxyd
|
||||
hosts: vipy
|
||||
become: yes
|
||||
vars_files:
|
||||
- ../../infra_vars.yml
|
||||
- ../../services_config.yml
|
||||
- ../../infra_secrets.yml
|
||||
- ./datum_gateway_vars.yml
|
||||
vars:
|
||||
datum_tailscale_hostname: "knots-box"
|
||||
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
|
||||
|
||||
tasks:
|
||||
- name: Create Stratum proxy socket unit
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-stratum-proxy.socket
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Stratum Proxy Socket
|
||||
|
||||
[Socket]
|
||||
ListenStream={{ datum_gateway_stratum_port }}
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart datum-stratum-proxy socket
|
||||
|
||||
- name: Create Stratum proxy service unit
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-stratum-proxy.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Stratum Proxy to {{ datum_tailscale_hostname }}
|
||||
Requires=datum-stratum-proxy.socket
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/lib/systemd/systemd-socket-proxyd {{ datum_tailscale_hostname }}:{{ datum_gateway_stratum_port }}
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Reload systemd daemon
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Enable and start Stratum proxy socket
|
||||
systemd:
|
||||
name: datum-stratum-proxy.socket
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
- name: Allow Stratum port through UFW
|
||||
ufw:
|
||||
rule: allow
|
||||
port: "{{ datum_gateway_stratum_port | string }}"
|
||||
proto: tcp
|
||||
comment: "DATUM Gateway Stratum public access"
|
||||
|
||||
- name: Verify connectivity to knots-box Stratum via Tailscale
|
||||
wait_for:
|
||||
host: "{{ datum_tailscale_hostname }}"
|
||||
port: "{{ datum_gateway_stratum_port }}"
|
||||
timeout: 10
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display public Stratum endpoint
|
||||
debug:
|
||||
msg: "DATUM Stratum public endpoint: {{ ansible_host }}:{{ datum_gateway_stratum_port }}"
|
||||
|
||||
# ===========================================
|
||||
# Uptime Kuma TCP Monitor for Public Stratum
|
||||
# ===========================================
|
||||
- name: Create Uptime Kuma TCP monitor setup script for Stratum
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/setup_datum_stratum_tcp_monitor.py
|
||||
content: |
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import yaml
|
||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
||||
|
||||
try:
|
||||
with open('/tmp/ansible_datum_stratum_config.yml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
url = config['uptime_kuma_url']
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
monitor_host = config['monitor_host']
|
||||
monitor_port = config['monitor_port']
|
||||
monitor_name = config['monitor_name']
|
||||
|
||||
api = UptimeKumaApi(url, timeout=30)
|
||||
api.login(username, password)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
if not group:
|
||||
try:
|
||||
api.add_monitor(type='group', name='services')
|
||||
except Exception:
|
||||
time.sleep(2)
|
||||
monitors = api.get_monitors()
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
|
||||
notifications = api.get_notifications()
|
||||
ntfy_notification_id = None
|
||||
for notif in notifications:
|
||||
if notif.get('type') == 'ntfy':
|
||||
ntfy_notification_id = notif.get('id')
|
||||
break
|
||||
|
||||
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
|
||||
if existing:
|
||||
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
|
||||
else:
|
||||
print(f"Creating TCP monitor '{monitor_name}'...")
|
||||
try:
|
||||
api.add_monitor(
|
||||
type=MonitorType.PORT,
|
||||
name=monitor_name,
|
||||
hostname=monitor_host,
|
||||
port=monitor_port,
|
||||
parent=group['id'],
|
||||
interval=60,
|
||||
maxretries=3,
|
||||
retryInterval=60,
|
||||
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
api.disconnect()
|
||||
print("SUCCESS")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: {str(e)}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
mode: "0755"
|
||||
|
||||
- name: Create temporary config for Stratum TCP monitor
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/ansible_datum_stratum_config.yml
|
||||
content: |
|
||||
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
|
||||
username: "{{ uptime_kuma_username }}"
|
||||
password: "{{ uptime_kuma_password }}"
|
||||
monitor_host: "{{ ansible_host }}"
|
||||
monitor_port: {{ datum_gateway_stratum_port }}
|
||||
monitor_name: "DATUM Stratum (public)"
|
||||
mode: "0644"
|
||||
|
||||
- name: Run Uptime Kuma TCP monitor setup
|
||||
command: python3 /tmp/setup_datum_stratum_tcp_monitor.py
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: tcp_monitor_setup
|
||||
changed_when: "'SUCCESS' in tcp_monitor_setup.stdout"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display TCP monitor setup output
|
||||
debug:
|
||||
msg: "{{ tcp_monitor_setup.stdout_lines }}"
|
||||
when: tcp_monitor_setup.stdout is defined
|
||||
|
||||
- name: Clean up Stratum TCP monitor temporary files
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/setup_datum_stratum_tcp_monitor.py
|
||||
- /tmp/ansible_datum_stratum_config.yml
|
||||
|
||||
handlers:
|
||||
- name: Restart datum-stratum-proxy socket
|
||||
systemd:
|
||||
name: datum-stratum-proxy.socket
|
||||
state: restarted
|
||||
|
|
@ -25,6 +25,9 @@ subdomains:
|
|||
# Mempool Block Explorer (on mempool_box, proxied via vipy)
|
||||
mempool: mempool
|
||||
|
||||
# DATUM Gateway dashboard (on knots_box, proxied via vipy)
|
||||
datum_gateway: datum
|
||||
|
||||
# Caddy configuration
|
||||
caddy_sites_dir: /etc/caddy/sites-enabled
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue