ReadyView Dashboard Python Library
The ReadyView Dashboard Python library provides all current ReadyView dashboard API calls to interface with the ReadyLinks ReadyView cloud-managed platform. ReadyView generates the library based on dashboard API's OpenAPI spec to keep it up to date with the latest API releases, and provides the full source code for the library including the tools used to generate the library. ReadyLinks welcomes constructive pull requests that maintain backwards compatibility with prior versions. The library requires Python 3.8+, receives support from the community, and you can install it via PyPI:
pip install --upgrade readyview
Usage
- Export your Organization ID and API Key as an environment variable, for example:
export READYVIEW_DASHBOARD_ORG_ID=YOUR_ORG_ID
export READYVIEW_DASHBOARD_API_KEY=YOUR_API_KEY
The library calls https://api.readylinks.io/. To use another ReadyView API, such as a test server, set READYVIEW_BASE_URL.
2. Import the library with a single line at the top of your Python script.
import readyview
- Instantiate the dashboard client.
dashboard = readyview.Dashboard()
- Start programmatically interacting with your ReadyView dashboard!
my_networks = dashboard.network.get_networks()
Errors and strict mode
By default, a call that fails returns None, so existing scripts keep working. To find out why a call failed, turn on strict mode:
import readyview
dashboard = readyview.Dashboard(raise_errors=True)
try:
config = dashboard.switch.get_switch_configurations("AA:BB:CC:DD:EE:FF")
except readyview.ReadyViewAPIError as error:
print(error)
# GET /v1/devices/AA:BB:CC:DD:EE:FF/switch failed with HTTP 504: The device did not answer within the time limit.
In strict mode, a call that fails raises readyview.ReadyViewAPIError, which has:
status_code: the HTTP status, such as 401, 404, 429 or 504. It isNonewhen no reply arrived, because the API could not be reached or did not answer in time. Those calls raise the subclassreadyview.ReadyViewConnectionError.message: why the call failed, as the API explained it.methodandpath: the call that failed, such asGETand/v1/devices/AA:BB:CC:DD:EE:FF/switch.retry_after: when the reply has aRetry-Afterheader (usually with a 429, when you are rate limited), how many seconds it asks you to wait before trying again. OtherwiseNone.
The error is built from the API's reply only. It never repeats what you sent, or the values the API rejected, since those can be passwords.
Before anything is sent, a configuration is checked against the models in readyview.switch_post, whether you build a SwitchPost yourself or pass a dict to update_switch() or apply_settings(). One they cannot accept raises pydantic's ValidationError, in either mode. Its message names the field and the problem, but not the value, which can be a password.
Printing a model, or showing it in a notebook or a log line, leaves out its passwords, keys and community strings too, since repr() and str() skip them. model_dump() and the body that is sent still include them.
Calls that work return the same values in both modes, with two differences in strict mode: any 2xx status counts as success, and a reply that is not JSON (say, a hotel network's sign-in page) raises ReadyViewAPIError.
Logging
The library logs to the readyview logger with Python's logging module. It logs each reply at DEBUG. In the default mode, it also logs a failed call: an error status at INFO, and a request that got no reply at WARNING, which earlier versions printed. To see why calls return None without switching to strict mode:
import logging
logging.basicConfig()
logging.getLogger("readyview").setLevel(logging.INFO)
SNMPv3 users
SnmpV3User builds a user that a switch accepts. It checks the firmware's rules as it is built: a name of 3-16 characters, passwords of 9-15, printable ASCII without spaces, ' or \. dashboard.switch.add_snmp_user() then adds it safely:
import readyview
from readyview import SnmpV3User
dashboard = readyview.Dashboard()
user = SnmpV3User.auth_priv("nms-poller", auth_password="...", privacy_password="...")
for device in dashboard.network.get_devices("YOUR-NETWORK-ID"):
if device["device_type"] == "Switch" and device["status"] == "Online":
result = dashboard.switch.add_snmp_user(device["mac_address"], user, current=device)
print(device["name"], result.status.value, result.detail)
For each switch, add_snmp_user:
- reads the switch first, and leaves it alone if it already has the user, so it is safe to run again
- reports a
conflictwhen the name exists with other settings, since the API can add an SNMPv3 user but cannot change or delete one. A user the switch stores encrypted (ReadyView's SNMP user form does by default) counts as the same user when its name, access and algorithms match: its passwords cannot be compared - reports
fullwhen the switch already has 10 users - turns the SNMP service on if it is off
- checks the reply for refused values, then reads the configuration back until the switch reports the user, the only real confirmation
password_mode="cipher" (on SnmpV3User.auth_priv() or auth_no_priv()) has the switch store the passwords encrypted, as ReadyView's SNMP user form does by default:
- It hides rather than protects. The switch encrypts with a key built into its firmware. That keeps the passwords out of plain sight in ReadyView and the API, but it is not real secrecy.
- The read-back can't check the passwords. The switch reports them encrypted, so the read-back confirms everything but the passwords.
- Some passwords are refused. The switch would mistake a password whose last digits match a firmware pattern for one it had already encrypted, and store a different password. Those are refused.
Passing the switch's record as current= (the device records above include each switch's SNMP settings) saves the first read. When the record shows the switch without the user, the change goes out at once and the read-back is the only live read. The result still comes from the switch: a record that is out of date can cost a read, never change the outcome.
It never raises for a switch it could not configure: result.status, result.detail and result.ok say what happened. Its live configuration reads are spaced to stay under the API's limit of 30 a minute, and a read the API turns away with HTTP 429 is tried again after a pause. SnmpV3User.auth_no_priv() and SnmpV3User.no_auth_no_priv() build the other security levels, and dashboard.switch.get_snmp_users(mac) lists a switch's users without their passwords.
Secure connections
On Python 3.10 and later the library checks the API's certificate against the computer's own certificate store, as a browser does, through truststore. That is what makes it work on a network that inspects HTTPS, where the traffic is re-signed with a certificate that company IT has installed on the computer. On Python 3.8 and 3.9, run python -m pip install pip-system-certs, or set REQUESTS_CA_BUNDLE to the network's certificate. A REQUESTS_CA_BUNDLE you set is always used as given. When the check fails, the error says why and what to do.
Automation
Operations that report an outcome
These dashboard.switch methods each make one change and return an Outcome:
ok: whether the switch ends up as askedstatus: what happened, such assent,refused,up to dateorerrordetail: the API's reason, or what changed
They never raise for a switch they could not change, so a loop over many switches reports every one:
| Method | What it does |
|---|---|
apply_settings(mac, change) |
Sends a SwitchPost change and reports any value the switch refused |
use_template(mac, template_id) |
Applies a configuration template; reports skipped when the template is for another model |
upgrade_firmware(mac, version=None, channel=None) |
Starts an upgrade; reports up to date when the switch already has the firmware |
add_bandwidth_profiles(mac, profiles) |
Adds bandwidth profiles, or replaces those with the same ids |
bind_bandwidth_profile(mac, client_mac, profile_id) |
Finds the client's port and limits the client to a profile |
update_client(mac, client_mac, **fields) |
Changes a connected client, e.g. location="Room 101" |
add_snmp_user(mac, user, current=None) |
Adds an SNMPv3 user (see above) |
check_snmp_user(mac, name, privilege=, level=, ...) |
Says what add_snmp_user would do, without the passwords |
Each of the others takes dry_run=True to make a check instead: it only reads, and says what it would do.
- Would change: the status is
readyview.WOULD_CHANGE("would change"), and the detail says what, such asfrom 8.047 to 8.048orchanges syslog.server_ip_address. - Nothing to do: a switch it would leave alone gets the answer a run would give:
already configured,up to date, orskippedfor a template of another model. - Cannot go ahead: the status says why, with
okFalse, such asno such profile.
Checks use the switch's record (current=, as automation.select_switches() returns it) or the API's cached configuration. They never make a live read. The upgrade check asks the API which build the upgrade would install (GET /v1/devices/{mac}/switch/upgrade).
Scripts that change many switches
readyview.automation holds the parts every automation script repeats. That is the --network / --switch / --all-networks / --dry-run / --yes / --parallel N command line, plus picking the online switches, with a firmware minimum if you set one. It shows the plan, asks before changing anything, reports each switch, and ends with a summary and an exit status. A complete script:
from readyview import automation
from readyview.switch_post import Lldp, SwitchPost
parser = automation.command_line("Turn LLDP on.")
args = parser.parse_args()
change = SwitchPost(lldp=Lldp(enabled=True))
raise SystemExit(automation.run(
args, "turn LLDP on",
lambda dashboard, switch: dashboard.switch.apply_settings(switch["mac_address"], change),
min_firmware=7.655,
))
--parallel N (or run(..., parallel=N)) changes up to N switches at the same time, 1-16. automation.command_line(description, parallel=6) sets a script's default; it is 1 otherwise. Each switch spends most of its time waiting: on its reply, and for the change to show up when it is read back. Running several at once overlaps those waits, while live switch reads stay paced across all of them, so the API's rate limit holds. The outcomes are printed in the order of the plan.
--only MAC [MAC ...] changes only those of the chosen switches, for example the ones a run could not change. The others are listed as skipped.
--all-networks takes every online switch in the organization. It reads the switches once, as does a --switch list of more than 30. The device list allows 15 requests a minute, so a few hundred MACs looked up ten at a time would hit that limit.
Pass check= to make the plan exact: usually the same call with dry_run=True. A dry run, and the confirmation, then ask each switch first.
- Switches to change lists only the ones the run would change, each with what would change.
- Already as asked lists the ones the run would leave as they are.
- Skipped gets the ones the change does not apply to.
Runs with --yes make no checks.
automation.run(
args, "upgrade to the recommended firmware",
lambda dashboard, switch: dashboard.switch.upgrade_firmware(switch["mac_address"]),
check=lambda dashboard, switch: dashboard.switch.upgrade_firmware(switch["mac_address"], dry_run=True),
)
Ctrl+C starts no more switches and finishes the ones already started, one at a time or in parallel, so no switch is left halfway. The summary counts the rest as "not started".
automation.ask_secret(label, env_var) reads a password from the environment, or has it typed twice at a hidden prompt.
A program that runs these scripts, such as ReadyView's Automation scripts page, can follow them without reading the printed text. Set READYVIEW_EVENTS_FD to an open file descriptor, and run() also writes its plan, each switch as it starts and finishes, and the summary there, as one JSON object per line. The readyview.automation module documents the events.
Reports
readyview.reports.gather(dashboard, switches) reads every switch's ports and recent metrics from ReadyView's cache, several at a time. A switch that cannot be read is listed with the reason. device_rows(), client_rows(), fiber_rows() and firmware_rows() turn that into report rows, and find_device(mac, ...) finds where a device is connected. The rows are plain dicts, ready for pandas:
import pandas as pd
from readyview import reports
switches = dashboard.device.get_devices(params={"device_type": ["Switch"]})
data = reports.gather(dashboard, switches)
pd.DataFrame(reports.device_rows(switches, data)).to_excel("switches.xlsx", index=False)
Contribute
Create a branch to add your feature/fix then submit a merge request to main for review. Be sure to follow the correct format in your commit message to trigger the pipeline to submit a new release. Example commit below.
feat: Added the ability to update switch configurations.
fix: Fixes some bug.
BREAKING CHANGE: Updated function API.
Run the tests before you submit:
pip install -e ".[test]"
pytest
Metadata
Release files for readyview 1.23.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| readyview-1.23.1.tar.gz | 101.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| readyview-1.23.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 164.5 kB
Release files / readyview-1.23.1.tar.gz
| Download URL | readyview-1.23.1.tar.gz |
|---|---|
| Size | 101.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
32d004eb4460124af43b710c12bea73271122129089a901dca1e07580acd1af9
|
|
BLAKE2b-256 checksum How to use checksums |
f8606e364e516fb0e333365c1cdb8ba5537a3ceac8339b97f93afafb1905ba1d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
Hatch/1.18.1 {"ci":true,"cpu":"x86_64","distro":{"id":"trixie","libc":{"lib":"glibc","version":"2.41"},"name":"Debian GNU/Linux","version":"13"},"implementation":{"name":"CPython","version":"3.11.17"},"installer":{"name":"hatch","version":"1.18.1"},"openssl_version":"OpenSSL 3.5.7 9 Jun 2026","python":"3.11.17","system":{"name":"Linux","release":"5.15.154+"}} HTTPX2/2.14.0
|
Release files / readyview-1.23.1-py3-none-any.whl
| Download URL | readyview-1.23.1-py3-none-any.whl |
|---|---|
| Size | 63.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2e72821987f74a693971c27f1da189ec810114fe71eb9d915fdbceeb75061fb1
|
|
BLAKE2b-256 checksum How to use checksums |
cde3319b606a2f203651aa3e0b712533d440030d971a1039a7ff7a9d97ed7978
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
Hatch/1.18.1 {"ci":true,"cpu":"x86_64","distro":{"id":"trixie","libc":{"lib":"glibc","version":"2.41"},"name":"Debian GNU/Linux","version":"13"},"implementation":{"name":"CPython","version":"3.11.17"},"installer":{"name":"hatch","version":"1.18.1"},"openssl_version":"OpenSSL 3.5.7 9 Jun 2026","python":"3.11.17","system":{"name":"Linux","release":"5.15.154+"}} HTTPX2/2.14.0
|
Security for readyview 1.23.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.