Skip to main content

Bulk fleet operations for Foundries.io devices

Project description

fiofleet

Bulk fleet operations for Foundries.io devices.

fioctl is great for single-device work. fiofleet is designed for when you have a large fleet of devices, and want to enable/disable wireguard vpn and run ssh commands remotely en masse. It's a thin, scriptable layer over the Foundries OTA API (and, optionally, fioctl).

Features

  • Device inventory — list/show devices, filter by tag or group, with online/offline detection.
  • OTA update reports — for a tag/group, show each device's last update broken down by stage (download → install), exactly which stage failed and the error the device reported, plus a fleet-level pass/fail summary. Drill into a single device's full update timeline with ota stages.
  • WireGuard fleet management — enable/disable/status across many devices at once, and wait until the platform confirms each device is a live VPN peer. Works through the config API directly, so fioctl is not required.
  • Fan-out SSH/exec — run a command (or open a shell) across a tag/group in parallel, and collect the results as JSON (--json) so you can drive scripts off them. Runs from any machine: fiofleet hops through your Factory WireGuard server (a bastion) and SSHes to the devices from there, so the operator doesn't need to be on the VPN.

Install

pip install fiofleet

Requires Python 3.9+. fioctl is optional — only needed if you pass --via-fioctl to the WireGuard commands.

Setup

fiofleet config set
# prompts for API token, factory name (and optionally an API base URL)

Or via env vars (these override the saved config):

export FOUNDRIES_API_TOKEN=...
export FOUNDRIES_FACTORY=my-factory

Get your API token at https://app.foundries.io/settings/tokens/.

To use ssh/exec from a machine that isn't on the device VPN, point fiofleet at your Factory WireGuard server (the bastion it hops through):

fiofleet config set-server --server vpn.example.com --server-user ops
# add --device-password ... if your devices use password (sshpass) auth

