An SDK for interacting with Azimuth resources using Python.
Both synchronous use and asynchronous use, using the async/await
syntax from
asyncio, are supported.
The Azimuth SDK can be installed from PyPI:
pip install azimuth-sdk
To begin using the Azimuth SDK, you must first create a configuration object that defines how to authenticate with Azimuth.
Currently, authentication with Azimuth uses OpenStack credentials. Azimuth supports two user-facing authentication methods:
- Username + Password
- Keystone federation
The Keystone federation flow requires a browser, so is not supported by the Azimuth SDK.
Azimuth also supports authenticating with an application credential. This authentication method is usually hidden from browser-based users, but is available for use by the SDK. This is the recommended authentication method.
The SDK Configuration
object can be initialised either using known credentials or from a
clouds.yaml file.
The Azimuth SDK respects the same OS_CLOUD
and OS_CLIENT_CONFIG_FILE
environment variables that
the OpenStack CLI respects.
from azimuth_sdk import Configuration
AZIMUTH_URL = "https://portal.azimuth.example.org"
# Initialise from variables
config = Configuration.create(
AZIMUTH_URL,
authenticator = "appcred",
auth_data = {
"application_credential_id": "<application credential id>",
"application_credential_secret": "<application credential secret>",
},
# Optionally set a default tenancy
default_tenancy_id = "<tenancy id>"
)
# Initialise from a specific clouds.yaml file
config = Configuration.from_openstack_clouds_file(
AZIMUTH_URL,
"/path/to/openstack/clouds.yaml"
)
# Initialise from environment variables
config = Configuration.from_environment(AZIMUTH_URL)
Once you have a Configuration
object, it can be used to create either synchronous or
asynchronous clients with which you can interact with Azimuth, e.g. by listing the
available tenancies:
# Synchronous client
with config.sync_client() as client:
for tenancy in client.tenancies().list():
print(tenancy.name)
# Asynchronous client
async with config.async_client() as client:
async for tenancy in client.tenancies().list():
print(tenancy.name)
WARNING
It is important that the client is used inside a
with
orasync with
block, for synchronous or asynchronous clients respectively, as this ensures that resources are set up and released as required.See Python's contextlib for more information.
You can then interact with resources for a tenancy. The following resources are available:
# Interact with the images for a tenancy
client.images(tenancy_id = None)
# Interact with the sizes for a tenancy
client.sizes(tenancy_id = None)
# Interact with the volumes for a tenancy
client.volumes(tenancy_id = None)
# Interact with the external IPs for a tenancy
client.external_ips(tenancy_id = None)
# Interact with the machines for a tenancy
client.machines(tenancy_id = None)
# Interact with the CaaS cluster types for a tenancy
client.cluster_types(tenancy_id = None)
# Interact with the CaaS clusters for a tenancy
client.clusters(tenancy_id = None)
# Interact with the Kubernetes templates for a tenancy
client.kubernetes_cluster_templates(tenancy_id = None)
# Interact with the Kubernetes clusters for a tenancy
client.kubernetes_clusters(tenancy_id = None)
# Interact with the Kubernetes app templates for a tenancy
client.kubernetes_app_templates(tenancy_id = None)
# Interact with the Kubernetes apps for a tenancy
client.kubernetes_apps(tenancy_id = None)
For each of these methods, the tenancy_id
is optional. If it is not given, a default
tenancy ID will be used, which is determined as follows:
- Explicitly set when the
Configuration
is created - From
clouds.{cloud}.auth.project_id
in theclouds.yaml
, if present - As the first available tenancy in the tenancy list (may not be deterministic)
The default tenancy for a client can also be changed using the switch_tenancy
method:
client.switch_tenancy(new_tenancy_id)
Each of these returns a Resource
object, which can be interacted with as follows.
NOTE
The sync methods are available on resources produced by synchronous clients (i.e. created using
config.sync_client
), and the async methods on resources produced by asynchronous clients (i.e. created usingconfig.async_client
).
# List instances
# sync
for instance in resource.list():
print(instance)
# async
async for instance in resource.list():
print(instance)
# Get the first instance from a list response
# sync
instance = resource.first()
# async
instance = await resource.first()
# Fetch an instance by ID
# sync
instance = resource.fetch("<id>")
# async
instance = await resource.fetch("<id>")
# Create a new instance
# sync
instance = resource.create({"name": "my-instance", "<prop>": "<value>"})
# async
instance = await resource.create({ "name": "my-instance", "<prop>": "<value>"})
# Replace an instance by ID (PUT request)
# sync
instance = resource.replace("<id>", {"name": "my-instance", "<prop>": "<value>"})
# async
instance = await resource.replace("<id>", {"name": "my-instance", "<prop>": "<value>"})
# Patch an instance by ID (PATCH request)
# sync
instance = resource.patch("<id>", {"<prop>": "<value>"})
# async
instance = await resource.patch("<id>", {"<prop>": "<value>"})
# Replace an instance by ID, or create it (using the same data) if it doesn't exist
# sync
instance = resource.create_or_replace("<id>", {"name": "my-instance", "<prop>": "<value>"})
# async
instance = await resource.create_or_replace("<id>", {"name": "my-instance", "<prop>": "<value>"})
# Patch an instance by ID, or create it (using the same data) if it doesn't exist
# sync
instance = resource.create_or_patch("<id>", {"<prop>": "<value>"})
# async
instance = await resource.create_or_patch("<id>", {"<prop>": "<value>"})
# Delete an instance by ID
# sync
instance = resource.delete("<id>")
# async
instance = await resource.delete("<id>")