Skip to content

Commit ac62836

Browse files
authored
fix device_utils (#16)
This release improves async support with a new `arun()` helper, makes the Device Utilities module fully non-blocking, adds VT100 terminal emulation for screen-based commands, and introduces interactive SSH shell access for EX/SRX devices. (PR #16) --- ### 1. NEW FEATURES #### **`mistapi.arun()` — Async Helper** New helper function to run any sync mistapi function without blocking the event loop. Wraps the function call in `asyncio.to_thread()` so blocking HTTP requests run in a thread pool. ```python import asyncio import mistapi from mistapi.api.v1.sites import devices async def main(): session = mistapi.APISession(env_file="~/.mist_env") session.login() # Run sync API call without blocking the event loop response = await mistapi.arun(devices.listSiteDevices, session, site_id) print(response.data) asyncio.run(main()) ``` #### **Interactive SSH Shell** (`device_utils.ex` / `device_utils.srx`) New `interactiveShell()` and `createShellSession()` functions for SSH-over-WebSocket access to EX and SRX devices. - `interactiveShell()` — takes over the terminal for human SSH access (uses `sshkeyboard`) - `createShellSession()` — returns a `ShellSession` object for programmatic send/recv - `ShellSession` — bidirectional WebSocket session with `send()`, `recv()`, `resize()`, context manager support ```python from mistapi.device_utils import ex # Interactive (human at the keyboard) ex.interactiveShell(apisession, site_id, device_id) # Programmatic with ex.createShellSession(apisession, site_id, device_id) as session: session.send_text("show version\r\n") import time; time.sleep(3) while (data := session.recv(timeout=0.5)): print(data.decode("utf-8", errors="replace"), end="") ``` #### **`topCommand`** (`device_utils.ex` / `device_utils.srx`) New `topCommand()` function to stream `top` output from EX and SRX devices. Uses VT100 screen-buffer rendering for proper in-place display. #### **VT100 Terminal Emulation** Added ANSI escape stripping and a minimal VT100 screen-buffer renderer for device command output. Stream-mode commands (ping, traceroute) have ANSI codes stripped automatically. Screen-mode commands (top, monitor interface) are rendered through a virtual terminal buffer. --- ### 2. IMPROVEMENTS #### **Non-Blocking Device Utilities** All `mistapi.device_utils` functions now return immediately. The HTTP trigger and WebSocket streaming run in background threads, allowing your code to continue executing while data is collected. **UtilResponse Object:** | Method/Property | Description | |-----------------|-------------| | `.ws_data` | List of processed messages | | `.done` | `True` if data collection is complete | | `.wait(timeout)` | Block until complete, returns self | | `.receive()` | Generator yielding messages as they arrive | | `.disconnect()` | Stop the WebSocket connection early | | `await response` | Async-friendly wait (non-blocking event loop) | **Example Usage:** ```python from mistapi.device_utils import ex # Non-blocking - returns immediately, data collected in background response = ex.ping(apisession, site_id, device_id, host="8.8.8.8") do_other_work() # Can do other things while waiting response.wait() # Block when ready to collect results print(response.ws_data) # Generator style - process messages as they arrive for msg in response.receive(): print(msg) # Async-friendly - doesn't block the event loop await response ``` #### **Binary WebSocket Frame Support** `_MistWebsocket._handle_message()` now handles binary frames (strips null bytes, decodes UTF-8 with replacement characters). #### **Trigger-Only Commands Run Synchronously** Fire-and-forget device commands (e.g., `clearMacTable`, `clearBpduError`, `clearHitCount`) that don't require a WebSocket stream now run the API trigger synchronously, ensuring `trigger_api_response` is immediately available on the returned `UtilResponse`. --- ### 3. BUG FIXES - Fixed double-space typo in API token privilege mismatch error message - Fixed `first_message_timeout` timer stop to check timer is active before stopping --- ### 4. DEPENDENCIES - Added `sshkeyboard>=2.3.1` (for `interactiveShell()`) ---
1 parent 2fcb483 commit ac62836

50 files changed

Lines changed: 2514 additions & 2845 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,4 @@ cython_debug/
152152
#.idea/
153153

154154
test/
155+
debug.py

CHANGELOG.md

Lines changed: 117 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,116 @@
11
# CHANGELOG
2+
## Version 0.61.1 (March 2026)
3+
4+
**Released**: March 15, 2026
5+
6+
This release improves async support with a new `arun()` helper, makes the Device Utilities module fully non-blocking, adds VT100 terminal emulation for screen-based commands, and introduces interactive SSH shell access for EX/SRX devices. (PR #16)
7+
8+
---
9+
10+
### 1. NEW FEATURES
11+
12+
#### **`mistapi.arun()` — Async Helper**
13+
New helper function to run any sync mistapi function without blocking the event loop. Wraps the function call in `asyncio.to_thread()` so blocking HTTP requests run in a thread pool.
14+
15+
```python
16+
import asyncio
17+
import mistapi
18+
from mistapi.api.v1.sites import devices
19+
20+
async def main():
21+
session = mistapi.APISession(env_file="~/.mist_env")
22+
session.login()
23+
24+
# Run sync API call without blocking the event loop
25+
response = await mistapi.arun(devices.listSiteDevices, session, site_id)
26+
print(response.data)
27+
28+
asyncio.run(main())
29+
```
30+
31+
#### **Interactive SSH Shell** (`device_utils.ex` / `device_utils.srx`)
32+
New `interactiveShell()` and `createShellSession()` functions for SSH-over-WebSocket access to EX and SRX devices.
33+
34+
- `interactiveShell()` — takes over the terminal for human SSH access (uses `sshkeyboard`)
35+
- `createShellSession()` — returns a `ShellSession` object for programmatic send/recv
36+
- `ShellSession` — bidirectional WebSocket session with `send()`, `recv()`, `resize()`, context manager support
37+
38+
```python
39+
from mistapi.device_utils import ex
40+
41+
# Interactive (human at the keyboard)
42+
ex.interactiveShell(apisession, site_id, device_id)
43+
44+
# Programmatic
45+
with ex.createShellSession(apisession, site_id, device_id) as session:
46+
session.send_text("show version\r\n")
47+
import time; time.sleep(3)
48+
while (data := session.recv(timeout=0.5)):
49+
print(data.decode("utf-8", errors="replace"), end="")
50+
```
51+
52+
#### **`topCommand`** (`device_utils.ex` / `device_utils.srx`)
53+
New `topCommand()` function to stream `top` output from EX and SRX devices. Uses VT100 screen-buffer rendering for proper in-place display.
54+
55+
#### **VT100 Terminal Emulation**
56+
Added ANSI escape stripping and a minimal VT100 screen-buffer renderer for device command output. Stream-mode commands (ping, traceroute) have ANSI codes stripped automatically. Screen-mode commands (top, monitor interface) are rendered through a virtual terminal buffer.
57+
58+
---
59+
60+
### 2. IMPROVEMENTS
61+
62+
#### **Non-Blocking Device Utilities**
63+
All `mistapi.device_utils` functions now return immediately. The HTTP trigger and WebSocket streaming run in background threads, allowing your code to continue executing while data is collected.
64+
65+
**UtilResponse Object:**
66+
| Method/Property | Description |
67+
|-----------------|-------------|
68+
| `.ws_data` | List of processed messages |
69+
| `.done` | `True` if data collection is complete |
70+
| `.wait(timeout)` | Block until complete, returns self |
71+
| `.receive()` | Generator yielding messages as they arrive |
72+
| `.disconnect()` | Stop the WebSocket connection early |
73+
| `await response` | Async-friendly wait (non-blocking event loop) |
74+
75+
**Example Usage:**
76+
```python
77+
from mistapi.device_utils import ex
78+
79+
# Non-blocking - returns immediately, data collected in background
80+
response = ex.ping(apisession, site_id, device_id, host="8.8.8.8")
81+
do_other_work() # Can do other things while waiting
82+
response.wait() # Block when ready to collect results
83+
print(response.ws_data)
84+
85+
# Generator style - process messages as they arrive
86+
for msg in response.receive():
87+
print(msg)
88+
89+
# Async-friendly - doesn't block the event loop
90+
await response
91+
```
92+
93+
#### **Binary WebSocket Frame Support**
94+
`_MistWebsocket._handle_message()` now handles binary frames (strips null bytes, decodes UTF-8 with replacement characters).
95+
96+
#### **Trigger-Only Commands Run Synchronously**
97+
Fire-and-forget device commands (e.g., `clearMacTable`, `clearBpduError`, `clearHitCount`) that don't require a WebSocket stream now run the API trigger synchronously, ensuring `trigger_api_response` is immediately available on the returned `UtilResponse`.
98+
99+
---
100+
101+
### 3. BUG FIXES
102+
103+
- Fixed double-space typo in API token privilege mismatch error message
104+
- Fixed `first_message_timeout` timer stop to check timer is active before stopping
105+
106+
---
107+
108+
### 4. DEPENDENCIES
109+
110+
- Added `sshkeyboard>=2.3.1` (for `interactiveShell()`)
111+
112+
---
113+
2114
## Version 0.61.0 (March 2026)
3115

4116
**Released**: March 13, 2026
@@ -19,7 +131,7 @@ Complete real-time event streaming support with flexible consumption patterns:
19131
|-------|-------------|
20132
| `mistapi.websockets.orgs.InsightsEvents` | Real-time insights events for an organization |
21133
| `mistapi.websockets.orgs.MxEdgesStatsEvents` | Real-time MX edges stats for an organization |
22-
| `mistapi.websockets.orgs.MxEdgesUpgradesEvents` | Real-time MX edges upgrades events for an organization |
134+
| `mistapi.websockets.orgs.MxEdgesEvents` | Real-time MX edges events for an organization |
23135

24136
* Site Channels
25137

@@ -28,7 +140,8 @@ Complete real-time event streaming support with flexible consumption patterns:
28140
| `mistapi.websockets.sites.ClientsStatsEvents` | Real-time clients stats for a site |
29141
| `mistapi.websockets.sites.DeviceCmdEvents` | Real-time device command events for a site |
30142
| `mistapi.websockets.sites.DeviceStatsEvents` | Real-time device stats for a site |
31-
| `mistapi.websockets.sites.DeviceUpgradesEvents` | Real-time device upgrades events for a site |
143+
| `mistapi.websockets.sites.DeviceEvents` | Real-time device events for a site |
144+
| `mistapi.websockets.sites.MxEdgesEvents` | Real-time MX edges events for a site |
32145
| `mistapi.websockets.sites.MxEdgesStatsEvents` | Real-time MX edges stats for a site |
33146
| `mistapi.websockets.sites.PcapEvents` | Real-time PCAP events for a site |
34147

@@ -89,7 +202,7 @@ print(result.ws_data)
89202
def handle(msg):
90203
print("got:", msg)
91204

92-
result = ex.cableTest(apisession, site_id, device_id, port="ge-0/0/0", on_message=handle)
205+
result = ex.cableTest(apisession, site_id, device_id, port_id="ge-0/0/0", on_message=handle)
93206
```
94207

95208
#### **1.3 New API Endpoints**
@@ -153,49 +266,6 @@ result = ex.cableTest(apisession, site_id, device_id, port="ge-0/0/0", on_messag
153266

154267
---
155268

156-
## Version 0.60.3 (February 2026)
157-
158-
**Released**: February 21, 2026
159-
160-
This release add a missing query parameter to the `searchOrgWanClients()` function.
161-
162-
---
163-
164-
### 1. CHANGES
165-
166-
##### **API Function Updates**
167-
- Updated `searchOrgWanClients()` and related functions in `orgs/wan_clients.py`.
168-
169-
---
170-
171-
## Version 0.60.1 (February 2026)
172-
173-
**Released**: February 21, 2026
174-
175-
This release includes function updates and bug fixes in the self/logs.py and sites/sle.py modules.
176-
177-
---
178-
179-
### 1. CHANGES
180-
181-
##### **API Function Updates**
182-
- Updated `listSelfAuditLogs()` and related functions in `self/logs.py`.
183-
- Updated deprecated and new SLE classifier functions in `sites/sle.py`.
184-
185-
---
186-
187-
### 2. BUG FIXES
188-
189-
- Minor bug fixes and improvements in API modules.
190-
191-
---
192-
193-
### Breaking Changes
194-
195-
No breaking changes in this release.
196-
197-
---
198-
199269
## Version 0.60.4 (March 2026)
200270

201271
**Released**: March 3, 2026
@@ -715,4 +785,4 @@ Previous stable release. See commit history for details.
715785

716786
**Author**: Thomas Munzer <tmunzer@juniper.net>
717787
**License**: MIT License
718-
**Python Compatibility**: Python 3.8+
788+
**Python Compatibility**: Python 3.10+

0 commit comments

Comments
 (0)