Connecting to the server with an OpenSSH key (e.g. an Azure VM running the Factory WireGuard server — the same .pem/OpenSSH key you'd use for ssh -i):

fiofleet config set-server \
  --server my-fio-vpn.eastus.cloudapp.azure.com \
  --server-user azureuser \
  --server-key ~/.ssh/azure-fio-vpn.pem \
  --device-user fio
# device auth happens on the server: add --device-password ... if devices need
# sshpass, otherwise omit (the server's own key reaches the devices).

--server-key is passed through to paramiko — anything ssh -i would accept works (.pem, ~/.ssh/id_ed25519, …). Omit it to fall back to your SSH agent / default keys, then password.

Commands

# Factories your token can see
fiofleet factories

# Devices
fiofleet devices list
fiofleet devices list --tag prod-eu --online-only
fiofleet devices show my-device-01

# OTA update reports
fiofleet ota report --tag prod-eu                   # last update per device + fleet summary
fiofleet ota report --tag prod-eu --failed-only     # just the devices that failed
fiofleet ota report --tag prod-eu --json            # structured, for dashboards/CI
fiofleet ota report --tag prod-eu --target lmp-124  # every device that attempted target lmp-124
fiofleet ota report --tag prod-eu --target lmp-124 --failed-only   # …and which of them failed
fiofleet ota stages my-device-01                    # full stage timeline for one device

# WireGuard
fiofleet wg enable my-device-01
fiofleet wg enable --tag prod-eu --parallel 20      # enable + wait until applied
fiofleet wg status --tag prod-eu
fiofleet wg disable --tag prod-eu
fiofleet wg enable my-device-01 --via-fioctl        # delegate to fioctl instead

# SSH / exec (hops through the configured WireGuard-server bastion by default)
fiofleet ssh my-device-01
fiofleet exec "uptime" --tag prod-eu
fiofleet exec "systemctl is-active aktualizr-lite" --tag prod-eu
fiofleet exec "fiotest" --tag prod-eu --json        # collect results as JSON
fiofleet exec "reboot" --tag prod-eu --strict       # non-zero exit if any device fails
fiofleet exec "uptime" --tag prod-eu --server vpn.example.com   # ad-hoc bastion
fiofleet exec "uptime" --name dev-01 --direct       # already on the VPN; skip the hop

A typical ota report looks like:

DEVICE                RESULT       FAILED@    TARGET                        WHEN
dev-us-01             FAILED       install    raspberrypi4-64-lmp-124       2026-05-21T08:14:02Z
    -> install: Installation failed: ostree pull error: Server returned HTTP 500
dev-eu-02             IN_PROGRESS  -          raspberrypi4-64-lmp-124       2026-05-21T08:13:55Z
dev-eu-01             SUCCESS      -          raspberrypi4-64-lmp-124       2026-05-20T22:01:10Z

Fleet summary (3 device(s)):
  FAILED       1   (install: 1)
  IN_PROGRESS  1
  SUCCESS      1

How OTA reporting works

Each device posts a stream of libaktualizr report events to the device-gateway as it updates (EcuDownloadStarted/Completed, EcuInstallationStarted/Applied/Completed). fiofleet reads that stream from the OTA API's per-device updates view — the same history fioctl shows — and collapses it into two operator-facing stages, download and install. A stage that reports success=false marks the update FAILED at that stage and surfaces the details the device attached; an update that reached EcuInstallationApplied but not …Completed is IN_PROGRESS (applied, awaiting the post-reboot confirmation). No agent on the device is required — it's all read from the API.

Pass --target X to scope the report to one rollout: each device's update history is searched (newest first) for an update whose target/version contains X, and only devices that actually attempted it appear in the output — their most recent attempt, with the same SUCCESS/FAILED/IN_PROGRESS verdict and failing-stage detail.

How WireGuard works here

Enabling WireGuard on a device writes a wireguard-client config entry (the same one fioctl devices config wireguard enable writes). The platform assigns the device a 10.42.42.x address; the device applies the change on its next check-in.

fiofleet wg status / --wait poll the Foundries wireguard-ips view — the same one the Factory WireGuard server reads to learn its peers — so "applied" means the platform actually considers the device a live VPN peer, not just that a config was queued.

How ssh/exec reach a device (the jump-host model)

A route to a device only exists on the Factory WireGuard server — it's peered into the VPN and keeps /etc/hosts in sync with device VPN IPs. Rather than require you to be on that box, fiofleet treats it as a bastion: it opens an SSH connection to the server (via paramiko) and runs the device ssh there. So:

your laptop ──SSH──► WireGuard server ──SSH──► device (10.42.42.x)
 (anywhere)          (on the VPN)              (fio@…)

Device authentication therefore happens on the server — using the server's key, or a password via sshpass (--device-password) — exactly as an admin SSHing into the box by hand would. Configure the bastion once with fiofleet config set-server (or pass --server ad hoc); pass --direct to skip it when you're already on the VPN. fiofleet runs ssh; it doesn't manage the tunnel itself.

Development

pip install -e ".[dev]"
pytest

A local end-to-end harness (real Pi WireGuard server + containerised devices) lives in harness/.

License

Apache 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fiofleet-0.5.0.tar.gz (29.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fiofleet-0.5.0-py3-none-any.whl (23.2 kB view details)

Uploaded Python 3

File details

Details for the file fiofleet-0.5.0.tar.gz.

File metadata

  • Download URL: fiofleet-0.5.0.tar.gz
  • Upload date:
  • Size: 29.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for fiofleet-0.5.0.tar.gz
Algorithm Hash digest
SHA256 79dcce36560b1d518095b16cdd4ab6ab2a5c190801a6a7db4abf47cdfd1201bc
MD5 1a196cc68665e62c47def33cba43e941
BLAKE2b-256 2754fe12aeb602711761a6595179fab4c10f58ef3934b256c9e0376a81d35295

See more details on using hashes here.

File details

Details for the file fiofleet-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: fiofleet-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 23.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for fiofleet-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a56595c241557b3accadaedc01665eaafd199d67d53bd2512a1e99d4cdc44b05
MD5 0f711a0f31bd274ae75683bbb2b2396d
BLAKE2b-256 df0e04cd11bb807ee5e0c4afe5a56a3ed5b4f9095fcc76fa86b3d10fe4959b18

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page