rsspot
rsspot is a Python SDK and CLI for Rackspace Spot with unified sync/async ergonomics, sqlite-backed runtime state, and profile-based configuration.
Highlights
- Unified client API:
SpotClientfor sync + async access on one objectAsyncSpotClientfor explicit async-only usage
- Persistent sqlite state (
state.db) for:- defaults/preferences
- HTTP cache
- redacted CLI command history
- VM registration ledger entries
- Config precedence + migration:
- new default path:
~/.config/rsspot/config.yml - legacy
~/.spot_configauto-imported/migrated
- new default path:
- Strong typing via
pydanticmodels - Multi-profile workflows + global singleton helpers
- OpenAPI sync/index scripts for upstream tracking
Install
uv sync
Quickstart (CLI)
uv run rsspot configure \
--profile default \
--org <org-name> \
--region us-central-dfw-1 \
--refresh-token "$SPOT_REFRESH_TOKEN"
uv run rsspot organizations list --output table
Pricing Explorer + Builder
# default output is a rich table when --output is not explicitly set
uv run rsspot pricing list --region us-central-dfw-1 --class gp,ch --min-cpu 4 --gen 2 --nodes 5
# explicit machine-readable output stays json/yaml
uv run rsspot -o json pricing list --class gp --max-cpu 8 --nodes 10
# generate 3 recommendation strategies (max_performance, max_value, balanced)
uv run rsspot pricing build --nodes 5 --risk med --classes gp,ch,mh
# spread balanced strategy across multiple pools and constrain cluster hourly spend
uv run rsspot pricing build --nodes 5 --balanced --risk low --max-hour 0.25
# constrain by total cluster monthly spend
uv run rsspot pricing build --nodes 5 --min-month 50 --max-month 180
Quickstart (SDK)
Unified client
from rsspot import SpotClient
client = SpotClient(profile="default")
orgs = client.organizations.list()
print([org.name for org in orgs.organizations])
client.close()
import asyncio
from rsspot import SpotClient
async def main() -> None:
client = SpotClient(profile="default")
orgs = await client.aorganizations.list()
print([org.name for org in orgs.organizations])
await client.aclose()
asyncio.run(main())
Async-only client
import asyncio
from rsspot import AsyncSpotClient
async def main() -> None:
async with AsyncSpotClient(profile="default") as client:
regions = await client.regions.list()
print([r.name for r in regions])
asyncio.run(main())
Configuration
Resolution precedence:
- Runtime config dict/model passed to client constructor
- Explicit
config_path=... - Env path (
RSSPOT_CONFIG,RSSPOT_CONFIG_FILE,SPOT_CONFIG_FILE) - Default search in
~/.config/rsspot/config.{yml,yaml,toml,json} - Legacy fallback
~/.spot_config(auto-migrated)
Expected schema is identical across config.yaml, config.toml, and config.json.
config.yaml
version: "2"
default_profile: prod
active_profile: prod
state_path: /Users/you/.config/rsspot/state.db
profiles:
prod:
org: sparkai
region: us-central-dfw-1
refresh_token: replace-me
base_url: https://spot.rackspace.com
oauth_url: https://spot.rackspace.com
request_timeout_seconds: 30
verify_ssl: true
preferences:
default_profile: prod
default_org: sparkai
default_region: us-central-dfw-1
retry:
max_attempts: 4
base_delay: 0.2
max_delay: 2.5
jitter: 0.2
cache:
enabled: true
default_ttl: 5
max_entries: 1000
backend: sqlite
config.toml
version = "2"
default_profile = "prod"
active_profile = "prod"
state_path = "/Users/you/.config/rsspot/state.db"
[profiles.prod]
org = "sparkai"
region = "us-central-dfw-1"
refresh_token = "replace-me"
base_url = "https://spot.rackspace.com"
oauth_url = "https://spot.rackspace.com"
request_timeout_seconds = 30
verify_ssl = true
[preferences]
default_profile = "prod"
default_org = "sparkai"
default_region = "us-central-dfw-1"
[retry]
max_attempts = 4
base_delay = 0.2
max_delay = 2.5
jitter = 0.2
[cache]
enabled = true
default_ttl = 5
max_entries = 1000
backend = "sqlite"
config.json
{
"version": "2",
"default_profile": "prod",
"active_profile": "prod",
"state_path": "/Users/you/.config/rsspot/state.db",
"profiles": {
"prod": {
"org": "sparkai",
"region": "us-central-dfw-1",
"refresh_token": "replace-me",
"base_url": "https://spot.rackspace.com",
"oauth_url": "https://spot.rackspace.com",
"request_timeout_seconds": 30,
"verify_ssl": true
}
},
"preferences": {
"default_profile": "prod",
"default_org": "sparkai",
"default_region": "us-central-dfw-1"
},
"retry": {
"max_attempts": 4,
"base_delay": 0.2,
"max_delay": 2.5,
"jitter": 0.2
},
"cache": {
"enabled": true,
"default_ttl": 5,
"max_entries": 1000,
"backend": "sqlite"
}
}
Notes:
profiles.<name>is where profile-specific credentials/selectors live.- Top-level
retry/cacheare defaults; profile-levelretry/cachecan override them. - Legacy aliases like
refreshToken,clientId, andbaseUrlare still accepted.
VM Registration Workflow Primitives
rsspot includes registration state helpers for composing with external orchestrators (including separate omni-sdk scripts) without taking a direct dependency.
Core entrypoint:
rsspot.workflows.RegistrationWorkflow
OpenAPI Tracking
uv run python scripts/sync_openapi.py
uv run python scripts/generate_openapi_index.py
Development
uv run ruff check src tests
uv run mypy src
uv run pytest -q
Changelog
See CHANGELOG.md.
Metadata
Release files for rsspot 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rsspot-0.3.0.tar.gz | 92.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rsspot-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 144.4 kB
Release files / rsspot-0.3.0.tar.gz
| Download URL | rsspot-0.3.0.tar.gz |
|---|---|
| Size | 92.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e7048a2f867461eb3ee4ff3f898e684837bded8333e0457822acb7edeb758fe3
|
|
BLAKE2b-256 checksum How to use checksums |
24a9c85d029e13c393f7b63e9eeb4d110b09457f3e22945a340923ceb8e2417f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rsspot-0.3.0-py3-none-any.whl
| Download URL | rsspot-0.3.0-py3-none-any.whl |
|---|---|
| Size | 51.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
895479fc68c9b6222a297b9c791a609f6749c4cf2b8aa9b98201d1c4173189a9
|
|
BLAKE2b-256 checksum How to use checksums |
6c322577a1bc25b27fadb611549eea3e26b7618eb77931666144544dfe92c3bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|