# cnmaestro CLI

Cleanup offline devices and bulk-upgrade firmware on a cnMaestro Cloud
instance (cloud.cambiumnetworks.com).

## Setup

### 1. Find your tenant's API base URL

Each cnMaestro Cloud tenant lives on a regional subdomain. Log in to
your tenant in a browser and copy the scheme + host portion of the
URL — everything before the first `/#/`. Example:

```
https://us-e1-s1-cmjbcvncy6.cloud.cambiumnetworks.com/#/VERONA_NETWORKS/home-view/home
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
this is your CNMAESTRO_BASE_URL
```

### 2. Create an API client

1. While logged in, go to the menu (top-right gear/avatar) →
   **Application** (or **Services**) → **API Clients**.
2. Click **Add New** (or **Add API Client**).
3. Give it a name (e.g. `cnmaestro-cli`). Grant a role that has at
   minimum:
   - Read access to devices and firmware.
   - Delete access to devices.
   - Permission to create firmware-update jobs.
   The built-in **Administrator** role covers all three.
4. Save and copy the **Client ID** and **Client Secret**. The secret
   is shown only once.

### 3. Install

```bash
cd /Users/graham/dev/network/cnmaestro
uv sync
cp .env.example .env
# edit .env with your client id + secret
```

### 4. Smoke-test

```bash
uv run cnmaestro cleanup --dry-run
```

This authenticates, lists devices, and prints what *would* be removed
without actually removing anything.

## Usage

### Remove devices offline > 24h

```bash
uv run cnmaestro cleanup                       # interactive, asks before deleting
uv run cnmaestro cleanup --threshold-hours 48  # custom threshold
uv run cnmaestro cleanup --yes                 # non-interactive (cron)
uv run cnmaestro cleanup --dry-run             # just print, never delete
```

### Bulk-upgrade online devices to latest firmware per model

```bash
uv run cnmaestro upgrade                       # interactive
uv run cnmaestro upgrade --product ePMP,PMP    # only these products
uv run cnmaestro upgrade --yes                 # non-interactive
uv run cnmaestro upgrade --dry-run             # show planned jobs only
```

The upgrade command groups devices by `(product, target_version)` and
submits one bulk firmware job per group (cnMaestro caps each job at
100 devices, so larger groups are chunked).

## Safety

- Both commands default to interactive confirmation.
- `cleanup` refuses to run if more than 50 % of the fleet would be
  removed; pass `--force` to override.
- Every destructive call (delete, job-submit) is appended to
  `./cnmaestro.log` for audit.
- The OAuth2 token is held in memory only.

## Tests

```bash
uv run pytest
```

All HTTP is mocked with `respx`; tests never touch the live API.
