# azimuth-sdk
An SDK for interacting with [Azimuth](https://github.com/azimuth-cloud/azimuth) resources using
[Python](https://www.python.org/).
Both synchronous use and asynchronous use, using the `async/await` syntax from
[asyncio](https://docs.python.org/3/library/asyncio.html), are supported.
## Installation
The Azimuth SDK can be installed from PyPI:
```sh
pip install azimuth-sdk
```
## Usage
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](https://www.openstack.org/) credentials.
Azimuth supports two user-facing authentication methods:
* Username + Password
* [Keystone federation](https://docs.openstack.org/keystone/latest/admin/federation/introduction.html)
The Keystone federation flow requires a browser, so is not supported by the Azimuth SDK.
Azimuth also supports authenticating with an
[application credential](https://docs.openstack.org/keystone/latest/user/application_credentials.html).
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](https://docs.openstack.org/python-openstackclient/pike/configuration/index.html).
The Azimuth SDK respects the same `OS_CLOUD` and `OS_CLIENT_CONFIG_FILE` environment variables that
the [OpenStack CLI](https://docs.openstack.org/python-openstackclient/latest/) respects.
```python
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:
```python
# 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` or `async with` block, for
> synchronous or asynchronous clients respectively, as this ensures that resources
> are set up and released as required.
>
> See Python's [contextlib](https://docs.python.org/3/library/contextlib.html) for more information.
You can then interact with resources for a tenancy. The following resources are available:
```python
# 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 the `clouds.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:
```python
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 using `config.async_client`).
```python
# 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>")
```
Raw data
{
"_id": null,
"home_page": "https://github.com/azimuth-cloud/azimuth-sdk",
"name": "azimuth-sdk",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "Matt Pryor",
"author_email": "matt@stackhpc.com",
"download_url": "https://files.pythonhosted.org/packages/18/08/1f05533df9626d087a6feee62738c8efc6bbba0db9970cab0d2888d7c337/azimuth_sdk-0.2.0.tar.gz",
"platform": null,
"description": "# azimuth-sdk\n\nAn SDK for interacting with [Azimuth](https://github.com/azimuth-cloud/azimuth) resources using\n[Python](https://www.python.org/).\n\nBoth synchronous use and asynchronous use, using the `async/await` syntax from\n[asyncio](https://docs.python.org/3/library/asyncio.html), are supported.\n\n## Installation\n\nThe Azimuth SDK can be installed from PyPI:\n\n```sh\npip install azimuth-sdk\n```\n\n## Usage\n\nTo begin using the Azimuth SDK, you must first create a configuration object that defines how\nto authenticate with Azimuth.\n\nCurrently, authentication with Azimuth uses [OpenStack](https://www.openstack.org/) credentials.\nAzimuth supports two user-facing authentication methods:\n\n * Username + Password\n * [Keystone federation](https://docs.openstack.org/keystone/latest/admin/federation/introduction.html)\n\nThe Keystone federation flow requires a browser, so is not supported by the Azimuth SDK.\n\nAzimuth also supports authenticating with an\n[application credential](https://docs.openstack.org/keystone/latest/user/application_credentials.html).\nThis authentication method is usually hidden from browser-based users, but is available for use\nby the SDK. **This is the recommended authentication method.**\n\nThe SDK `Configuration` object can be initialised either using known credentials or from a\n[clouds.yaml file](https://docs.openstack.org/python-openstackclient/pike/configuration/index.html).\nThe Azimuth SDK respects the same `OS_CLOUD` and `OS_CLIENT_CONFIG_FILE` environment variables that\nthe [OpenStack CLI](https://docs.openstack.org/python-openstackclient/latest/) respects.\n\n```python\nfrom azimuth_sdk import Configuration\n\n\nAZIMUTH_URL = \"https://portal.azimuth.example.org\"\n\n# Initialise from variables\nconfig = Configuration.create(\n AZIMUTH_URL,\n authenticator = \"appcred\",\n auth_data = {\n \"application_credential_id\": \"<application credential id>\",\n \"application_credential_secret\": \"<application credential secret>\",\n },\n # Optionally set a default tenancy\n default_tenancy_id = \"<tenancy id>\"\n)\n\n# Initialise from a specific clouds.yaml file\nconfig = Configuration.from_openstack_clouds_file(\n AZIMUTH_URL,\n \"/path/to/openstack/clouds.yaml\"\n)\n\n# Initialise from environment variables\nconfig = Configuration.from_environment(AZIMUTH_URL)\n```\n\nOnce you have a `Configuration` object, it can be used to create either synchronous or\nasynchronous clients with which you can interact with Azimuth, e.g. by listing the\navailable tenancies:\n\n```python\n#\u00a0Synchronous client\nwith config.sync_client() as client:\n for tenancy in client.tenancies().list():\n print(tenancy.name)\n\n# Asynchronous client\nasync with config.async_client() as client:\n async for tenancy in client.tenancies().list():\n print(tenancy.name)\n```\n\n> **WARNING**\n>\n> It is important that the client is used inside a `with` or `async with` block, for\n> synchronous or asynchronous clients respectively, as this ensures that resources\n> are set up and released as required.\n>\n> See Python's [contextlib](https://docs.python.org/3/library/contextlib.html) for more information.\n\nYou can then interact with resources for a tenancy. The following resources are available:\n\n```python\n# Interact with the images for a tenancy\nclient.images(tenancy_id = None)\n# Interact with the sizes for a tenancy\nclient.sizes(tenancy_id = None)\n# Interact with the volumes for a tenancy\nclient.volumes(tenancy_id = None)\n# Interact with the external IPs for a tenancy\nclient.external_ips(tenancy_id = None)\n# Interact with the machines for a tenancy\nclient.machines(tenancy_id = None)\n# Interact with the CaaS cluster types for a tenancy\nclient.cluster_types(tenancy_id = None)\n# Interact with the CaaS clusters for a tenancy\nclient.clusters(tenancy_id = None)\n# Interact with the Kubernetes templates for a tenancy\nclient.kubernetes_cluster_templates(tenancy_id = None)\n# Interact with the Kubernetes clusters for a tenancy\nclient.kubernetes_clusters(tenancy_id = None)\n# Interact with the Kubernetes app templates for a tenancy\nclient.kubernetes_app_templates(tenancy_id = None)\n# Interact with the Kubernetes apps for a tenancy\nclient.kubernetes_apps(tenancy_id = None)\n```\n\nFor each of these methods, the `tenancy_id` is optional. If it is not given, a default\ntenancy ID will be used, which is determined as follows:\n\n * Explicitly set when the `Configuration` is created\n * From `clouds.{cloud}.auth.project_id` in the `clouds.yaml`, if present\n * As the first available tenancy in the tenancy list (may not be deterministic)\n\nThe default tenancy for a client can also be changed using the `switch_tenancy` method:\n\n```python\nclient.switch_tenancy(new_tenancy_id)\n```\n\nEach of these returns a `Resource` object, which can be interacted with as follows.\n\n> **NOTE**\n>\n> The sync methods are available on resources produced by synchronous clients\n> (i.e. created using `config.sync_client`), and the\n> async methods on resources produced by asynchronous clients\n> (i.e. created using `config.async_client`).\n\n```python\n# List instances\n# sync\nfor instance in resource.list():\n print(instance)\n# async\nasync for instance in resource.list():\n print(instance)\n\n# Get the first instance from a list response\n# sync\ninstance = resource.first()\n# async\ninstance = await resource.first()\n\n# Fetch an instance by ID\n# sync\ninstance = resource.fetch(\"<id>\")\n# async\ninstance = await resource.fetch(\"<id>\")\n\n# Create a new instance\n# sync\ninstance = resource.create({\"name\": \"my-instance\", \"<prop>\": \"<value>\"})\n# async\ninstance = await resource.create({ \"name\": \"my-instance\", \"<prop>\": \"<value>\"})\n\n# Replace an instance by ID (PUT request)\n#\u00a0 sync\ninstance = resource.replace(\"<id>\", {\"name\": \"my-instance\", \"<prop>\": \"<value>\"})\n#\u00a0 async\ninstance = await resource.replace(\"<id>\", {\"name\": \"my-instance\", \"<prop>\": \"<value>\"})\n\n# Patch an instance by ID (PATCH request)\n# sync\ninstance = resource.patch(\"<id>\", {\"<prop>\": \"<value>\"})\n# async\ninstance = await resource.patch(\"<id>\", {\"<prop>\": \"<value>\"})\n\n# Replace an instance by ID, or create it (using the same data) if it doesn't exist\n# sync\ninstance = resource.create_or_replace(\"<id>\", {\"name\": \"my-instance\", \"<prop>\": \"<value>\"})\n# async\ninstance = await resource.create_or_replace(\"<id>\", {\"name\": \"my-instance\", \"<prop>\": \"<value>\"})\n\n# Patch an instance by ID, or create it (using the same data) if it doesn't exist\n# sync\ninstance = resource.create_or_patch(\"<id>\", {\"<prop>\": \"<value>\"})\n# async\ninstance = await resource.create_or_patch(\"<id>\", {\"<prop>\": \"<value>\"})\n\n# Delete an instance by ID\n# sync\ninstance = resource.delete(\"<id>\")\n# async\ninstance = await resource.delete(\"<id>\")\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "SDK for the Azimuth API",
"version": "0.2.0",
"project_urls": {
"Homepage": "https://github.com/azimuth-cloud/azimuth-sdk"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "801b6c3406ca11e01d42d960344862f048ba023a2952dd67c8da34e62cecbef3",
"md5": "f06aedadd41a62e70eb2328857f55e27",
"sha256": "b2213029a4b6a0eba93107793fff822fb17be473d8cb5d80fb353197d6154cbf"
},
"downloads": -1,
"filename": "azimuth_sdk-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f06aedadd41a62e70eb2328857f55e27",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 12363,
"upload_time": "2024-07-31T12:35:49",
"upload_time_iso_8601": "2024-07-31T12:35:49.378510Z",
"url": "https://files.pythonhosted.org/packages/80/1b/6c3406ca11e01d42d960344862f048ba023a2952dd67c8da34e62cecbef3/azimuth_sdk-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "18081f05533df9626d087a6feee62738c8efc6bbba0db9970cab0d2888d7c337",
"md5": "d2a6cbc23cdd84b52ac4a19da018d7f4",
"sha256": "5756a4c1e84d25a981eda405d409714849890f270b368a45c7d57546e270d4bb"
},
"downloads": -1,
"filename": "azimuth_sdk-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "d2a6cbc23cdd84b52ac4a19da018d7f4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13383,
"upload_time": "2024-07-31T12:35:51",
"upload_time_iso_8601": "2024-07-31T12:35:51.387484Z",
"url": "https://files.pythonhosted.org/packages/18/08/1f05533df9626d087a6feee62738c8efc6bbba0db9970cab0d2888d7c337/azimuth_sdk-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-07-31 12:35:51",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "azimuth-cloud",
"github_project": "azimuth-sdk",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "azimuth-sdk"
}