Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ init_script/.env
init_script/cameras.csv
init_script/venv
venv/
host_vars/
/host_vars/*
!/host_vars/template/
inventory/inventory
inventory/hosts_*
group_vars
!group_vars/template
inventory/host_vars/
inventory/group_vars/
/group_vars/*
!/group_vars/template/
id_rsa
.ansible/
.env
Expand Down
46 changes: 45 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,58 @@ This repository contains Ansible playbooks and configuration files to manage and

### Pre-requisites

1) git clone the pi-manager-X repository, corresponding to the github repository containing your inventory and host_vars file.
1) git clone the pi-manager-X repository, corresponding to the github repository containing your inventory and host_vars file. This repo is **private** — see [The pi-manager-X sister repo](#the-pi-manager-x-sister-repo) below for what it must contain.
2) create a .env file in the root of this repository (used by your Makefile) and set the REPO_PATH variable accordingly (for ex : ../pi-manager-X)
3) create the .vault_passwrd file containing your ansible vault password

Ensure the following tools are installed by using the Makefile "dependencies" command.

YOU WILL NEVER NEED TO MODIFY THIS REPOSITORY. All the modification must to be done by the pyronear team. If you want to install a new raspberry pi, you only need to modify the files in your pi-manager-X repository.

### The pi-manager-X sister repo

The sister repo holds your fleet's inventory and secrets. It is private, but its shape is fixed:

```
pi-manager-X/
├── .vault_passwrd # ansible-vault password, read in place via VAULT_PASSWORD_FILE (.env)
├── id_rsa # SSH private key, read in place via SSH_PRIVATE_KEY_FILE (.env)
├── inventory/
│ ├── hosts_prod # production inventory ─┐
│ ├── hosts_dev # dev inventory │ copied into this
│ └── group_vars/ │ repo by `make prepare`
│ ├── all/{vars.yml,vars.vault.yml} │
│ ├── alert_server/vars.yml │
│ ├── annotation_server/vars.yml │
│ ├── engine_servers/{vars.yml,vars.vault.yml} │
│ ├── envdev/vars.yml │
│ ├── envprod/vars.yml │
│ └── pi_zero/vars.yml │
└── host_vars/ ─┘
├── <engine-host>/{vars.yml,vars.vault.yml}
├── <pi-zero-host>/{vars.yml,vars.vault.yml}
├── <alert-server-host>/vars.vault.yml
└── <annotation-server-host>/vars.vault.yml
```

`make prepare` copies `inventory/hosts*`, `host_vars/`, and `inventory/group_vars/` into this repo on every run. The vault password file and SSH key stay in the sister repo and are referenced in place through `.env`.

Worked examples for every file above ship with this repo as templates — copy them into your sister repo and edit:

- `inventory/inventory.template` — inventory groups skeleton (see `inventory/hosts_*` for fully filled-in examples).
- `group_vars/template/` — one folder per group, including `vars.vault.yml` files listing the expected vault keys.
- `host_vars/template/` — one folder per host kind (`engine`, `pi_zero`, `alert_server`, `annotation_server`).

`vars.vault.yml` files in the templates are intentionally **plain YAML with `CHANGE_ME` placeholders** so the expected keys are visible. Encrypt each one before committing it to your sister repo:

```bash
ansible-vault encrypt host_vars/<host>/vars.vault.yml
```

Two extras worth knowing about:
- The `static_ip_address` of each engine and pi_zero is referenced by name across host_vars (e.g. the pi_zero watchdog derives `MAIN_PI_IP` from `hostvars[relay_host]`), so keep the values consistent.
- After the first run of `rpi-init-pi-zero.yml`, the Pi Zero reboots onto its static IP — update `ansible_host` in its `vars.yml` to match `static_ip_address` before the next run.

### Installing a new Raspberry Pi
See [How to configure a new raspberry](./docs/howto/how-to-configure-a-new-raspberry.md)

Expand Down
9 changes: 9 additions & 0 deletions group_vars/template/all/vars.vault.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# TEMPLATE — placeholders only. Encrypt before committing to your pi-manager-X repo:
# ansible-vault encrypt inventory/group_vars/all/vars.vault.yml
---
# Telegram bot used by the alert API to push notifications.
TELEGRAM_TOKEN: "CHANGE_ME"

# Docker Hub password used by roles/servers/tasks/main.yml to docker login.
# The username is currently hardcoded to `pyronear` in that task.
dockerhub_password: "CHANGE_ME"
29 changes: 29 additions & 0 deletions group_vars/template/engine_servers/vars.vault.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# TEMPLATE — placeholders only. Encrypt before committing to your pi-manager-X repo:
# ansible-vault encrypt inventory/group_vars/engine_servers/vars.vault.yml
---
# Default camera credentials (override per host in host_vars/<host>/vars.vault.yml).
CAM_USER: "admin"
CAM_PWD: "CHANGE_ME"

# Default Wi-Fi (override per host in host_vars/<host>/vars.vault.yml).
# Consumed by the `wifi` role; entries with a static IP are also picked up by `static_ip`.
wifi_connections:
- ssid: "Pyronear"
password: "CHANGE_ME"
priority: 10

# Grafana Alloy config rendered into /etc/alloy/config.alloy by the
# grafana.grafana.alloy role. Contains the remote_write basic-auth token,
# hence why it lives in the vault file. See the role README for the full
# block; only the basic_auth username/password should change between sites.
alloy_config: |
prometheus.remote_write "metrics_service" {
endpoint {
url = "https://<your-grafana-cloud-prom-endpoint>/api/prom/push"
basic_auth {
username = "CHANGE_ME"
password = "CHANGE_ME"
}
}
}
# ... rest of the Alloy pipeline (loki.write, prometheus.exporter.unix, etc.)
28 changes: 28 additions & 0 deletions group_vars/template/envdev/vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
# Dev environment.
# Hosts opt in via the `envdev` group in inventory (see inventory.template).

prefix: "ansible" # short, lowercase, used to derive resource names
pyronear_version: new-datamodel
api_dns: "alertapidev.pyronear.org"
openvpn_server_dns: "bastion{{ prefix }}.pyronear.org"
openvpn_server_ansible_host: "ovh-dev-3" # inventory name of the OpenVPN host

# Encrypt with: ansible-vault encrypt_string '<password>' --name 'openvpn_ca_password'
openvpn_ca_password: "CHANGE_ME" # vault-encrypted in real repos

# Object storage (OVH S3) used by the dev alert API.
S3_ENDPOINT_URL: https://s3.sbg.io.cloud.ovh.net/
S3_ACCESS_KEY: "CHANGE_ME" # vault
S3_SECRET_KEY: "CHANGE_ME" # vault
S3_REGION: sbg

# Public hostnames served by the dev platform-react server.
platform_react_url_frontend: platformv3.pyronear.org
platform_react_url_backend: https://alertapi.pyronear.org
platform_react_url_livestream: https://livestream.pyronear.org

# Sites visible per dev org.
platform_react_sites_per_organization:
- name: example-org
sites: ["dev-site"]
48 changes: 48 additions & 0 deletions group_vars/template/envprod/vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
# Production environment.
# Hosts opt in via the `envprod` group in inventory (see inventory.template).

prefix: "fr" # short, lowercase, used to derive resource names (e.g. bastion{{ prefix }}.pyronear.org)
pyronear_version: latest
api_dns: "alertapi.pyronear.org"
mediamtx_server_ip: "CHANGE_ME" # public IP of the mediamtx host (e.g. 91.134.47.14)
openvpn_server_dns: "bastion{{ prefix }}.pyronear.org"
openvpn_server_ansible_host: "fr_openvpn" # inventory name of the OpenVPN host

# Encrypt with: ansible-vault encrypt_string '<password>' --name 'openvpn_ca_password'
openvpn_ca_password: "CHANGE_ME" # vault-encrypted in real repos

# Object storage (OVH S3) used by alert API + annotation API.
S3_ENDPOINT_URL: https://s3.gra.io.cloud.ovh.net/
S3_ACCESS_KEY: "CHANGE_ME" # vault
S3_SECRET_KEY: "CHANGE_ME" # vault
S3_REGION: gra

# Public hostnames served by the platform-react server.
platform_react_url_backend: https://alertapi.pyronear.org

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are those url wanted to be exposed ? ( same comment elsewhere whenever they're present)

platform_react_url_livestream: https://livestream.pyronear.org
platform_react_url_frontend: platform.pyronear.org

# One entry per organization in the alert API. Each lists the engine hostnames
# (must match inventory) whose detections that org is allowed to see.
platform_react_sites_per_organization:
- name: example-org
sites: ["site-a", "site-b"]

# alert API configuration.
alert_api_docker_version: latest
alert_api_proxy_url: alertapi.pyronear.org
alert_api_s3_proxy_url: assets.pyronear.org
alert_api_postgres_db: pyroapi
alert_api_postgres_pwd: "CHANGE_ME" # vault
alert_api_superuser_pwd: "CHANGE_ME" # vault
alert_api_jwt_secret: "CHANGE_ME" # vault
alert_api_s3_access_key: "CHANGE_ME" # vault
alert_api_s3_secret_key: "CHANGE_ME" # vault
alert_api_s3_region: gra
alert_api_s3_url: https://s3.gra.io.cloud.ovh.net/
alert_api_server_name: ovh-alert-api-prod # used to derive S3 bucket names
alert_api_risk_api_url: https://riskapi.pyronear.org
alert_api_risk_refresh_hour_utc: 4
alert_api_risk_api_login: "CHANGE_ME" # vault
alert_api_risk_api_pwd: "CHANGE_ME" # vault
19 changes: 19 additions & 0 deletions group_vars/template/pi_zero/vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
ansible_user: pi
ansible_become_pass: "{{ ansible_password }}"

# Access Pi Zeros via their assigned engine (relay host) using SSH ProxyJump.
# Each pi_zero host must define `relay_host` in host_vars pointing to the
# engine's inventory hostname (e.g. relay_host: chambery).
ansible_ssh_common_args: >-
-o StrictHostKeyChecking=no
-o ProxyCommand="sshpass -p {{ hostvars[relay_host]['ansible_password'] }} ssh -o StrictHostKeyChecking=no -W %h:%p {{ hostvars[relay_host]['ansible_user'] }}@{{ hostvars[relay_host]['ansible_host'] }}"

# WiFi — override per host or per site in host_vars / group_vars
# wifi_ssid: ""
# wifi_password: ""

# Static IP — must be set in host_vars for each Pi Zero.
# After first run, update ansible_host to match static_ip_address.
# static_ip_address: "192.168.X.Y"
# static_ip_gateway: "192.168.X.1"
11 changes: 11 additions & 0 deletions host_vars/template/alert_server/vars.vault.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# TEMPLATE — placeholders only. Encrypt before committing to your pi-manager-X repo:
# ansible-vault encrypt host_vars/<alert-server-host>/vars.vault.yml
---
# Postgres database used by the alert API container.
POSTGRES_USER: "dbadmin"
POSTGRES_PASSWORD: "CHANGE_ME"
POSTGRES_DB: "pyroapi"

# For dev hosts you typically override the env prefix and DNS here, e.g.:
# prefix: "frdev"
# api_dns: "apidev.pyronear.org"
12 changes: 12 additions & 0 deletions host_vars/template/annotation_server/vars.vault.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# TEMPLATE — placeholders only. Encrypt before committing to your pi-manager-X repo:
# ansible-vault encrypt host_vars/<annotation-server-host>/vars.vault.yml
---
# Postgres database used by the annotation API container.
POSTGRES_USER: "dbadmin"
POSTGRES_PASSWORD: "CHANGE_ME"
POSTGRES_DB: "pyroannotation"

# Annotation API basic-auth credentials and JWT signing secret.
AUTH_USERNAME: "admin"
AUTH_PASSWORD: "CHANGE_ME"
JWT_SECRET: "CHANGE_ME" # any high-entropy hex string
28 changes: 28 additions & 0 deletions host_vars/template/engine/vars.vault.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# TEMPLATE — placeholders only. Encrypt before committing to your pi-manager-X repo:
# ansible-vault encrypt host_vars/<host>/vars.vault.yml
---
# SSH / sudo on the Pi (user defined in group_vars/engine_servers/vars.yml — `pi`).
ansible_password: "CHANGE_ME"
# ansible_become_password: "CHANGE_ME" # only if different from ansible_password

# Camera credentials used by pyro-engine to talk to the cameras.
CAM_USER: "admin"
CAM_PWD: "CHANGE_ME"

# OpenVPN client password for this engine's certificate (matches the cert generated
# by the pyronear.openvpn role on the OpenVPN server).
open_vpn_password: "CHANGE_ME"
openvpn_client_password: "CHANGE_ME"

# Optional — only if this engine pushes streams to mediamtx with a per-engine password.
# mediamtx_pass: "CHANGE_ME"

# Wi-Fi — the `wifi` role iterates over `wifi_connections` (nmcli-based).
# Higher `priority` wins when several APs are visible.
wifi_connections:
- ssid: "Pyronear"
password: "CHANGE_ME"
priority: 10
- ssid: "Backup-AP"
password: "CHANGE_ME"
priority: 8
40 changes: 40 additions & 0 deletions host_vars/template/engine/vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
# Template for an engine host (Raspberry Pi running pyro-engine).
# Copy this folder to your pi-manager-X repo under host_vars/<your-host>/.
# Hostname must match the entry under engine_servers: in inventory/hosts_prod.

# Cameras attached to this engine.
# Keys are camera IPs on the engine's LAN. Values follow group_vars/engine_servers
# engine_json_schema. `id` is the camera id returned by init_script/create_cameras.py.
config_json: |
{
"192.168.1.11": {
"pose_ids": [401, 402, 403, 404],
"adapter": "reolink-823S2",
"id": "122",
"name": "<site>-01",
"bbox_mask_url": "",
"poses": [0, 1, 2, 3],
"token": "",
"type": "ptz"
},
"192.168.1.12": {
"pose_ids": [405, 406, 407, 408],
"adapter": "reolink-823S2",
"id": "123",
"name": "<site>-02",
"bbox_mask_url": "",
"poses": [0, 1, 2, 3],
"token": "",
"type": "ptz"
}
}

# Static IP assigned to the Pi on its camera LAN (set by the static_ip role).
static_ip_interface: eth0
static_ip_address: 192.168.1.99
static_ip_gateway: 192.168.1.1

# Name of the associated Pi Zero in inventory (omit if no Pi Zero).
# The engine_cron role uses this to derive PIZERO_IP from hostvars.
pi_zero_hostname: <site>-pi-zero
17 changes: 17 additions & 0 deletions host_vars/template/pi_zero/vars.vault.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# TEMPLATE — placeholders only. Encrypt before committing to your pi-manager-X repo:
# ansible-vault encrypt host_vars/<host>-pi-zero/vars.vault.yml
---
# SSH / sudo on the Pi Zero.
ansible_password: "CHANGE_ME"

# Wi-Fi — the `wifi` role iterates over `wifi_connections` (nmcli-based).
# The `static_ip` role then applies a static IP for entries that define one.
wifi_connections:
- ssid: "Pyronear"
password: "CHANGE_ME"
priority: 10
- ssid: "RUT200_XXXX" # e.g. a Teltonika 4G router
password: "CHANGE_ME"
priority: 8
static_ip_address: "{{ static_ip_address }}"
static_ip_gateway: 192.168.1.1
18 changes: 18 additions & 0 deletions host_vars/template/pi_zero/vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
# Template for a Pi Zero host (watchdog companion to an engine).
# Copy to your pi-manager-X repo under host_vars/<your-host>-pi-zero/.
# Hostname must match the entry under pi_zero: in inventory/hosts_prod.

# First boot uses DHCP — set ansible_host to the lease IP you find with `nmap`/router UI.
# After the first run of rpi-init-pi-zero.yml the Pi Zero reboots onto its static IP;
# update ansible_host below to match static_ip_address before the next run.
ansible_host: 192.168.1.24 # DHCP ip first, then static after init

# Engine that proxies SSH into this Pi Zero. Must be an inventory hostname under
# engine_servers. The pi_zero_watchdog role derives MAIN_PI_IP and CAM_IPS from
# hostvars[relay_host].
relay_host: <site>

# Static IP assigned by the static_ip role on the camera LAN.
static_ip_address: 192.168.1.98
static_ip_gateway: 192.168.1.1