818 lines
41 KiB
Markdown
818 lines
41 KiB
Markdown
---
|
|
name: omada-controller-administration
|
|
description: >-
|
|
Class-level skill for TP-Link Omada Cloud Controller administration via browser automation.
|
|
Covers login flow, SPA navigation quirks, iframe-based site management, VLAN configuration,
|
|
ACL rules, inter-VLAN mDNS limitations, WLAN/channel optimization, device management,
|
|
and firmware updates. Applies to cloud-based Omada controllers at omada.tplinkcloud.com.
|
|
device management, and firmware updates. Applies to cloud-based Omada controllers at omada.tplinkcloud.com.
|
|
tags:
|
|
- tp-link
|
|
- omada
|
|
- wlan
|
|
- channel-optimization
|
|
- network-management
|
|
- browser-automation
|
|
---
|
|
|
|
# TP-Link Omada Cloud Controller Administration
|
|
|
|
## When to Use
|
|
Any task involving TP-Link Omada Cloud Controller — WLAN configuration, channel optimization, device management, firmware updates, site settings, or viewing logs/alerts.
|
|
|
|
## Environment
|
|
- **URL**: `https://omada.tplinkcloud.com/`
|
|
- **Credentials**: 1Password vault "Hermes", item "omada - Netzwerkverwaltung"
|
|
- **Controller name**: "Schön" (Cloud Based System)
|
|
- **Site name**: "Rohr"
|
|
- **Devices** (all on VLAN 1 / 172.16.1.x management subnet):
|
|
- Router: DR3650v-4G HW v1.0, FW 1.1.1, IP 172.16.1.1 (also 10.0.30.1 on VLAN 30)
|
|
- Switch: SG3428MP HW v6.20, FW 6.20.26 (update 6.20.27 avail), IP 172.16.1.2
|
|
- AP Erdgeschoss: EAP683 UR v1.0, FW 1.4.0, IP 172.16.1.3 (switch port 16)
|
|
- AP Obergeschoss: EAP683 UR v1.0, FW 1.4.0, IP 172.16.1.4 (switch port 14)
|
|
- AP Garage: EAP615-Wall v1.0, FW 1.5.4, IP 172.16.1.5 (switch port 21)
|
|
- AP Garten: EAP225-Outdoor v3.0, FW 5.2.2, IP 172.16.1.6
|
|
- **Local router fallback**: `http://172.16.1.1/webpages/login.html` (login blocked when managed by controller — see "Known Issue" section)
|
|
- **Local switch UI**: `http://172.16.1.2` (accessible but requires unknown device-specific credentials)
|
|
- **Monitoring stack**: CT 141 (proxmox7), Docker-based: prometheus (:9090), grafana (:3000), alertmanager, blackbox, pve-exporter, telegram-bridge (:9099). snmp_exporter runs as systemd binary (:9116) — Docker deployment fails on CT 141.
|
|
- **Grafana dashboard**: `network-infra` (uid: `network-infra`) — Proxmox nodes, guests, HA state, blackbox probes. Omada metrics NOT included (no exporter available for cloud controller).
|
|
- **Local Omada Controller** (in progress): CT 142 (`omada-controller`, 10.0.30.142/24, VLAN 30, proxmox7, vm_disks storage). Will replace cloud controller to enable SNMP + API exporters.
|
|
- **Docker hosts in cluster**: Only CT 141 (6 containers: monitoring stack) and CT 121 (1 container: portainer). All other CTs/VMs run services natively without Docker.
|
|
|
|
## Login Flow
|
|
|
|
### Steps
|
|
1. Navigate to `https://omada.tplinkcloud.com/#/login`
|
|
2. Wait 5-8 seconds — the SPA loads slowly, initial snapshot shows `(empty page)` with 0 elements
|
|
3. Take snapshot to confirm login form is visible
|
|
4. Type TP-Link ID into the "TP-Link ID (Partner ID)" textbox
|
|
5. Type password into the "Password" textbox (nested inside a generic wrapper — use the inner textbox ref, not the outer one)
|
|
6. Press **Enter** (do NOT click the "Sign In" link — it redirects to tp-link.com marketing site instead of submitting the form)
|
|
7. Wait 5-8 seconds for redirect
|
|
8. Dismiss cookie banner if present ("Alle Cookies akzeptieren")
|
|
9. Dismiss any notice dialogs ("OK" or "Do not remind again")
|
|
|
|
### Alternative: Console-Based Login
|
|
|
|
Instead of `browser_type` + `browser_press`, you can fill the login form via `browser_console`:
|
|
|
|
```javascript
|
|
(async function(){
|
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
const inputs = document.querySelectorAll('input');
|
|
if (inputs.length >= 2) {
|
|
inputs[0].focus();
|
|
inputs[0].value = 'dominik@familie-schoen.com';
|
|
inputs[0].dispatchEvent(new Event('input', {bubbles: true}));
|
|
const pwInputs = document.querySelectorAll('input[type="password"]');
|
|
if (pwInputs.length > 0) {
|
|
pwInputs[pwInputs.length - 1].focus();
|
|
pwInputs[pwInputs.length - 1].value = '<password>';
|
|
pwInputs[pwInputs.length - 1].dispatchEvent(new Event('input', {bubbles: true}));
|
|
}
|
|
}
|
|
await sleep(500);
|
|
// Submit via Enter keydown event
|
|
document.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true}));
|
|
// Also click Sign In link as backup
|
|
for (const el of document.querySelectorAll('a, button')) {
|
|
if (el.textContent.trim() === 'Sign In' && el.tagName === 'A') { el.click(); break; }
|
|
}
|
|
return 'login submitted';
|
|
})()
|
|
```
|
|
|
|
> Note: This approach dispatches React-compatible `input` events. The `browser_type` + `Enter` approach is simpler and equally reliable for the login form.
|
|
|
|
### Critical Pitfalls
|
|
- **Empty page on navigate**: The Omada SPA frequently returns `(empty page)` with `element_count: 0` right after navigation. Always wait 5-8s then re-snapshot before concluding the page failed to load.
|
|
- **"Sign In" link vs form submit**: The "Sign In" element (`@e9` in observed session) is a **link**, not a submit button. Clicking it navigates to `https://www.tp-link.com/de/support/`. Use `browser_press` with `Enter` while focused in the password field to submit the form.
|
|
- **Password field nesting**: The password input is wrapped in a generic div containing two textboxes — use the one with aria-label "Password" (the inner ref), not the outer wrapper ref.
|
|
- **Multiple dismissable dialogs**: After login, expect up to 3 sequential dialogs: cookie consent, regional isolation notice, and firmware/system update announcement. Dismiss each before proceeding.
|
|
- **Session expiry / white screen**: After extended interaction (especially after clicking buttons inside the iframe), the SPA may go completely blank/white. The iframe disappears from the DOM entirely (`document.querySelector('iframe')` returns null). Recovery: re-navigate to `https://omada.tplinkcloud.com/#/login` and repeat the full login flow. The session does NOT persist across browser navigations.
|
|
- **`browser_console` variable re-declaration**: Repeated `browser_console` calls that declare variables (e.g., `const fr = document.querySelector(...)`) fail with `SyntaxError: Identifier 'xxx' has already been declared` because the execution context persists between calls. Always wrap code in an IIFE: `(function(){ ... })()` to avoid polluting the persistent scope.
|
|
- **`browser_click` refs for iframe content**: Refs from `browser_snapshot` for elements inside the iframe (e.g., `@e14` for "Optimize Now") sometimes fail with "Unknown ref". Fall back to `browser_console` with IIFE-scoped DOM queries to click iframe-internal elements directly.
|
|
|
|
## Navigation: Controller → Site → WLAN
|
|
|
|
### Structure
|
|
```
|
|
Omada Cloud Landing
|
|
└─ Dropdown: "On Premise Systems" / "Cloud Based Systems" → select "Cloud Based Systems"
|
|
└─ Controller card ("Schön") → click to enter
|
|
└─ Global View (org overview, site list)
|
|
└─ Site "Rohr" → click site name cell
|
|
└─ Site Dashboard with left nav:
|
|
├─ Management: Dashboard, Devices, Clients
|
|
├─ Monitoring: Map, Logs
|
|
├─ Configuration: Network Config, Device Config
|
|
├─ Hotspot
|
|
└─ Maintenance: Network Tools, IntelliRecover
|
|
```
|
|
|
|
### Switching to Cloud Based Systems
|
|
The top-left dropdown defaults to "On Premise Systems". Click the combobox, then use `browser_console` to find and click the "Cloud Based Systems" `<li>` inside `.ant-dropdown-menu`.
|
|
|
|
### Switching Back to a Site from Global View
|
|
After performing actions in Global View (e.g., checking org-wide logs), returning to a specific site requires navigating through the Dashboard site list:
|
|
|
|
1. Click **Dashboard** in the Global View sidebar
|
|
2. Find the site name in the Site List table
|
|
3. Click the `span.text-link.cursor-pointer` inside the site name cell (NOT the `<td>` itself — it has no click handler)
|
|
4. Verify by checking the sidebar: site-level menus (Management, Monitoring, Configuration, Hotspot, Maintenance) should appear instead of Global View menus (Dashboard, Devices, Logs, SD-WAN, Accounts, Settings)
|
|
|
|
**Pitfall: Combobox Does Not List Sites**
|
|
The top-left combobox (showing controller name "Schön") opens a dropdown with controller/system options (e.g., "SchönEssentials") but does **NOT** list individual sites. It cannot be used to switch between sites. Always use the Dashboard → Site List table to navigate to a specific site.
|
|
|
|
### Entering a Site
|
|
Click the site name in the Site List table. **Pitfall**: clicking the `<td>` cell itself does NOT navigate into the site — the cell has `cursor: auto` and no click handler. The actual clickable element is a `span.text-link.cursor-pointer` *inside* the cell. Double-clicking the cell also does not work. Use `browser_console`:
|
|
|
|
```javascript
|
|
(function(){
|
|
const fr = document.querySelector('iframe');
|
|
if (!fr || !fr.contentDocument) return 'no iframe';
|
|
const spans = fr.contentDocument.querySelectorAll('span.text-link, .cursor-pointer');
|
|
for (const s of spans) {
|
|
if (s.textContent.trim() === 'Rohr') { s.click(); return 'clicked Rohr text-link'; }
|
|
}
|
|
return 'not found';
|
|
})()
|
|
```
|
|
|
|
**Verification**: After clicking, the sidebar should show site-level menus (Management, Monitoring, Configuration, Hotspot, Maintenance) instead of Global View menus (Dashboard, Devices, Logs, SD-WAN, Accounts, Settings). If you still see "SD-WAN" or "Accounts" in the sidebar, you're still in Global View — the click didn't work.
|
|
|
|
### Iframe Navigation
|
|
|
|
> **Reference**: See `references/navigation-css-structure.md` for detailed CSS class hierarchy of the Omada site iframe navigation.
|
|
|
|
Once inside a site, all content is rendered inside an `<iframe>`. Standard `browser_click` refs from the outer snapshot may not work for iframe-internal elements. Use `browser_console` with `document.querySelector('iframe').contentDocument` to find and click menu items:
|
|
|
|
```javascript
|
|
(function(){
|
|
const fr = document.querySelector('iframe');
|
|
if (!fr || !fr.contentDocument) return 'no iframe';
|
|
const items = fr.contentDocument.querySelectorAll('li');
|
|
for (const item of items) {
|
|
if (item.textContent.trim() === 'WLAN') {
|
|
item.click();
|
|
return 'clicked WLAN';
|
|
}
|
|
}
|
|
return 'not found';
|
|
})()
|
|
```
|
|
|
|
### Full Menu Structure (inside site iframe)
|
|
- **Configuration** → expands to:
|
|
- Network Config → General Settings (Site Settings, SSH, VoIP), Network Settings (**Internet, LAN, WLAN**), Authentication (Portal), VPN, Security (ACL), Transmission (Routing, NAT, DHCP Reservation, Bandwidth Control, Gateway QoS), Profile (Groups, Time Range, Rate Limit, APN Profile)
|
|
- Device Config → Gateway (DNS), Switch (Switch Ports)
|
|
|
|
## VLAN Configuration
|
|
|
|
### VLAN Inventory (site "Rohr")
|
|
|
|
Navigate to Configuration → Network Config → Network Settings → LAN tab.
|
|
|
|
| VLAN | Name | Subnet | Purpose |
|
|
|------|------|--------|---------|
|
|
| 1 | CatchAll(Default) | 172.16.1.1/24 | Default/untagged |
|
|
| 10 | Infrastructure | 10.0.10.1/24 | Network infrastructure |
|
|
| 20 | Server / Hypervisors | — | PVE hosts (10.0.20.x) |
|
|
| 30 | Server / VMs & LXCs | — | Containers & VMs (10.0.30.x) |
|
|
| 40 | Clients | — | Client devices (Mac, PC, mobile) |
|
|
| 50 | IoT | 10.0.50.1/24 | IoT devices |
|
|
| 60 | DMZ | 10.0.60.1/24 | Demilitarized zone |
|
|
| 192 | 192.168.100.x | 192.168.100.x | Special purpose |
|
|
|
|
**Pitfall**: The VLAN list in the LAN tab only shows 4 VLANs by default
|
|
(1, 10, 50, 60). VLANs 20, 30, 40, 192 are visible in the **Isolation Settings**
|
|
view but not in the main VLAN table. They may be configured elsewhere or
|
|
inherited from the switch/gateway.
|
|
|
|
### VLAN Isolation Settings
|
|
|
|
Navigate to Configuration → Network Config → Network Settings → LAN → **Isolation Settings** button.
|
|
|
|
Two zones:
|
|
- **Isolated Network** — can only access internet, no inter-VLAN communication
|
|
- **Interconnected Network** — can communicate with each other
|
|
|
|
As of 2026-07-05: **0 isolated**, all 8 VLANs are interconnected (all can
|
|
reach each other). Inter-VLAN routing is handled by the DR3650 gateway.
|
|
|
|
### ACL Rules (Gateway)
|
|
|
|
Navigate to Configuration → Network Config → Security → ACL.
|
|
|
|
Key rules (LAN→LAN direction, relevant for inter-VLAN access):
|
|
|
|
| # | Description | Direction | Proto | Source → Destination |
|
|
|---|-------------|-----------|-------|---------------------|
|
|
| 1 | Permit Internet to everyone | LAN→WAN | All | All VLANs → Any |
|
|
| 5 | Allow Hypervisors from Clients | LAN→LAN | All | Clients → Hypervisors |
|
|
| 6 | Allow Clients to VMs | LAN→LAN | **TCP** | Clients → VMs & LXCs |
|
|
| 8 | Allow Access to IoT | LAN→LAN | All | Clients/Hyper/VMs/192 → IoT |
|
|
| 9 | DENY Botnet Stuff for IoT | LAN→LAN | Deny All | IoT/CatchAll → Clients/Hyper/VMs/Infra |
|
|
| 10 | Deny Ingress to VLANs | WAN IN | Deny All | Any → Client/Hyper/Infra/IoT/VM IPs |
|
|
|
|
**Important**: Rule 6 allows **TCP only** from Clients (VLAN 40) to VMs (VLAN 30).
|
|
UDP and ICMP are NOT explicitly permitted — they may be allowed by default
|
|
(no deny rule for LAN→LAN between these VLANs), but if you need UDP services
|
|
(e.g. mDNS, SNMP) from VLAN 40 to VLAN 30, verify with a packet capture.
|
|
|
|
### mDNS / Bonjour Across VLANs
|
|
|
|
**Omada does NOT provide mDNS/multicast relay or IGMP proxy functionality.**
|
|
|
|
mDNS broadcasts (224.0.0.251:5353) are confined to their originating VLAN.
|
|
Bonjour service discovery (AirPlay, Time Machine, printer discovery) does NOT
|
|
cross VLAN boundaries.
|
|
|
|
**Workarounds**:
|
|
1. **Manual connection** — clients specify the IP/path directly
|
|
(e.g. `smb://10.0.30.21/share` for Time Machine)
|
|
2. **External mDNS repeater** — run `mdns-repeater` or `avahi-reflector` on
|
|
a host with interfaces in both VLANs (e.g. a CT with trunk or dual NIC)
|
|
|
|
**Deployed Solution**: CT 127 (avahi-reflector) on proxmox6 runs
|
|
`avahi-daemon` in reflector mode with interfaces in VLAN 30 (10.0.30.5,
|
|
eth0), VLAN 40 (10.0.40.5, eth1), and VLAN 50 (10.0.50.5, eth2).
|
|
Configured with a **directional matrix** via iptables:
|
|
- 30→40 ✅ (server services visible to clients)
|
|
- 50→30 ✅ (IoT services visible to servers)
|
|
- 50→40 ✅ (IoT services visible to clients)
|
|
- 40→30, 40→50, 30→50 ❌ (blocked by iptables INPUT/OUTPUT rules)
|
|
|
|
See the `proxmox-ve-administration` skill, `references/avahi-reflector-setup.md`
|
|
for full setup details including the multi-VLAN iptables rule pattern.
|
|
|
|
The DR3650 gateway does not expose multicast forwarding or IGMP proxy settings
|
|
in the Omada controller UI. This is a firmware limitation.
|
|
|
|
### Navigating to VLAN/ACL Views
|
|
|
|
**VLAN list**: Configuration → Network Config → Network Settings → LAN tab.
|
|
Shows only 4 VLANs by default (1, 10, 50, 60). VLANs 20, 30, 40, 192 are
|
|
hidden — visible only in Isolation Settings.
|
|
|
|
**Isolation Settings**: Click the "Isolation Settings" button on the LAN tab
|
|
(next to "Add"). Shows all 8 VLANs grouped into Isolated vs Interconnected.
|
|
As of 2026-07-05: 0 isolated, all interconnected.
|
|
|
|
**ACL Rules**: Configuration → Network Config → Security → ACL.
|
|
Shows Gateway ACL rules table with INDEX, ENABLED, DESCRIPTION, DIRECTION,
|
|
POLICY, PROTOCOLS, SOURCE, DESTINATION, ACTION columns.
|
|
|
|
## WLAN Management
|
|
|
|
### SSID Overview
|
|
Navigate to Configuration → Network Settings → WLAN. Shows tabs: **SSID**, **WLAN Optimization**, **Optimization History**.
|
|
|
|
Observed SSIDs (site "Rohr"):
|
|
| SSID | Security | Bands | VLAN |
|
|
|------|----------|-------|------|
|
|
| Schön | WPA-Personal | 2.4/5/6 GHz | 40 |
|
|
| Schön - Gäste | WPA-Personal | 2.4/5/6 GHz | — |
|
|
| IoT | WPA-Personal | 2.4/5 GHz | 50 |
|
|
| 2Schoen | WPA-Personal | 2.4/5 GHz | 40 |
|
|
|
|
### WLAN Optimization (Channel Optimization)
|
|
1. Navigate to WLAN → "WLAN Optimization" tab
|
|
2. Click "Optimize Now" link
|
|
3. **Optimization Mode dialog** appears with two radio options:
|
|
- **Partial** (default) — scans and optimizes only channels needing adjustment
|
|
- **Global** — full scan and reassignment of all wireless configuration
|
|
- Select mode, then click "Confirm" to start
|
|
4. **Warning**: Internet connectivity drops for several minutes during optimization. Schedule during low-traffic periods.
|
|
5. Progress can be tracked under "Optimization History" tab
|
|
6. "Excluded Devices List" allows excluding specific APs from optimization
|
|
|
|
### Per-SSID Channel Configuration
|
|
To manually set channels (instead of auto-optimization):
|
|
1. Go to WLAN → SSID tab
|
|
2. Click the SSID name or the action icon in the row
|
|
3. Edit the wireless network settings — channel selection is per-band (2.4 GHz, 5 GHz, 6 GHz)
|
|
4. Available 5 GHz channels observed: 36, 40, 44, 48, 149, 153, 157, 161
|
|
|
|
## Async Console Automation Pattern
|
|
|
|
For multi-step flows (login → dismiss dialogs → navigate to page → action), chaining individual `browser_snapshot` + `browser_click` calls is fragile and slow due to iframe ref instability and session expiry. Instead, use a **single `browser_console` call with an async IIFE** that sleeps between steps:
|
|
|
|
```javascript
|
|
(async function(){
|
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
// Step 1: close notice
|
|
for (const el of document.querySelectorAll('a, button')) {
|
|
if (el.textContent.trim() === 'OK') { el.click(); break; }
|
|
}
|
|
await sleep(1000);
|
|
// Step 2: click site cell
|
|
let fr = document.querySelector('iframe');
|
|
if (!fr || !fr.contentDocument) return 'no iframe';
|
|
for (const c of fr.contentDocument.querySelectorAll('td, [role="cell"]')) {
|
|
if (c.textContent.trim() === 'Rohr') { c.click(); break; }
|
|
}
|
|
await sleep(5000);
|
|
// Step 3: expand Configuration + click WLAN
|
|
fr = document.querySelector('iframe');
|
|
for (const li of fr.contentDocument.querySelectorAll('li')) {
|
|
if (li.textContent.trim() === 'Configuration' && li.classList.contains('title')) { li.click(); break; }
|
|
}
|
|
await sleep(1000);
|
|
fr = document.querySelector('iframe');
|
|
for (const li of fr.contentDocument.querySelectorAll('li')) {
|
|
if (li.textContent.trim() === 'WLAN' && li.classList.contains('navigator-li-effective')) { li.click(); break; }
|
|
}
|
|
await sleep(3000);
|
|
// ... continue with tab click, Optimize Now, mode selection, Confirm
|
|
return 'done';
|
|
})()
|
|
```
|
|
|
|
### Why This Works Better
|
|
- **Atomic**: All steps run in one execution context — no risk of session expiring between tool calls
|
|
- **No ref fragility**: Direct DOM queries inside iframe don't depend on snapshot refs staying valid
|
|
- **Sleep-based timing**: `await sleep(ms)` gives SPA time to render between steps
|
|
- **Early return on failure**: Each step checks for iframe existence and returns descriptive error if missing
|
|
|
|
### Triggering WLAN Optimization (Full Async Flow)
|
|
|
|
To run the entire optimization (site click → WLAN → Optimization tab → Optimize Now → Global → Confirm) in one shot:
|
|
|
|
```javascript
|
|
(async function(){
|
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
// ... (site click + Configuration expansion + WLAN click as above)
|
|
// Click "WLAN Optimization" tab
|
|
fr = document.querySelector('iframe');
|
|
for (const el of fr.contentDocument.querySelectorAll('span, div, a, li')) {
|
|
if (el.textContent.trim() === 'WLAN Optimization' && el.children.length <= 1) { el.click(); break; }
|
|
}
|
|
await sleep(3000);
|
|
// Click "Optimize Now"
|
|
fr = document.querySelector('iframe');
|
|
for (const el of fr.contentDocument.querySelectorAll('a, button, span')) {
|
|
if (el.textContent.trim() === 'Optimize Now' && el.children.length <= 2) { el.click(); break; }
|
|
}
|
|
await sleep(2000);
|
|
// Select "Global" radio
|
|
fr = document.querySelector('iframe');
|
|
for (const el of fr.contentDocument.querySelectorAll('label, span, div, input')) {
|
|
if (el.textContent.trim().startsWith('Global')) { el.click(); break; }
|
|
}
|
|
await sleep(1000);
|
|
// Click "Confirm"
|
|
fr = document.querySelector('iframe');
|
|
for (const el of fr.contentDocument.querySelectorAll('a, button')) {
|
|
if (el.textContent.trim() === 'Confirm') { el.click(); return '✅ Global optimization started!'; }
|
|
}
|
|
return 'Confirm not found';
|
|
})()
|
|
```
|
|
|
|
### Reading Optimization Results
|
|
|
|
After the optimization starts, the SPA typically goes blank (session expires). To check results:
|
|
1. Re-login (navigate + login flow)
|
|
2. Navigate to site Rohr → Configuration → WLAN → "Optimization History" tab
|
|
3. Read the iframe body text:
|
|
|
|
```javascript
|
|
(async function(){
|
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
// ... (navigate to Optimization History tab as above)
|
|
fr = document.querySelector('iframe');
|
|
return fr.contentDocument.body.innerText.substring(0, 3000);
|
|
})()
|
|
```
|
|
|
|
The history table shows: TIME, TYPE (Manual/Adaptive), DEVICES, DURATION, MODE, RESULT (score before→after), and ACTION.
|
|
|
|
Observed result (Global mode, 2026-06-29):
|
|
```
|
|
Jun 29, 2026 08:36:34 am 3 4m 21s Manual
|
|
86 → 88 (+2)
|
|
```
|
|
|
|
Score scale: 0-100. Improvements of +2-4 points per optimization run are typical. 3 of 4 APs were optimized (one may have already been on an optimal channel).
|
|
|
|
**Post-optimization**: The optimization generated 2 "monitor link down" alerts (see "Pitfall: Optimization Causes Monitor Link Down Alerts" below). These were batch-resolved, bringing the total to 20 resolved / 0 unresolved.
|
|
|
|
### Full Optimization Flow (Confirmed Working)
|
|
|
|
The entire flow — from Global View to optimization confirmation — can be executed as a single async IIFE after login. This was confirmed end-to-end on 2026-06-29:
|
|
|
|
1. Login (Enter key in password field)
|
|
2. Dismiss notice dialog (OK button)
|
|
3. Click site "Rohr" cell in iframe
|
|
4. Expand Configuration → click WLAN
|
|
5. Click "WLAN Optimization" tab
|
|
6. Click "Optimize Now" link
|
|
7. Select "Global" radio in the optimization mode dialog
|
|
8. Click "Confirm"
|
|
|
|
Steps 3-8 can all run in one `browser_console` async IIFE (see "Triggering WLAN Optimization" code block above). After Confirm, the SPA goes blank — re-login to check results via Optimization History.
|
|
|
|
### Session Expiry After Confirm
|
|
|
|
Clicking "Confirm" in the optimization dialog causes the SPA to go blank — the iframe disappears entirely. This is expected: the optimization disrupts connectivity (including the controller's own management plane). Recovery requires a full re-login cycle.
|
|
|
|
## Device Detail Pages
|
|
|
|
### Opening Device Details — Two Levels
|
|
|
|
There are TWO ways to open device details, producing different views:
|
|
|
|
#### 1. Simple View: Click device name (`span.device-name`)
|
|
Click the `span.device-name` element in the Devices table. Opens a
|
|
compact detail panel with limited tabs.
|
|
|
|
```javascript
|
|
// Correct selector for device names in the table
|
|
const spans = doc.querySelectorAll('span.device-name');
|
|
for (const s of spans) {
|
|
if (s.textContent.trim() === 'Router') { s.click(); break; }
|
|
}
|
|
```
|
|
|
|
> **Pitfall**: `span.text-link` does NOT match device names in the table.
|
|
> The correct CSS class is `span.device-name` (class:
|
|
> `device-name u-text-word-break-all ellipsis`). The `span.text-link`
|
|
> selector works for site names in the Site List, not device names.
|
|
|
|
#### 2. Rich View: Click "Manage Device" button
|
|
After opening the simple view, click the **"Manage Device"** button
|
|
(`a.manage-device-button`). This opens a wider drawer with full tabs:
|
|
|
|
**Router (DR3650v-4G) tabs in Manage Device view:**
|
|
- **Overview** — Upload/Download rate, Temperature, CPU%, Memory%, Channel
|
|
Utilization, Link graph, Current Clients table (shows connected clients
|
|
with MAC, IP, IPv6)
|
|
- **Network View** — Network topology visualization
|
|
- **Ports** — Port-level statistics (packets, dropped, errors, bytes per port)
|
|
- **Logs** — Device-specific log entries
|
|
- **Tools** — Network Check, Terminal
|
|
- **Config** — Device configuration with left-side menu:
|
|
- **General**: Name, Description, LED, Device Labels, Remember Device
|
|
- **Wireless**: Radio settings
|
|
- **SIM Config**: PIN Management, Statistics, SMS Message, SMS Settings
|
|
- **Transmission**: Routing, NAT, Bandwidth Control, Gateway QoS
|
|
- **Network Security**: ACL
|
|
- **Advanced**: General, DNS
|
|
- **No SNMP section exists** (verified 2026-07-05 — see SNMP Monitoring section)
|
|
|
|
**Switch (SG3428MP) tabs in Manage Device view:**
|
|
- Same 6 tabs (Overview, Network View, Ports, Logs, Tools, Config)
|
|
- **Config** menu differs: General, VLAN Interface, Services (Loopback
|
|
Control, Spanning Tree), Static Route
|
|
- **No SNMP section exists**
|
|
|
|
> **Pitfall**: The Config tab's left-side menu uses `.ant-menu-item` class.
|
|
> Some items show as `[skeleton-item fake-menu-item]` placeholders that
|
|
never load (20 skeleton items remained permanently on the router config).
|
|
Scrolling the menu container does not trigger lazy loading. This appears
|
|
to be a bug in the Omada SPA when certain firmware versions don't expose
|
|
all expected config sections.
|
|
|
|
### Known Issue: DR3650 ISP Statistics Empty
|
|
|
|
**Symptom**: ISP statistics page for the DR3650v-4G router is always empty in Omada.
|
|
|
|
**Root Cause**: The DR3650v-4G with **Hardware v1.0 / Firmware 1.1.1** does not push ISP-level telemetry (WAN traffic, cellular data volume, signal strength, connection statistics) to the Omada controller. The controller can only read basic device telemetry (temperature, CPU, memory, port status). This is a **hardware/firmware limitation**, not a configuration issue.
|
|
|
|
**Observations** (2026-06-29):
|
|
- Router detail page has only "Overview" and "Ports" tabs — no ISP/Cellular/WAN section
|
|
- Active WAN ports: DSL + Port 5 (green/active) — DSL is primary WAN, not 4G/SIM
|
|
- Router reports: 75°C, CPU 24%, Memory 57%, uptime 6+ days
|
|
- Port statistics ARE available (packets, bytes, errors per port) but aggregated WAN/ISP stats are NOT
|
|
|
|
**Workaround**: Enable local management on the router to access its built-in web UI directly:
|
|
1. In Omada: Configuration → Device Config → Gateway → Local Management → Enable
|
|
2. Access router UI at `http://10.0.30.1/webpages/login.html`
|
|
3. ISP/cellular statistics are available in the router's own web UI under WAN/Cellular sections
|
|
|
|
**Pitfall: Router Local Login Blocked When Managed by Controller**
|
|
When the DR3650 is adopted by an Omada controller, the local web UI at `http://10.0.30.1/webpages/login.html` displays: *"Note: This Gateway is being managed by Controller [IP]"*. Login attempts with admin credentials or the Omada cloud password will fail. Local management must be explicitly enabled through the Omada controller's Device Config → Gateway → Local Management settings before local login works.
|
|
|
|
## Logs & Alerts
|
|
|
|
### Viewing Alerts
|
|
Navigate to site → **Logs** (left nav, under Monitoring). The Logs badge shows the count of unresolved alerts. Three tabs: **Alerts**, **Events**, **Audit Logs**.
|
|
|
|
Filter options:
|
|
- **Unresolved / Resolved / All** — toggle between alert states
|
|
- **System / Device** — filter by alert source
|
|
- **Filter** link — advanced filtering
|
|
|
|
### Alert Types Observed
|
|
| Type | Level | Cause | Action |
|
|
|------|-------|-------|--------|
|
|
| Device informed monitor link down | Warning | AP lost TCP connection to Controller on port 29817 | Usually transient — see pitfall below |
|
|
|
|
### Resolving Alerts (Batch Resolve)
|
|
|
|
To mark multiple alerts as resolved at once:
|
|
|
|
1. Navigate to site → **Logs** → **Alerts** tab (must be in site view, NOT Global View — Global View only has "Events" and "Audit Logs" tabs, no "Alerts")
|
|
2. Click the **"Select all"** checkbox in the table header (first `ant-checkbox-input` in the table)
|
|
3. Click **"Batch Resolved"** link (class `s-button s-button-link`) in the toolbar
|
|
4. No confirmation dialog appears — alerts are resolved immediately
|
|
5. Verify: the "Unresolved" count drops to 0
|
|
|
|
```javascript
|
|
// Async flow: select all + batch resolve
|
|
(async function(){
|
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
const fr = document.querySelector('iframe');
|
|
if (!fr || !fr.contentDocument) return 'no iframe';
|
|
const doc = fr.contentDocument;
|
|
// Select all checkboxes
|
|
const cbs = doc.querySelectorAll('.ant-checkbox-input');
|
|
if (cbs.length > 0) cbs[0].click(); // first visible CB = select all
|
|
await sleep(1000);
|
|
// Click "Batch Resolved"
|
|
for (const l of doc.querySelectorAll('a, button, span')) {
|
|
if (l.textContent.trim() === 'Batch Resolved' && l.offsetParent !== null) {
|
|
l.click();
|
|
return 'batch resolved';
|
|
}
|
|
}
|
|
return 'Batch Resolved not found';
|
|
})()
|
|
```
|
|
|
|
### Checking Firmware Versions & Available Updates
|
|
|
|
Navigate to site → **Devices** (left nav). The device table shows all 6 devices with columns: NAME, IP, STATUS, MODEL, ACTION.
|
|
|
|
- **Firmware update indicator**: A green upward-arrow icon in the ACTION column signals an available firmware update for that device.
|
|
- **"Start Rolling Upgrade"** button (top right) initiates staged firmware updates across multiple devices.
|
|
- Devices without the arrow icon are on the latest firmware.
|
|
|
|
Observed firmware status (2026-06-29):
|
|
|
|
| Device | Model | HW | Update? |
|
|
|--------|-------|----|---------|
|
|
| Router | DR3650v-4G | v1.0 | ✅ Current |
|
|
| Core (Switch) | SG3428MP | v6.20 | ⬆️ Update available |
|
|
| AP Erdgeschoss | EAP683 UR(EU) | v1.0 | ✅ Current |
|
|
| AP Obergeschoss | EAP683 UR(EU) | v1.0 | ✅ Current |
|
|
| AP Garage | EAP615-Wall(EU) | v1.0 | ✅ Current |
|
|
| AP Garten | EAP225-Outdoor(EU) | v3.0 | ✅ Current |
|
|
|
|
### Pitfall: Optimization Causes "Monitor Link Down" Alerts
|
|
Running WLAN Optimization (especially Global mode) disrupts AP connectivity for several minutes. Each affected AP generates a "Device informed monitor link down" Warning alert (TCP connection to Controller on port 29817 failed). These are **expected and harmless** — the APs reconnect after optimization completes. Don't treat them as real network problems. Use "Batch Resolved" (see above) to clear them.
|
|
|
|
### Pitfall: Global View vs Site View — Different Tab Sets
|
|
The Global View Logs page only has "Events" and "Audit Logs" tabs. The "Alerts" tab exists ONLY in the site-level Logs page. If you see only "Events" and "Audit Logs", you're in Global View — navigate back to the site first. Similarly, the site-level sidebar has "Management", "Monitoring", "Configuration", "Hotspot", "Maintenance" sections, while Global View has "Dashboard", "Devices", "Logs", "SD-WAN", "Accounts", "Settings". Use the sidebar structure to verify which view you're in.
|
|
|
|
## SNMP Monitoring
|
|
|
|
### CRITICAL FINDING: SNMP Cannot Be Enabled via Omada Cloud Controller
|
|
|
|
**As of 2026-07-05, the Omada Cloud Controller does NOT expose SNMP
|
|
configuration for ANY device type.** This was verified exhaustively:
|
|
|
|
- **Router (DR3650v-4G, FW 1.1.1)**: Config tab has sections: General,
|
|
Wireless, SIM Config (PIN Management, Statistics, SMS Message, SMS
|
|
Settings), Transmission (Routing, NAT, Bandwidth Control, Gateway QoS),
|
|
Network Security (ACL), Advanced (General, DNS). **None contain SNMP.**
|
|
Some sections show `[skeleton-item fake-menu-item]` placeholders that
|
|
never load (20 skeleton items remain permanently).
|
|
- **Switch (SG3428MP, FW 6.20.26)**: Config tab has sections: General,
|
|
VLAN Interface, Services (Loopback Control, Spanning Tree), Static Route.
|
|
**No SNMP.**
|
|
- **APs**: Config tab not tested for SNMP, but given the pattern above,
|
|
unlikely to have SNMP settings either.
|
|
|
|
**Conclusion**: SNMP configuration is only available through the devices'
|
|
local web UIs, NOT through the Omada cloud controller. The cloud controller
|
|
simply does not provide this capability.
|
|
|
|
### Prerequisites for SNMP via Local Device UIs
|
|
|
|
To enable SNMP on the devices, you need access to each device's local web UI:
|
|
|
|
1. **Router (172.16.1.1)**: Local login is BLOCKED — shows *"Note: This
|
|
Gateway is being managed by Controller 99.80.193.119"*. Must enable
|
|
Local Management via Omada controller first (Configuration → Device
|
|
Config → Gateway → Local Management — if this setting exists in your
|
|
controller version).
|
|
2. **Switch (172.16.1.2)**: Local web UI is accessible (no "managed by
|
|
controller" message), but requires device-specific admin credentials.
|
|
Neither `admin/admin` nor the Omada cloud password work. The local
|
|
credentials are NOT stored in 1Password.
|
|
3. **APs**: Each AP may have a local web UI at its 172.16.1.x address.
|
|
Credentials unknown.
|
|
|
|
### Omada API Exporters — Do NOT Work with Cloud Controller
|
|
|
|
Two community Prometheus exporters exist for TP-Link Omada:
|
|
- `charlie-haley/omada_exporter` (140 stars) — Docker image `chhaley/omada_exporter`
|
|
- `RCooLeR/omada_exporter` (56 stars) — Docker image `ghcr.io/rcooler/omada_exporter`
|
|
|
|
**Both require a LOCAL Omada controller URL** (`OMADA_HOST=https://192.168.x.x`).
|
|
They use the Omada controller's REST API (`/api/v2/...`), which is only
|
|
served by on-premise controllers. The cloud controller at
|
|
`omada.tplinkcloud.com` returns HTML (SPA) for all API paths — it does NOT
|
|
expose a REST API backend.
|
|
|
|
**To use these exporters, you would need to run a local Omada Software
|
|
Controller** (Docker container) and migrate devices from the cloud
|
|
controller to the local one.
|
|
|
|
### Local Omada Software Controller Migration (In Progress)
|
|
|
|
**Decision (2026-07-05)**: Migrate from cloud controller to a local Omada
|
|
Software Controller to enable SNMP and API-based monitoring.
|
|
|
|
**Rationale**: The cloud controller cannot enable SNMP or serve API
|
|
exporters. A local controller unlocks both paths. Devices retain cached
|
|
config during controller downtime, so the circular dependency
|
|
(controller needs network ↔ network managed by controller) is not
|
|
real — devices boot with last-known config and continue operating.
|
|
|
|
**Deployment target**: CT 142 (`omada-controller`, 10.0.30.142/24,
|
|
VLAN 30, proxmox7, vm_disks storage, 2 cores / 4 GB RAM / 32 GB disk).
|
|
Created via `pct create` with Debian 12 template.
|
|
|
|
**Migration steps** (planned):
|
|
1. Install Docker on CT 142
|
|
2. Deploy Omada Software Controller container (TP-Link official image)
|
|
3. Run setup wizard (admin user, site "Rohr")
|
|
4. Remove devices from cloud controller
|
|
5. Adopt devices on local controller (devices at 172.16.1.x)
|
|
6. Deploy `omada_exporter` (charlie-haley or RCooLeR) pointing at
|
|
`http://10.0.30.142:8088`
|
|
7. Add Prometheus scrape config for omada_exporter
|
|
8. Extend Grafana dashboard with Omada panels
|
|
|
|
**Circular Dependency Analysis** (user-raised concern):
|
|
- Cold-start sequence: Gateway boots (cached config) → Switch boots
|
|
(cached config) → PVE nodes boot → HA manager starts CT 142 →
|
|
Controller reconnects to devices
|
|
- Risk: only if gateway config is corrupt AND controller is down
|
|
simultaneously — very unlikely
|
|
- Mitigation: CT 142 is in HA-manager (auto-restart on any node)
|
|
|
|
### Alternatives for Omada Metrics Collection
|
|
|
|
Since neither SNMP nor API exporters work with the cloud controller:
|
|
|
|
1. **Run a local Omada Software Controller** — IN PROGRESS (CT 142).
|
|
Devices will be re-adopted from the local controller. Then both
|
|
SNMP (via local device UIs) and API exporters will work.
|
|
2. **Browser automation scraping** — Periodically read device metrics
|
|
(CPU, memory, temperature, port stats, client counts) from the Omada
|
|
cloud controller via browser automation and push to Prometheus via
|
|
pushgateway. Hacky but functional.
|
|
3. **Accept the limitation** — The existing Grafana dashboard
|
|
(`network-infra`, uid: `network-infra`) already tracks PVE nodes,
|
|
guests, HA state, and blackbox probes. Omada device metrics (CPU,
|
|
memory, temp) are visible in the Omada UI but not in Prometheus.
|
|
|
|
### Device Inventory (Verified 2026-07-05)
|
|
|
|
All devices are on VLAN 1 (CatchAll/Default, 172.16.1.0/24):
|
|
|
|
| Device | Model | HW/FW | IP | Notes |
|
|
|--------|-------|-------|-----|-------|
|
|
| Router | DR3650v-4G | HW v1.0 / FW 1.1.1 | 172.16.1.1 | Primary gateway, VLAN routing. Also at 10.0.30.1 (VLAN 30 interface) |
|
|
| Core (Switch) | SG3428MP | HW v6.20 / FW 6.20.26 | 172.16.1.2 | PoE switch, 28 ports, firmware update available (6.20.27) |
|
|
| AP Erdgeschoss | EAP683 UR(EU) | HW v1.0 / FW 1.4.0 | 172.16.1.3 | Connected to switch port 16 |
|
|
| AP Obergeschoss | EAP683 UR(EU) | HW v1.0 / FW 1.4.0 | 172.16.1.4 | Connected to switch port 14 |
|
|
| AP Garage | EAP615-Wall(EU) | HW v1.0 / FW 1.5.4 | 172.16.1.5 | Connected to switch port 21 |
|
|
| AP Garten | EAP225-Outdoor(EU) | HW v3.0 / FW 5.2.2 | 172.16.1.6 | Outdoor AP |
|
|
|
|
> **Note**: The 10.0.30.1, 10.0.20.1, etc. addresses are the gateway's VLAN
|
|
> interfaces, not separate devices. All Omada-managed devices use
|
|
> 172.16.1.x (VLAN 1 / CatchAll) as their management IP. The Omada
|
|
> controller communicates with devices via the organization connection IP
|
|
> (84.39.78.100) through the cloud.
|
|
|
|
### snmp_exporter Deployment Status
|
|
|
|
**snmp_exporter IS deployed** on CT 141 as a binary + systemd service
|
|
(v0.27.0, port 9116, using the official snmp.yml with all standard MIBs).
|
|
Docker-based deployment on CT 141 does not work — see the deployment
|
|
section below for details. The exporter is running and ready; it just
|
|
needs SNMP to be enabled on the Omada devices to start collecting.
|
|
|
|
The monitoring stack on CT 141 runs via Docker: prometheus, grafana,
|
|
alertmanager, blackbox-exporter, pve-exporter, telegram-bridge.
|
|
snmp_exporter is the exception — it runs as a systemd service, not Docker.
|
|
|
|
### Deploying snmp_exporter in Monitoring Stack
|
|
|
|
> **Critical**: Docker-based deployment of snmp_exporter on CT 141 FAILS.
|
|
> Containers get stuck in "Created" state, `docker start`/`docker logs`/
|
|
> `docker inspect` all hang indefinitely. This affects both `docker run`
|
|
> and `docker compose up`. The root cause is unclear (other containers
|
|
> like prometheus/grafana work fine), but the pattern is 100%
|
|
> reproducible. **Use the binary + systemd approach instead** (below).
|
|
|
|
#### Approach A (RECOMMENDED): Binary + systemd service
|
|
|
|
Works reliably on CT 141. Tested with snmp_exporter v0.27.0.
|
|
|
|
```bash
|
|
# On CT 141 (via SSH to proxmox7, pct exec 141):
|
|
curl -sL -o /tmp/snmp_exporter.tar.gz \
|
|
https://github.com/prometheus/snmp_exporter/releases/download/v0.27.0/snmp_exporter-0.27.0.linux-amd64.tar.gz
|
|
tar xzf /tmp/snmp_exporter.tar.gz -C /tmp/
|
|
|
|
# Install binary
|
|
cp /tmp/snmp_exporter-0.27.0.linux-amd64/snmp_exporter /usr/local/bin/snmp_exporter
|
|
chmod +x /usr/local/bin/snmp_exporter
|
|
|
|
# Use the official snmp.yml bundled with the release (1.4MB, 63574 lines)
|
|
# Contains all standard MIBs including if_mib, host_resources, etc.
|
|
cp /tmp/snmp_exporter-0.27.0.linux-amd64/snmp.yml /etc/snmp_exporter.yml
|
|
|
|
# Create systemd service
|
|
cat > /etc/systemd/system/snmp-exporter.service << 'EOF'
|
|
[Unit]
|
|
Description=SNMP Exporter for Prometheus
|
|
After=network.target
|
|
|
|
[Service]
|
|
ExecStart=/usr/local/bin/snmp_exporter --config.file=/etc/snmp_exporter.yml
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable --now snmp-exporter
|
|
# Verify: systemctl status snmp-exporter
|
|
# Test: curl -s "http://localhost:9116/snmp?target=172.16.1.1&module=if_mib&auth=public_v2"
|
|
# Metrics: curl -s http://localhost:9116/metrics | head -5
|
|
```
|
|
|
|
**Pitfall**: v0.27.0 does NOT have the `/-/healthy` endpoint (returns 404).
|
|
Use `curl -s http://localhost:9116/metrics` to verify the exporter is alive.
|
|
|
|
#### Approach B (NOT RECOMMENDED): Docker container
|
|
|
|
Adding snmp_exporter as a Docker container on CT 141 does not work.
|
|
`docker create` succeeds but `docker start` hangs for 30+ seconds,
|
|
then the container enters a crash-restart loop. `docker logs`,
|
|
`docker inspect`, and `docker rm` all hang. The only way to recover
|
|
is `docker container prune -f` or waiting for operations to eventually
|
|
complete after 30-60s timeouts. Both the full 2MB snmp.yml and a
|
|
minimal custom snmp.yml produce the same result. This is specific to
|
|
CT 141 — not a general snmp_exporter Docker issue.
|
|
|
|
### Prometheus Scrape Configuration
|
|
|
|
Add to `/opt/monitoring/prometheus/prometheus.yml` on CT 141:
|
|
|
|
```yaml
|
|
- job_name: snmp_omada
|
|
scrape_interval: 30s
|
|
static_configs:
|
|
- targets:
|
|
- 172.16.1.1 # Gateway (DR3650v-4G)
|
|
- 172.16.1.2 # Switch (SG3428MP)
|
|
- 172.16.1.3 # AP Erdgeschoss (EAP683 UR)
|
|
- 172.16.1.4 # AP Obergeschoss (EAP683 UR)
|
|
- 172.16.1.5 # AP Garage (EAP615-Wall)
|
|
- 172.16.1.6 # AP Garten (EAP225-Outdoor)
|
|
metrics_path: /snmp
|
|
params:
|
|
module: [if_mib]
|
|
auth: [public_v2]
|
|
relabel_configs:
|
|
- source_labels: [__address__]
|
|
target_label: __param_target
|
|
- source_labels: [__param_target]
|
|
target_label: instance
|
|
- target_label: __address__
|
|
replacement: localhost:9116 # snmp_exporter runs as systemd, not Docker
|
|
```
|
|
|
|
Then reload: `curl -s -X POST http://localhost:9090/-/reload`
|
|
Verify: `curl -s http://localhost:9090/api/v1/targets | python3 -m json.tool | grep snmp`
|
|
|
|
### TP-Link Enterprise OIDs
|
|
TP-Link uses enterprise OID 11863 (per IANA assignment). Key MIB subtrees:
|
|
- `1.3.6.1.4.1.11863.1` — tp-linkDevice
|
|
- `1.3.6.1.4.1.11863.2` — tp-linkCommon
|
|
- `1.3.6.1.4.1.11863.3` — tp-linkPort
|
|
- `1.3.6.1.4.1.11863.6` — tp-linkSwitch
|
|
|
|
Standard IF-MIB (`1.3.6.1.2.1.2`) works for interface stats on all devices.
|
|
Host Resources MIB (`1.3.6.1.2.1.25`) works for CPU/memory on devices that
|
|
expose it.
|
|
|
|
## Best Practices
|
|
- Always wait 5-8s after navigation before snapshotting — the SPA is slow
|
|
- Use `browser_console` for iframe-internal interactions when `browser_click` refs fail
|
|
- **Prefer async IIFE console chains** over individual tool calls for multi-step flows — fewer session-expiry failures
|
|
- Dismiss all popup dialogs before attempting navigation
|
|
- Channel optimization disrupts connectivity — confirm with user before triggering
|
|
- Check "Optimization History" to verify optimization completed successfully
|
|
- After triggering optimization, expect session to drop — re-login to check results
|