Add Ansible playbooks for MeshCore monitoring node deployment
Roles: base (apt, tailscale, motd), meshcore_cli (pipx), meshcore_capture (agessaman/meshcore-packet-capture + .env.local template), scripts (voltage, bandwidth). Host vars contain per-device serial ports; group vars hold shared MQTT broker config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,2 +1,76 @@
|
|||||||
# dotmesh-monitor
|
# dotmesh-monitor
|
||||||
|
|
||||||
|
Ansible playbooks for deploying MeshCore monitoring nodes (Raspberry Pi Zero W / Zero 2 W).
|
||||||
|
|
||||||
|
## Hosts
|
||||||
|
|
||||||
|
| Host | Hardware | Group |
|
||||||
|
|---|---|---|
|
||||||
|
| dm-baldock | Pi Zero W (armv6) | zero_w |
|
||||||
|
| dm-ashwell | Pi Zero 2 W (armv7) | zero2_w |
|
||||||
|
| dm-edworth | Pi Zero 2 W (armv7) | zero2_w |
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**Local machine:**
|
||||||
|
```bash
|
||||||
|
pip install ansible
|
||||||
|
# or: sudo apt install ansible
|
||||||
|
```
|
||||||
|
|
||||||
|
**New Pi node checklist:**
|
||||||
|
1. Flash Raspberry Pi OS Lite (Bookworm), connect to WiFi
|
||||||
|
2. Install Tailscale and join the network
|
||||||
|
3. Ensure `david` user exists with sudo access
|
||||||
|
4. Connect the MeshCore device via USB, then find its serial ID:
|
||||||
|
```bash
|
||||||
|
ls /dev/serial/by-id/
|
||||||
|
```
|
||||||
|
5. Set `serial_port` in `ansible/host_vars/<hostname>.yml`
|
||||||
|
|
||||||
|
SSH key auth is required. From this machine:
|
||||||
|
```bash
|
||||||
|
ssh-copy-id david@<hostname>.tail740bb.ts.net
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**Deploy to a single host (recommended for first run / testing):**
|
||||||
|
```bash
|
||||||
|
cd ansible
|
||||||
|
ansible-playbook -i inventory.yml site.yml --limit dm-edworth
|
||||||
|
```
|
||||||
|
|
||||||
|
**Deploy to all nodes:**
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventory.yml site.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dry run:**
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventory.yml site.yml --limit dm-edworth --check
|
||||||
|
```
|
||||||
|
|
||||||
|
If sudo requires a password, add `--ask-become-pass`.
|
||||||
|
|
||||||
|
You'll be prompted for a Tailscale auth key — leave blank if the node is already authenticated.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
1. **base** — apt upgrade, installs screen/pipx/vnstat/git, sets MOTD, installs and authenticates Tailscale
|
||||||
|
2. **meshcore_cli** — installs `meshcore-cli` via pipx
|
||||||
|
3. **meshcore_capture** — runs the agessaman/meshcore-packet-capture install script, writes `.env.local` config, enables `meshcore-capture.service`, deploys update/log helper scripts
|
||||||
|
4. **scripts** — deploys `voltage.sh` and `bandwidth.sh`
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Shared MQTT config lives in `group_vars/meshcore.yml`. Per-host serial port is in `host_vars/<hostname>.yml`.
|
||||||
|
|
||||||
|
Running the playbook again re-applies `.env.local` and restarts the service if it changed — safe to run on already-deployed nodes.
|
||||||
|
|
||||||
|
## Credentials note
|
||||||
|
|
||||||
|
`group_vars/meshcore.yml` contains MQTT credentials in plaintext. Consider encrypting with Ansible Vault if this repo is shared:
|
||||||
|
```bash
|
||||||
|
ansible-vault encrypt_string 'yourpassword' --name mqtt_ukmesh_password
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
remote_user: david
|
||||||
|
ansible_user: "{{ remote_user }}"
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
meshcore_description: "meshcore management"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
packetcapture_update_repo: agessaman/meshcore-packet-capture
|
||||||
|
packetcapture_update_branch: main
|
||||||
|
|
||||||
|
packetcapture_iata: LTN
|
||||||
|
packetcapture_advert_interval_hours: 11
|
||||||
|
packetcapture_log_level: INFO
|
||||||
|
packetcapture_owner_public_key: 6C6E8B1B0EE79816941D85928D6ABB0002F46D4ED15AD8D8D2A8B56A3022D13B
|
||||||
|
packetcapture_owner_email: david@datajack.org
|
||||||
|
|
||||||
|
# MQTT broker credentials
|
||||||
|
mqtt_ukmesh_username: dotdavid
|
||||||
|
mqtt_ukmesh_password: ApNPXjRj4xtRoluW2X22
|
||||||
|
mqtt_meshrank_token: 908E8CB151EE04E37A4B6529CD0263C3
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
serial_port: /dev/serial/by-id/usb-Espressif_USB_JTAG_serial_debug_unit_1C:DB:D4:5A:AA:B0-if00
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
serial_port: /dev/serial/by-id/usb-Espressif_USB_JTAG_serial_debug_unit_98:3D:AE:61:74:60-if00
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Find this by connecting the MeshCore device then running: ls /dev/serial/by-id/
|
||||||
|
serial_port: FILL_IN_SERIAL_PORT
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
all:
|
||||||
|
children:
|
||||||
|
meshcore:
|
||||||
|
children:
|
||||||
|
zero_w:
|
||||||
|
hosts:
|
||||||
|
dm-baldock:
|
||||||
|
ansible_host: dm-baldock
|
||||||
|
zero2_w:
|
||||||
|
hosts:
|
||||||
|
dm-ashwell:
|
||||||
|
ansible_host: dm-ashwell
|
||||||
|
dm-edworth:
|
||||||
|
ansible_host: dm-edworth
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
- name: Install base packages
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- screen
|
||||||
|
- pipx
|
||||||
|
- vnstat
|
||||||
|
- git
|
||||||
|
state: present
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Set MOTD
|
||||||
|
template:
|
||||||
|
src: motd.j2
|
||||||
|
dest: /etc/motd
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Add Tailscale apt signing key
|
||||||
|
get_url:
|
||||||
|
url: https://pkgs.tailscale.com/stable/debian/bookworm.nosetup.sh
|
||||||
|
dest: /tmp/tailscale-setup.sh
|
||||||
|
mode: "0755"
|
||||||
|
when: tailscale_auth_key != ""
|
||||||
|
|
||||||
|
- name: Run Tailscale install script
|
||||||
|
shell: sh /tmp/tailscale-setup.sh
|
||||||
|
args:
|
||||||
|
creates: /usr/bin/tailscale
|
||||||
|
become: true
|
||||||
|
when: tailscale_auth_key != ""
|
||||||
|
|
||||||
|
- name: Enable and start tailscaled
|
||||||
|
systemd:
|
||||||
|
name: tailscaled
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
become: true
|
||||||
|
when: tailscale_auth_key != ""
|
||||||
|
|
||||||
|
- name: Authenticate Tailscale
|
||||||
|
shell: tailscale up --authkey {{ tailscale_auth_key }}
|
||||||
|
become: true
|
||||||
|
when: tailscale_auth_key != ""
|
||||||
|
no_log: true
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
__ __ __
|
||||||
|
____/ /___ / /_____ ___ ___ _____/ /_
|
||||||
|
/ __ / __ \/ __/ __ `__ \/ _ \/ ___/ __ \
|
||||||
|
/ /_/ / /_/ / /_/ / / / / / __(__ ) / / /
|
||||||
|
\__,_/\____/\__/_/ /_/ /_/\___/____/_/ /_/
|
||||||
|
|
||||||
|
{{ inventory_hostname }} :: {{ meshcore_description }}
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: restart meshcore-capture
|
||||||
|
systemd:
|
||||||
|
name: meshcore-capture
|
||||||
|
state: restarted
|
||||||
|
become: true
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
- name: Run meshcore-packet-capture install script
|
||||||
|
shell: |
|
||||||
|
bash <(curl -fsSL https://raw.githubusercontent.com/agessaman/meshcore-packet-capture/main/install.sh)
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
creates: /etc/systemd/system/meshcore-capture.service
|
||||||
|
become: false
|
||||||
|
|
||||||
|
- name: Write .env.local config
|
||||||
|
template:
|
||||||
|
src: env.local.j2
|
||||||
|
dest: "{{ ansible_env.HOME }}/.meshcore-packet-capture/.env.local"
|
||||||
|
mode: "0640"
|
||||||
|
become: false
|
||||||
|
notify: restart meshcore-capture
|
||||||
|
|
||||||
|
- name: Enable and start meshcore-capture service
|
||||||
|
systemd:
|
||||||
|
name: meshcore-capture
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Deploy meshcore-capture-update script
|
||||||
|
copy:
|
||||||
|
content: "#!/usr/bin/env bash\nbash <(curl -fsSL https://raw.githubusercontent.com/agessaman/meshcore-packet-capture/main/install.sh)\n"
|
||||||
|
dest: "{{ ansible_env.HOME }}/meshcore-capture-update.sh"
|
||||||
|
mode: "0755"
|
||||||
|
become: false
|
||||||
|
|
||||||
|
- name: Deploy meshcore-capture-logs script
|
||||||
|
copy:
|
||||||
|
content: "#!/usr/bin/env bash\nsudo journalctl -u meshcore-capture -f\n"
|
||||||
|
dest: "{{ ansible_env.HOME }}/meshcore-capture-logs.sh"
|
||||||
|
mode: "0755"
|
||||||
|
become: false
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# MeshCore Packet Capture Configuration
|
||||||
|
# Managed by Ansible - local changes will be overwritten on next playbook run
|
||||||
|
|
||||||
|
# Update source
|
||||||
|
PACKETCAPTURE_UPDATE_REPO={{ packetcapture_update_repo }}
|
||||||
|
PACKETCAPTURE_UPDATE_BRANCH={{ packetcapture_update_branch }}
|
||||||
|
|
||||||
|
# Connection Configuration
|
||||||
|
PACKETCAPTURE_CONNECTION_TYPE=serial
|
||||||
|
PACKETCAPTURE_SERIAL_PORTS={{ serial_port }}
|
||||||
|
|
||||||
|
# Location Code
|
||||||
|
PACKETCAPTURE_IATA={{ packetcapture_iata }}
|
||||||
|
|
||||||
|
# Advert Settings
|
||||||
|
PACKETCAPTURE_ADVERT_INTERVAL_HOURS={{ packetcapture_advert_interval_hours }}
|
||||||
|
|
||||||
|
# Logging Settings
|
||||||
|
PACKETCAPTURE_LOG_LEVEL={{ packetcapture_log_level }}
|
||||||
|
PACKETCAPTURE_OWNER_PUBLIC_KEY={{ packetcapture_owner_public_key }}
|
||||||
|
PACKETCAPTURE_OWNER_EMAIL={{ packetcapture_owner_email }}
|
||||||
|
|
||||||
|
# MQTT Broker 1 - LetsMesh.net Packet Analyzer (US)
|
||||||
|
PACKETCAPTURE_MQTT1_ENABLED=true
|
||||||
|
PACKETCAPTURE_MQTT1_SERVER=mqtt-us-v1.letsmesh.net
|
||||||
|
PACKETCAPTURE_MQTT1_PORT=443
|
||||||
|
PACKETCAPTURE_MQTT1_TRANSPORT=websockets
|
||||||
|
PACKETCAPTURE_MQTT1_USE_TLS=true
|
||||||
|
PACKETCAPTURE_MQTT1_USE_AUTH_TOKEN=true
|
||||||
|
PACKETCAPTURE_MQTT1_TOKEN_AUDIENCE=mqtt-us-v1.letsmesh.net
|
||||||
|
PACKETCAPTURE_MQTT1_KEEPALIVE=120
|
||||||
|
PACKETCAPTURE_MQTT1_TOPIC_STATUS=meshcore/{IATA}/{PUBLIC_KEY}/status
|
||||||
|
PACKETCAPTURE_MQTT1_TOPIC_PACKETS=meshcore/{IATA}/{PUBLIC_KEY}/packets
|
||||||
|
|
||||||
|
# MQTT Broker 2 - LetsMesh.net Packet Analyzer (EU)
|
||||||
|
PACKETCAPTURE_MQTT2_ENABLED=true
|
||||||
|
PACKETCAPTURE_MQTT2_SERVER=mqtt-eu-v1.letsmesh.net
|
||||||
|
PACKETCAPTURE_MQTT2_PORT=443
|
||||||
|
PACKETCAPTURE_MQTT2_TRANSPORT=websockets
|
||||||
|
PACKETCAPTURE_MQTT2_USE_TLS=true
|
||||||
|
PACKETCAPTURE_MQTT2_USE_AUTH_TOKEN=true
|
||||||
|
PACKETCAPTURE_MQTT2_TOKEN_AUDIENCE=mqtt-eu-v1.letsmesh.net
|
||||||
|
PACKETCAPTURE_MQTT2_KEEPALIVE=120
|
||||||
|
PACKETCAPTURE_MQTT2_TOPIC_STATUS=meshcore/{IATA}/{PUBLIC_KEY}/status
|
||||||
|
PACKETCAPTURE_MQTT2_TOPIC_PACKETS=meshcore/{IATA}/{PUBLIC_KEY}/packets
|
||||||
|
|
||||||
|
# MQTT Broker 3 - MeshRank
|
||||||
|
PACKETCAPTURE_MQTT3_ENABLED=true
|
||||||
|
PACKETCAPTURE_MQTT3_SERVER=meshrank.net
|
||||||
|
PACKETCAPTURE_MQTT3_PORT=8883
|
||||||
|
PACKETCAPTURE_MQTT3_TRANSPORT=tcp
|
||||||
|
PACKETCAPTURE_MQTT3_USE_TLS=true
|
||||||
|
PACKETCAPTURE_MQTT3_TLS_VERIFY=true
|
||||||
|
PACKETCAPTURE_MQTT3_TOPIC_PACKETS=meshrank/uplink/{{ mqtt_meshrank_token }}/{PUBLIC_KEY}/packets
|
||||||
|
PACKETCAPTURE_MQTT3_TOPIC_STATUS=meshrank/uplink/{{ mqtt_meshrank_token }}/{PUBLIC_KEY}/status
|
||||||
|
|
||||||
|
# MQTT Broker 4 - UKMesh
|
||||||
|
PACKETCAPTURE_MQTT4_ENABLED=true
|
||||||
|
PACKETCAPTURE_MQTT4_SERVER=mqtt.ukmesh.com
|
||||||
|
PACKETCAPTURE_MQTT4_PORT=443
|
||||||
|
PACKETCAPTURE_MQTT4_TRANSPORT=websockets
|
||||||
|
PACKETCAPTURE_MQTT4_USE_TLS=true
|
||||||
|
PACKETCAPTURE_MQTT4_USERNAME={{ mqtt_ukmesh_username }}
|
||||||
|
PACKETCAPTURE_MQTT4_PASSWORD={{ mqtt_ukmesh_password }}
|
||||||
|
PACKETCAPTURE_MQTT4_TOPIC_STATUS=meshcore/{IATA}/{PUBLIC_KEY}/status
|
||||||
|
PACKETCAPTURE_MQTT4_TOPIC_PACKETS=meshcore/{IATA}/{PUBLIC_KEY}/packets
|
||||||
|
|
||||||
|
# MQTT Broker 5 - MeshMapper
|
||||||
|
PACKETCAPTURE_MQTT5_ENABLED=true
|
||||||
|
PACKETCAPTURE_MQTT5_SERVER=mqtt.meshmapper.cc
|
||||||
|
PACKETCAPTURE_MQTT5_PORT=443
|
||||||
|
PACKETCAPTURE_MQTT5_TRANSPORT=websockets
|
||||||
|
PACKETCAPTURE_MQTT5_USE_TLS=true
|
||||||
|
PACKETCAPTURE_MQTT5_USE_AUTH_TOKEN=true
|
||||||
|
PACKETCAPTURE_MQTT5_TOKEN_AUDIENCE=mqtt.meshmapper.cc
|
||||||
|
PACKETCAPTURE_MQTT5_TOPIC_STATUS=meshcore/{IATA}/{PUBLIC_KEY}/status
|
||||||
|
PACKETCAPTURE_MQTT5_TOPIC_PACKETS=meshcore/{IATA}/{PUBLIC_KEY}/packets
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure pipx path is configured
|
||||||
|
shell: pipx ensurepath
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
become: false
|
||||||
|
register: pipx_ensurepath
|
||||||
|
changed_when: "'already' not in pipx_ensurepath.stdout"
|
||||||
|
|
||||||
|
- name: Install meshcore-cli via pipx
|
||||||
|
shell: pipx install meshcore-cli
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
creates: "{{ ansible_env.HOME }}/.local/bin/meshcore-cli"
|
||||||
|
become: false
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
vnstat
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
SCRIPT=$(basename "$0")
|
||||||
|
|
||||||
|
# fetch status
|
||||||
|
STATUS=$(vcgencmd get_throttled | cut -d "=" -f 2)
|
||||||
|
|
||||||
|
# decode - https://www.raspberrypi.com/documentation/computers/os.html#get_throttled
|
||||||
|
echo "vcgencmd get_throttled ($STATUS)"
|
||||||
|
IFS=","
|
||||||
|
for BITMAP in \
|
||||||
|
00,"currently under-voltage" \
|
||||||
|
01,"ARM frequency currently capped" \
|
||||||
|
02,"currently throttled" \
|
||||||
|
03,"soft temperature limit reached" \
|
||||||
|
16,"under-voltage has occurred since last reboot" \
|
||||||
|
17,"ARM frequency capping has occurred since last reboot" \
|
||||||
|
18,"throttling has occurred since last reboot" \
|
||||||
|
19,"soft temperature reached since last reboot"
|
||||||
|
do set -- $BITMAP
|
||||||
|
if [ $(($STATUS & 1 << $1)) -ne 0 ] ; then echo " $2" ; fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "vcgencmd measure_volts:"
|
||||||
|
for S in core sdram_c sdram_i sdram_p ; do printf '%9s %s\n' "$S" "$(vcgencmd measure_volts $S)" ; done
|
||||||
|
|
||||||
|
echo "Temperature: $(vcgencmd measure_temp)"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy voltage.sh
|
||||||
|
copy:
|
||||||
|
src: voltage.sh
|
||||||
|
dest: "{{ ansible_env.HOME }}/voltage.sh"
|
||||||
|
mode: "0755"
|
||||||
|
become: false
|
||||||
|
|
||||||
|
- name: Deploy bandwidth.sh
|
||||||
|
copy:
|
||||||
|
src: bandwidth.sh
|
||||||
|
dest: "{{ ansible_env.HOME }}/bandwidth.sh"
|
||||||
|
mode: "0755"
|
||||||
|
become: false
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy MeshCore monitoring nodes
|
||||||
|
hosts: meshcore
|
||||||
|
vars_prompt:
|
||||||
|
- name: tailscale_auth_key
|
||||||
|
prompt: "Tailscale auth key (leave blank to skip)"
|
||||||
|
private: true
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Update apt cache and upgrade packages
|
||||||
|
apt:
|
||||||
|
update_cache: true
|
||||||
|
upgrade: dist
|
||||||
|
autoremove: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- base
|
||||||
|
- meshcore_cli
|
||||||
|
- meshcore_capture
|
||||||
|
- scripts
|
||||||
Reference in New Issue
Block a user