Air API & Python SDK
This project provides a Python SDK for interacting with the NVIDIA Air API.
SDK Usage
Prerequisite for the SDK
The SDK requires Python 3.7 or later. The safest way to install the SDK is to set up a virtual environment in Python 3.7. For example:
$ apt-get install python3.7
$ python3.7 -m pip install virtualenv
$ python3.7 -m virtualenv venv37
$ . venv37/bin/activate
Installation of the SDK
To install the SDK, use pip:
$ python3 -m pip install air-sdk
Authentication Options
Authentication requires an API token, a username/password, or a bearer token.
API token
The easiest way to generate an API token is with the Air UI.
After a token is generated:
>>> air = AirApi(username='<username>', password='<api_token>')
The API requires the use of the API Token to generate a bearer token:
curl --request POST 'https://air.nvidia.com/api/v1/login/' --form 'username="<user>"' --form 'password="<api_token>"'
The bearer token that is returned is used for authentication going forward:
curl --location --request <http_request>' \
--header 'Authorization: Bearer <bearer_token>'
Other Authentication Options
Username/Password
Username/password authentication is only valid for Air Service Accounts, which can only be created by NVIDIA Air administrators. After the administrator provides the username and password:
>>> air = AirApi(username='<username>', password='<password>')
Bearer token
For SDK use, NVIDIA recommends using an API token over a bearer token. However, a bearer token can be used for testing and performing operations that do not require a long term API token. To use a bearer token, the calling user must have an nvidia.com account and have previously approved access for NVIDIA Air. After you have obtained the token:
>>> air = AirApi(bearer_token='<bearer_token>')
Authenticate using the bearer token and get a list of all simulations:
curl --location --request GET 'https://air.nvidia.com/api/v1/simulation/' \
--header 'Authorization: Bearer <bearer_token>'
Service account
Internal NVIDIA users can use an SSA client ID to authenticate as a service account. First, a valid bearer token must be generated.
curl --user "$CLIENT_ID:$CLIENT_SECRET" --request POST $AIR_TOKEN_URL --header "Content-Type: application/x-www-form-urlencoded" --data-urlencode "grant_type=client_credentials" --data-urlencode "scope=api-access"
{"access_token":"eyJraWQ...","token_type":"bearer","expires_in":900,"scope":"api-access"}
Replace $CLIENT_ID, $CLIENT_SECRET, and $AIR_TOKEN_URL with values generated during your client registration. For more detail, please refer to internal documentation on using service accounts.
Once you have a bearer token, it can be used in the same way as an Air bearer token
Examples
List all Simulations
The SDK provides various helper methods for interacting with the API. The example below shows how to list all simulations with the SDK and with the API using cURL:
>>> air.simulations.list()
[<Simulation sim1 c51b49b6-94a7-4c93-950c-e7fa4883591>, <Simulation sim2 3134711d-015e-49fb-a6ca-68248a8d4aff>]
curl --request GET 'https://air.nvidia.com/api/v1/simulation/' \
--header 'Authorization: Bearer <bearer_token>'
Get a Specific Simulation
To get a simulation with ID c51b49b6-94a7-4c93-950c-e7fa4883591:
>>> sim1 = air.simulations.get('c51b49b6-94a7-4c93-950c-e7fa4883591')
>>> print(sim1.title)
My Sim
curl --request GET 'https://air.nvidia.com/api/v1/simulation/?id=c51b49b6-94a7-4c93-950c-e7fa4883591' \
--header 'Authorization: Bearer <bearer_token>'
Create a Simulation
Create a simulation using a custom topology and custom organization.
Example topology.dot file:
graph "sample_topology" {
"cumulus0" [ memory="1024" os="cumulus-vx-4.4.0" cpu="1"]
"cumulus1" [ memory="1024" os="cumulus-vx-4.4.0" cpu="1"]
"cumulus0":"swp1" -- "cumulus1":"swp1"
"cumulus0":"swp2" -- "cumulus1":"swp2"
}
Create the organization, then create and start the simulation:
Organization creation is currently only supported for NVIDIA users.
>>> from air_sdk import AirApi
>>> user = 'user@nvidia.com'
>>> api_token = 'fake_api_token'
>>> air = AirApi(username=user, password=api_token)
>>> dot_file_path = '/tmp/topology.dot'
>>> org_name = 'My Organization'
>>> org = air.organizations.create(name=org_name, members=[{'username': f'{user}', 'roles': ['Organization Admin']}])
>>> simulation = air.simulations.create(topology_data=dot_file_path, organization=org)
Create the organization:
curl --location --request POST 'https://air.nvidia.com/api/v1/organization/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "My Organization",
"members": [
{
"username": "user@nvidia.com"
}
]
}'
Create and start the simulation:
curl --location --request POST 'https://air.nvidia.com/api/v2/simulation/' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"topology_data": "graph \"My Simulation\" {\n \"cumulus0\" [ memory=\"1024\" os=\"cumulus-vx-5.7.0\" cpu=\"1\" ]\n \"cumulus1\" [ memory=\"1024\" os=\"cumulus-vx-5.7.0\" cpu=\"1\"]\n \"cumulus0\":\"swp1\" -- \"cumulus1\":\"swp1\"\n \"cumulus0\":\"swp2\" -- \"cumulus1\":\"swp2\"\n}\n",
"organization": "<organization_uuid>"
}'
Optionally, a ZTP script can also be included during simulation creation. The script will automatically be hosted by the oob-mgmt-server and fetched by nodes that support ZTP.
>>> ztp_contents = '<ztp_script_content_here>'
>>> simulation = air.simulations.create(topology_data=dot_file_path, ztp_script=ztp_contents)
curl --location --request POST 'https://air.nvidia.com/api/v2/simulation/' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"topology_data": "graph \"My Simulation\" {\n \"cumulus0\" [ memory=\"1024\" os=\"cumulus-vx-5.7.0\" cpu=\"1\" ]\n \"cumulus1\" [ memory=\"1024\" os=\"cumulus-vx-5.7.0\" cpu=\"1\"]\n \"cumulus0\":\"swp1\" -- \"cumulus1\":\"swp1\"\n \"cumulus0\":\"swp2\" -- \"cumulus1\":\"swp2\"\n}\n",
"ztp_script": "<ztp_script_content_here>"
}'
Delete a Simulation
>>> from air_sdk import AirApi
>>> air = AirApi(username='<user>', password='<api_token>')
>>> simulation = air.simulations.get('<simulation_uuid>')
>>> simulation.delete()
Find the simulation:
curl --request GET 'https://air.nvidia.com/api/v1/simulation/?id=<simulation_uuid>' \
--header 'Authorization: Bearer <bearer_token>'
Delete the simulation:
curl --request POST 'https://air.nvidia.com/api/v1/simulation/<simulation_uuid>/control/' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"action": "destroy",
}'
Enable SSH Service
Wake up a sleeping simulation and enable SSH to the oob-mgmt-server
Find and load the simulation:
>>> from air_sdk import AirApi
>>> air = AirApi(username='<user>', password='<api_token>')
>>> simulation = air.simulations.get('<simulation_uuid>')
>>> simulation.load()
Load the simulation:
curl --request POST 'https://air.nvidia.com/api/v1/simulation/<simulation_id>/control/' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"action": "load",
"start": true
}'
Note that the simulation ID will be used again later.
Enable ssh to the oob-mgmt-server’s management port and print the command to ssh to the device:
>>> service_name = 'oob-mgmt-server SSH'
>>> interface = 'oob-mgmt-server:eth0'
>>> dest_port = 22
>>> service = air.services.create(name=service_name, interface=interface, simulation=simulation, dest_port=dest_port)
>>> print(f'ssh -p {service.src_port} {service.os_default_username}@{service.host}')
ssh -p 15738 ubuntu@worker.air.nvidia.com
To enable SSH to a node’s interface, the API requires the specific simulation-interface object’s uuid or resource url. In this example, the simulation_id is 47c91cdd-93d2-42b7-9c94-1580a9e49a88 and the simulation-interface is eth0 on the oob-mgmt-server node.
To find the simulation interface uuid, first find the node object:
curl --request GET 'https://air.nvidia.com/api/v1/node/?name=oob-mgmt-server&simulation=47c91cdd-93d2-42b7-9c94-1580a9e49a88' \
--header 'Authorization: Bearer <bearer_token>'
This will return a list containing one node. The node will have a list of interfaces:
[{
"url": "https://air.nvidia.com/api/v1/node/f2b54dc7-2ec0-40de-b04f-8a2b8655814a/",
"id": "f2b54dc7-2ec0-40de-b04f-8a2b8655814a",
"name": "oob-mgmt-server",
"os": "https://air.nvidia.com/api/v1/image/40000000-0000-0000-8050-000000000001/",
"interfaces": [
{
"url": "https://air.nvidia.com/api/v1/interface/fc92eb67-0abb-4a36-8458-2ecf5cc8ec75/",
"id": "fc92eb67-0abb-4a36-8458-2ecf5cc8ec75", <--------
"name": "eth0",
"mac_address": "04:ca:04:5a:6c:17",
"outbound": true,
"index": 5
}
],
"topology": "https://air.nvidia.com/api/v1/topology/0fbdfdef-c284-4287-85aa-1499fef18a3b/"
}]
Find the interface ID and use it to resolve the simulation-interface:
curl --request GET 'https://air.nvidia.com/api/v1/simulation-interface/?original=fc92eb67-0abb-4a36-8458-2ecf5cc8ec75' \
--header 'Authorization: Bearer <bearer_token>'
[{
"url": "https://air.nvidia.com/api/v1/simulation-interface/bc084dc3-b009-430e-a49a-0699362f955a/",
"id": "bc084dc3-b009-430e-a49a-0699362f955a", <--------
"node": "https://air.nvidia.com/api/v1/simulation-node/fbfe474d-44ba-4ff5-b933-658a33857c96/",
"original": "https://air.nvidia.com/api/v1/interface/fc92eb67-0abb-4a36-8458-2ecf5cc8ec75/",
"link_up": false,
"services": [
"https://air.nvidia.com/api/v1/service/af474fff-4bc9-4590-9f68-0cd3c3b021da/"
],
...
}]
Finally, use the simulation-interface’s ID in the interface param and the simulation ID to create the ssh service:
curl --request POST 'https://air.nvidia.com/api/v1/service/' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "oob-mgmt-server SSH",
"simulation": "47c91cdd-93d2-42b7-9c94-1580a9e49a88",
"interface": "bc084dc3-b009-430e-a49a-0699362f955a",
"dest_port": 22
}'
The response will resemble the following:
"url": "https://air.nvidia.com/api/v1/service/af474fff-4bc9-4590-9f68-0cd3c3b021da/",
"id": "af474fff-4bc9-4590-9f68-0cd3c3b021da",
"name": "oob-mgmt-server SSH",
"simulation": "https://air.nvidia.com/api/v1/simulation/47c91cdd-93d2-42b7-9c94-1580a9e49a88/",
"interface": "https://air.nvidia.com/api/v1/simulation-interface/bc084dc3-b009-430e-a49a-0699362f955a/",
"dest_port": 22,
"src_port": 15502, <--------
"link": "",
"service_type": "other",
"node_name": "oob-mgmt-server",
"interface_name": "eth0",
"host": "worker.air.nvidia.com", <--------
"os_default_username": "ubuntu" <--------
}
The SSH command can be generated using the following template:
ssh -p <src_port> <os_default_username>@<host>
Which produces a command similar to:
ssh -p 15502 ubuntu@worker.air.nvidia.com
Use the oob-mgmt-server as a jump host, and ssh through the oob-mgmt-server to another node in the simulation:
>>> ssh -J ubuntu@worker.air.nvidia.com:15738 user@hostname
Upload an Image and Create a Topology
Upload a custom image and create a topology using that image.
Image upload is currently only supported for NVIDIA users.
Upload and create the image object:
>>> from air_sdk import AirApi
>>> user = 'user@nvidia.com'
>>> api_token = 'fake_api_token'
>>> air = AirApi(username=user, password=api_token)
>>> image_name = 'My Image'
>>> filename = '/Users/user/my_image.qcow2'
>>> # Is the air-agent enabled in the Image by default?
>>> agent_enabled = False
>>> # Should the image be published and accessible to all users?
>>> default_username = 'admin'
>>> default_password = 'admin'
>>> organization = '<organization_uuid>'
>>> version = '1.0.0'
>>> cpu_arch = 'x86'
>>> image = air.images.create(name=image_name, filename=filename, agent_enabled=agent_enabled, default_username=default_username, default_password=default_password, organization=organization, version=version, cpu_arch=cpu_arch)
Create the image object:
curl --request POST 'https://air.nvidia.com/api/v1/image/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "<image_name>",
"base": "false",
"agent_enabled": "false",
"cpu_arch": "x86",
"default_username": "admin",
"organization": "<organization_id>",
"simx": "false",
"provider": "VM",
"version": "1.0.0"
}'
The response will contain an image upload URL:
{
"url": "https://air.nvidia.co,/api/v1/image/3d9a34e6-fd64-47bc-a65d-8a30f9f3ddc7/",
"id": "3d9a34e6-fd64-47bc-a65d-8a30f9f3ddc7",
...
"upload_url": "https://air.nvidia.com/api/v1/image/3d9a34e6-fd64-47bc-a65d-8a30f9f3ddc7/upload/", <--------
...
}
Use the provided upload_url to upload a local image file to Air:
curl --request PUT 'https://air.nvidia.com/api/v1/image/3d9a34e6-fd64-47bc-a65d-8a30f9f3ddc7/upload/' -F filename='@/Users/admin/fake_image.qcow2' \
--header 'Authorization: Bearer <bearer_token>'
Use the image you created in a custom topology:
>>> topology_name = 'My Topology'
>>> node_name = 'server01'
>>> dot_graph = f'graph \"{topology_name}\" {{ \"{node_name}\" [ os=\"{image_name}\"] }}'
>>> simulation = air.simulations.create(topology_data=dot_graph)
curl --request POST 'https://air.nvidia.com/api/v2/simulation/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"topology_data": "graph \"My Topology\" {\n \"cumulus0\" [ memory=\"1024\" os=\"<image_uuid>\" cpu=\"1\" ]\n \"cumulus1\" [ memory=\"1024\" os=\"<image_uuid>\" cpu=\"1\" ]\n \"cumulus0\":\"swp1\" -- \"cumulus1\":\"swp1\"\n \"cumulus0\":\"swp2\" -- \"cumulus1\":\"swp2"\n}\n"
}'
Using Node Instructions
Simulation nodes that have the Air agent installed can be configured via the API using node instructions. The Air agent is present in simulation nodes that use images where the agent_enabled flag is set to true.
After creating (but not starting) a simulation, get the ID of the simulation node that you want to configure:
Find the simulation nodes for the simulation, and get the specific simulation node to be configured. In this example the node is named sample-node:
>>> simnodes = air.simulation_nodes.list(simulation=simulation)
>>> for simnode in simnodes:
>>> if 'sample-node' == simnode.name:
>>> sample_node = simnode
Edit /etc/network/interfaces on the sample-node and apply config with ifreload:
>>> eni_contents = '<string_containing_desired_etc_network_interfaces_content>''
>>> post_cmd = 'ifreload -a'
>>> data = {'/etc/network/interfaces': eni_contents, 'post_cmd': post_cmd}
>>> sample_node.create_instructions(data=json.dumps(data), executor='file')
Edit frr.conf on the sample-node and restart frr:
>>> frr_contents = '<frr_conf_config_here>'
>>> post_cmd = 'systemctl restart frr'
>>> data = {'/etc/frr/frr.conf': frr_contents, 'post_cmd':post_cmd}
>>> sample_node.create_instructions(data=json.dumps(data), executor='file')
To execute a command instead of populating the contents of a file, use the shell
executor instead of file
:
>>> data = 'ip link set swp1 ip address 192.168.100.2/24'
>>> sample_node.create_instructions(data=data, executor='shell')
Add a ZTP script to the node called oob-mgmt-server:
>>> oob_mgmt_server = air.simulation_nodes.list(simulation=simulation, name='oob-mgmt-server')[0]
>>> ztp_contents = '<ztp_script_content_here>'
>>> data = {'/var/www/html/cumulus-ztp': ztp_contents}
>>> oob_mgmt_server.create_instructions(data=json.dumps(data), executor='file')
Finally, start the simulation:
>>> simulation.start()
Find the simulation’s simulation nodes:
curl --request GET 'https://air.nvidia.com/api/v1/simulation-node/?simulation=<simulation_id>' \
--header 'Authorization: Bearer <bearer_token>'
Create and send the node instruction:
curl --request POST 'https://air.nvidia.com/api/v1/simulation-node/<simulation_node_id>/instructions/' \
--header 'Authorization: Bearer <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"executor": "file",
"data": "{'\''/etc/frr/frr.conf'\'': '\''<frr.conf contents>'\'', '\''post_cmd'\'':,'\''systemctl restart frr'\''}"
}'
Alternately, use the shell executor instead of file to run a command:
curl --request POST 'https://air.nvidia.com/api/v1/simulation-node/<simulation_node_id>/instructions/' \
--header 'Authorization: <bearer_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"executor": "shell",
"data": "ip link set swp1 ip address 192.168.100.2/24"
}'
To avoid a race condition on Cumulus Linux nodes running a version prior to 5.0.0, schedule the node instructions prior to starting the simulation. If you do not perform the steps in this order, the instructions might fail to complete.
Using Cloud-init
Cloud-init allows users to configure their nodes upon the first boot. One of its features is the ability to run user scripts, which can be used to perform various configuration tasks. Detailed information and examples of user data and metadata files can be found in the cloud-init documentation.
After creating (but not starting) a simulation, get the specific simulation nodes to be configured. In this example the nodes are named node-1 and node-2:
>>> sim_node_1 = air.simulation_nodes.list(name='node-1', simulation=simulation).pop()
>>> sim_node_2 = air.simulation_nodes.list(name='node-2', simulation=simulation).pop()
Create user data and metadata user configs:
>>> USER_DATA = """#cloud-config
...
... users:
... - default
... - name: custom-user
... passwd: "$6$kW4vfBM9kGgq4hr$TFtHW7.3jOECR9UCBuw9NrdSMJETzSVoNQGcVv2y.RqRUzWDEtYhYRkGvIpB6ml1fh/fZEVIgKbSXI9L1B6xF." # 'possible'
... shell: /bin/bash
... lock-passwd: false
... ssh_pwauth: True
... chpasswd: { expire: False }
... sudo: ALL=(ALL) NOPASSWD:ALL
... groups: users, admin
... """
>>> sim_node_1_metadata = air.user_configs.create(name='sim-node-1-metadata', kind='cloud-init-meta-data', organization=org, content='local-hostname: cloud-init-node-1')
>>> sim_node_2_metadata = air.user_configs.create(name='sim-node-1-metadata', kind='cloud-init-meta-data', organization=org, content='local-hostname: cloud-init-node-2')
>>> sim_node_shared_userdata = air.user_configs.create(name='sim-node-shared-userdata', kind='cloud-init-user-data', organization=org, content=USER_DATA)
Set the cloud-init assignment for the simulation nodes:
>>> sim_node_1.set_cloud_init_assignment({'user_data': sim_node_shared_userdata, 'meta_data': sim_node_1_metadata})
>>> sim_node_2.set_cloud_init_assignment({'user_data': sim_node_shared_userdata, 'meta_data': sim_node_2_metadata})
Finally, start the simulation:
>>> simulation.start()
Adjusting Request Timeouts
By default, the SDK implements the following timeouts for all API requests:
- Establishing a connection to the server (
connect_timeout
): 16 seconds - Receiving a response to a request (
read_timeout
): 61 seconds
These values can be adjusted after instantiating the AirApi
client:
>>> air = AirApi(username='<username>', password='<api_token>')
>>> air.client.default_connect_timeout = 30
>>> air.client.default_read_timeout = 120
Developing
Contributions to the SDK are very welcome. All code must pass linting and unit testing before it will be merged.
Requirements
python3 -m pip install -r requirements-dev.txt
Linting
pylint **/*.py
Unit testing
./unit_test.sh
Generating docs
pydoc-markdown
SDK Reference Guide
Account
Manage an account
json
Returns a JSON string representation of the account
refresh
Syncs the account with all values returned by the API
AccountApi
High-level interface for the Account API
get
Get an existing account
Arguments:
account_id
str - Account IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.accounts.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Account mrobertson@nvidia.com 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing accounts
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.accounts.list()
[<Account mrobertson@nvidia.com c51b49b6-94a7-4c93-950c-e7fa4883591>, <Account nmitchell@nvidia.com 3134711d-015e-49fb-a6ca-68248a8d4aff>]
Preferences
Get account preferences
Returns:
dict
- Response JSON
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.accounts.preferences()
{"baz": true, "foo": false}
Cumulus AIR API module
AirSession
Wrapper around requests.Session
rebuild_auth
Allow credential sharing between nvidia.com and cumulusnetworks.com only
AirApi
Main interface for an API client instance
__init__
Create a new API client instance. The caller MUST provide either username
and password
or a bearer_token
. The password
argument may either be an API token or a service account
password.
Arguments:
username
str, optional - Usernamepassword
str, optional - Password or API tokenbearer_token
str, optional - Pre-generated bearer tokenapi_url
str, optional - Default = https://air.nvidia.com/api/api_version
str - Default = v1
authorize
Authorizes the API client using either a pre-generated API token, a service account
username/password, or a pre-generated bearer token.
Callers MUST pass either a valid bearer_token
or a username
and password
.
The password
argument may either be an API token or a service account
password. After successfully authorizing, all subsequent API calls will include the
authorization token provided by the AIR API. Note: This is called once automatically
when an AirApi object is instantiated.
Arguments:
bearer_token
str, optional - Pre-generated bearer tokenusername
str, optional - Usernamepassword
str, optional - Password or API token
Raises:
ValueError - Caller did not pass either a token or a username/password
get_token
Gets a new bearer token for a given username and password
Arguments:
username
str - Usernamepassword
str - Password
Returns:
str
- Bearer token
Raises:
AirAuthorizationError
- API did not return a tokenJSONDecodeError
- API’s response is not a valid JSON object
get
Wrapper method for GET requests
post
Wrapper method for POST requests
put
Wrapper method for PUT requests
patch
Wrapper method for PATCH requests
delete
Wrapper method for DELETE requests
Capacity
View platform capacity
json
Returns a JSON string representation of the capacity
refresh
Syncs the capacity with all values returned by the API
CapacityApi
High-level interface for the Simulation API
get
Get current platform capacity for a Simulation
Arguments:
simulation_id
str |Simulation
- Simulation or ID
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.capacity.get(simulation)
<Capacity 30>
Demo
View demos
json
Returns a JSON string representation of the demo
refresh
Syncs the demo with all values returned by the API
DemoApi
High-level interface for the Demo API
get
Get an existing demo
Arguments:
dmeo_id
str - Demo IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.demos.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Demo EVPN 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing demos
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.demos.list()
[<Demo EVPN c51b49b6-94a7-4c93-950c-e7fa4883591>, <Demo Challenges 3134711d-015e-49fb-a6ca-68248a8d4aff>]
Custom exceptions for the AIR SDK
AirError
Base exception class. All custom exceptions should inherit from this class.
AirAuthorizationError
Raised when authorization with the API fails.
AirUnexpectedResponse
Raised when the API returns an unexpected response.
AirForbiddenError
Raised when an API call returns a 403 Forbidden error
AirObjectDeleted
Raised when accessing a previously instantiated object that has since been deleted
Image
Manage an image
copy
Copy an image into another organization.
Arguments:
organization
str |Organization
- Organization where the image should be copied
Returns:
Raises:
AirUnexpectedresponse
- Copy failed
delete
Delete the image. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the image
refresh
Syncs the image with all values returned by the API
update
Update the image with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
publish
Publish an image for public use
Arguments:
contact
str - The email address for the contact person associated with this image.
Raises:
AirUnexpectedresponse
- Publish failed
unpublish
Unpublish the image from public use
Raises:
AirUnexpectedresponse
- Unpublish failed
upload
Upload an image file
Arguments:
filename
str - Absolute path to the local image
Raises:
AirUnexpectedresponse
- Upload failed
ImageApi
High-level interface for the Image API
get
Get an existing image
Arguments:
image_id
str - Image IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.images.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Image cumulus-vx-4.2.1 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing images
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.images.list()
[<Image cumulus-vx-4.2.1 c51b49b6-94a7-4c93-950c-e7fa4883591>, <Image generic/ubuntu18.04 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new image
Arguments:
name
str - Image nameorganization
str |Organization
-Organization
or IDfilename
str, optional - Absolute path to the local file which should be uploadedkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> image = air.images.create(name='my_image', filename='/tmp/my_image.qcow2', agent_enabled=False, default_username='user', default_password='password', organization=org, version='1.0.0', cpu_arch='x86')
>>> image
<Image my_image 01298e0c-4ef1-43ec-9675-93160eb29d9f>
>>> image.upload_status
'COMPLETE'
>>> alt_img = air.images.create(name='my_alt_img', filename='/tmp/alt_img.qcow2', agent_enabled=False, default_username='user', default_password='password', organization=org, version='1.0.0', cpu_arch='x86')
>>> alt_img.upload_status
'FAILED'
Interface
View an interface
json
Returns a JSON string representation of the interface
refresh
Syncs the interface with all values returned by the API
InterfaceApi
High-level interface for the Interface API
get
Get an existing interface
Arguments:
interface_id
str - Interface IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.interfaces.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Interface eth0 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing interfaces
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.interfaces.list()
[<Interface eth0 c51b49b6-94a7-4c93-950c-e7fa4883591>, <Interface eth1 3134711d-015e-49fb-a6ca-68248a8d4aff>]
SimulationInterface module
SimulationInterface
Manage a simulation interface
json
Returns a JSON string representation of the simulation interface
refresh
Syncs the simulation interface with all values returned by the API
update
Update the simulation interface with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
SimulationInterfaceApi
High-level interface for the SimulationInterface API
get
Get an existing simulation interface
Arguments:
simulation_interface_id
str - SimulationInterface IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_interfaces.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<SimulationInterface 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing simulation interfaces
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_interfaces.list()
[<SimulationInterface c51b49b6-94a7-4c93-950c-e7fa4883591>, <SimulationInterface 3134711d-015e-49fb-a6ca-68248a8d4aff>]
Job
Manage a Job
json
Returns a JSON string representation of the job
refresh
Syncs the job with all values returned by the API
update
Update the job with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
JobApi
High-level interface for the Job API
get
Get an existing job
Arguments:
job_id
str - Job IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.jobs.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Job START 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing jobs
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.jobs.list()
[<Job START c51b49b6-94a7-4c93-950c-e7fa4883591>, <Job STOP 3134711d-015e-49fb-a6ca-68248a8d4aff>]
Link
Manage a link
delete
Delete the link. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the link
refresh
Syncs the link with all values returned by the API
update
Update the link with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
LinkApi
High-level interface for the Link API
get
Get an existing link
Arguments:
link_id
str - Link IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.links.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Link 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing links
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.links.list()
[<Link c51b49b6-94a7-4c93-950c-e7fa4883591>, <Link 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new link
Arguments:
topology
str |Topology
-Topology
or IDinterfaces
list - List ofInterface
objects or IDskwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.links.create(topology=topology, interfaces=[intf1, 'fd61e3d8-af2f-4735-8b1d-356ee6bf4abe'])
<Link 01298e0c-4ef1-43ec-9675-93160eb29d9f>
Login
View login information
json
Returns a JSON string representation of the login info
refresh
Syncs the login info with all values returned by the API
LoginApi
High-level interface for the Login API
get
Get login information or start an OAuth request. This is equivalent to login.list()
.
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.login.get()
<Login>
list
Get login information or start an OAuth request. This is equivalent to login.get()
.
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.login.get()
<Login>
Marketplace
View marketplace demos
json
Returns a JSON string representation of the marketplace demo
refresh
Syncs the marketplace demo with all values returned by the API
MarketplaceApi
High-level interface for the Marketplace API
get
Get an existing Marketplace Demo
Arguments:
demo_id
str - Marketplace Demo IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.Marketplace.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Marketplace Demo EVPN 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing Marketplace demos
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.marketplace.list()
[<Marketplace Demo EVPN c51b49b6-94a7-4c93-950c-e7fa4883591>, <Marketplace Demo Challenges 3134711d-015e-49fb-a6ca-68248a8d4aff>]
Node
Manage a node
delete
Delete the node. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the node
refresh
Syncs the node with all values returned by the API
update
Update the node with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
NodeApi
High-level interface for the Node API
get
Get an existing node
Arguments:
node_id
str - Node IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.nodes.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Node server 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing nodes
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.nodes.list()
[<Node server c51b49b6-94a7-4c93-950c-e7fa4883591>, <Node switch 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new node
Arguments:
name
str - Node nametopology
str |Topology
-Topology
or IDkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.nodes.create(name='server', topology=topology)
<Node server 01298e0c-4ef1-43ec-9675-93160eb29d9f>
SimulationNode
Manage a SimulationNode
json
Returns a JSON string representation of the simulation node
refresh
Syncs the simulation node with all values returned by the API
update
Update the simulation node with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
create_instructions
Create instructions for the SimulationNode
’s agent to execute
Arguments:
data
str | list - Instruction dataexecutor
str - Agent executor typekwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
dict
- Response JSON
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> simulation_node.create_instructions(data='echo foo', executor='shell')
{'id': '67f73552-ffdf-4e5f-9881-aeae227604a3'}
list_instructions
List all instructions for a SimulationNode
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> simulation_node.list_instructions()
[{'id': '56abc69b-489f-429a-aed9-600f26afc956'}, {'id': '7c9c3449-f071-4bbc-bb42-bef04e44d74e'}]
delete_instructions
Delete all instructions for a SimulationNode
Raises:
AirUnexpectedresponse
- Instruction delete failed
Example:
>>> simulation_node.delete_instructions()
set_cloud_init_assignment
Set assignment of cloud-init scripts for specific node
Arguments:
user_data
str - UserConfig ID. UserConfig must be of kind ‘cloud-init-user-data’meta_data
str - UserConfig ID. UserConfig must be of kind ‘cloud-init-meta-data’
Returns:
dict
- Response JSON
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> simulation_node.set_cloud_init_assignment({'user_data': '09b168b8-f9dd-408c-b3c6-bbecf6a43a09', 'meta_data': 'a19a8715-b46b-4599-81e6-379adebe4bb4'})
{'simulation_node': 'a44894a4-d805-42e2-a07f-7fd5fb0ea154', 'user_data': '09b168b8-f9dd-408c-b3c6-bbecf6a43a09', 'meta_data': 'a19a8715-b46b-4599-81e6-379adebe4bb4', 'user_data_name': 'userdata1', 'meta_data_name': 'metadata1'}
>>> simulation_node.set_cloud_init_assignment({'user_data': userdata, 'meta_data': metadata})
{'simulation_node': 'a44894a4-d805-42e2-a07f-7fd5fb0ea154', 'user_data': '09b168b8-f9dd-408c-b3c6-bbecf6a43a09', 'meta_data': 'a19a8715-b46b-4599-81e6-379adebe4bb4', 'user_data_name': 'userdata1', 'meta_data_name': 'metadata1'}
>>> simulation_node.set_cloud_init_assignment({'user_data': userdata, 'meta_data': None})
{'simulation_node': 'a44894a4-d805-42e2-a07f-7fd5fb0ea154', 'user_data': '09b168b8-f9dd-408c-b3c6-bbecf6a43a09', 'meta_data': None, 'user_data_name': 'userdata1', 'meta_data_name': None}
>>> sim.start()
get_cloud_init_assignment
Get cloud-init assignment for a specific node
Returns:
dict
- Response JSON
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> simulation_node.get_cloud_init_assignment()
{'simulation_node': 'a44894a4-d805-42e2-a07f-7fd5fb0ea154', 'user_data': 09b168b8-f9dd-408c-b3c6-bbecf6a43a09', 'meta_data': 'a19a8715-b46b-4599-81e6-379adebe4bb4', 'user_data_name': 'userdata1', 'meta_data_name': 'metadata1'}
control
Sends a control command to the SimulationNode
.
Arguments:
action
str - Control commandkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
dict
- Response JSON
Example:
>>> simulation_node.control(action='reset')
{'result': 'success'}
rebuild
Rebuild the SimulationNode
back to its initial state. All existing data will be lost.
reset
Reset the SimulationNode
SimulationNodeApi
Wrapper for the SimulationNode API
get
Get an existing simulation node
Arguments:
simulation_node_id
str - SimulationNode IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_nodes.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<SimulationNode my_sim 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing simulation nodes
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_nodes.list()
[<SimulationNode sim1 c51b49b6-94a7-4c93-950c-e7fa4883591>, <SimulationNode sim2 3134711d-015e-49fb-a6ca-68248a8d4aff>]
Organization
Manage an organization
delete
Delete the organization. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the organization
refresh
Syncs the organization with all values returned by the API
update
Update the organization with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
add_member
Add a new member to the organization
Arguments:
username
str - The email address of the user to addroles
list, optional - A list of roles to assign the user. Valid values are ‘Organization Admin’ or ‘Organization Member’. If no roles list is provided, ‘Organization Member’ is used as the default role.
Example:
>>> organization.add_member('user1@nvidia.com')
>>> organization.add_member('user2@nvidia.com', roles=['Organization Admin'])
add_members
Add new members to the organization
Arguments:
members
list - List of organization membership dicts in the format of {‘username’: <email_address>, ‘roles’: []}. ‘roles’ is optional and defaults to [‘Organization Member’] can be a value of ‘Organization Admin’ or ‘Organization Member’.
Example:
>>> organization.add_members([{'username': 'user1@nvidia.com', 'roles': ['Organization Admin']}, {'username': 'user2@nvidia.com'}])
remove_member
Remove a member from the organization
Arguments:
username
str - The email address of the user to remove
Example:
>>> organization.remove_member('user1@nvidia.com')
remove_members
Remove multiple members from the organization
Arguments:
members
list - Email addresses of the users to remove
Example:
>>> organization.remove_members(['user1@nvidia.com', 'user2@nvidia.com'])
OrganizationApi
High-level interface for the Organization API
get
Get an existing organization
Arguments:
organization_id
str - Organization IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.organizations.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Organization NVIDIA 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing organizations
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.organizations.list()
[<Organization NVIDIA c51b49b6-94a7-4c93-950c-e7fa4883591>, <Organization Customer 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new organization
Arguments:
name
str - Organization namemembers
list, optional - List of organization membership dicts in the format of {‘username’: <email_address>, ‘roles’: []}. ‘roles’ is optional and defaults to [‘Organization Member’] can be a value of ‘Organization Admin’ or ‘Organization Member’. If no member list is provided, the calling user’s account will be set as the organization admin by default. kwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.organizations.create(name='NVIDIA', members=[{'username': 'user1@nvidia.com', 'roles': ['Organization Admin']}, {'username': 'user2@nvidia.com'}])
<Organization NVIDIA 01298e0c-4ef1-43ec-9675-93160eb29d9f>
Permission
Manage a permission
delete
Delete the permission. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the permission
refresh
Syncs the permission with all values returned by the API
update
Update the permission with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
PermissionApi
High-level interface for the Permission API
create
Create a new permission. The caller MUST provide simulation
, topology
, or subject_id
Arguments:
email
str - Email address for the user being granted permissionsimulation
str |Simulation
, optional -Simulation
or IDtopology
str |Topology
, optional -Topology
or IDsubject_id
str |AirModel
, optional -AirModel
instance or IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.permissions.create(email='mrobertson@nvidia.com', topology=topology, write_ok=True)
<Permission 01298e0c-4ef1-43ec-9675-93160eb29d9f>
>>> air.permissions.create(email='mrobertson@nvidia.com',
... subject_id='80cf922a-7b80-4795-8cc5-550833ab1cec', subject_model='simulation.image')
<Permission 8a09ea66-51f9-4ddd-8416-62c266cd959e>
get
Get an existing permission
Arguments:
permission_id
str - Permission IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.permissions.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Permission 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing permissions
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.permissions.list()
[<Permission c51b49b6-94a7-4c93-950c-e7fa4883591>, <Permission 3134711d-015e-49fb-a6ca-68248a8d4aff>]
ResourceBudget
Manage a ResourceBudget
json
Returns a JSON string representation of the budget
refresh
Syncs the budget with all values returned by the API
update
Update the budget with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
ResourceBudgetApi
High-level interface for the ResourceBudget API
get
Get an existing budget
Arguments:
budget_id
str - ResourceBudget IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.resource_budgets.get('c604c262-396a-48a0-a8f6-31708c0cff82')
<ResourceBudget c604c262-396a-48a0-a8f6-31708c0cff82>
list
List existing budgets
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.resource_budgets.list()
[<ResourceBudget c604c262-396a-48a0-a8f6-31708c0cff82>, <ResourceBudget 906675f7-8b8d-4f52-b59d-52847af2f0ef>]
Service
Manage a service
delete
Delete the service. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the service
refresh
Syncs the service with all values returned by the API
update
Update the service with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
ServiceApi
High-level interface for the Service API
get
Get an existing service
Arguments:
service_id
str - Service IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.services.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Service SSH 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing services
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.services.list()
[<Service SSH c51b49b6-94a7-4c93-950c-e7fa4883591>, <Service HTTP 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new service
Arguments:
name
str - Service nameinterface
str |SimulationInterface
- Interface that the service should be created for. This can be provided in one of the following formats:SimulationInterface
object- ID of a
SimulationInterface
- String in the format of ‘node_name:interface_name’
simulation
str |Simulation
-Simulation
or IDkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.services.create(name='myservice', interface='oob-mgmt-server:eth0', dest_port=22)
<Service myservice cc18d746-4cf0-4dd3-80c0-e7df68bbb782>
>>> air.services.create(name='myservice', interface=simulation_interface, dest_port=22)
<Service myservice 9603d0d5-5526-4a0f-91b8-a600010d0091>
Simulation
Manage a simulation
json
Returns a JSON string representation of the simulation
preferences
Returns a JSON string representation of the simulation preferences
refresh
Syncs the simulation with all values returned by the API
update
Update the simulation with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
create_service
Create a new service for this simulation
Arguments:
name
str - Name of the serviceinterface
str |SimulationInterface
- Interface that the service should be created for. This can be provided in one of the following formats:SimulationInterface
object- ID of a
SimulationInterface
- String in the format of ‘node_name:interface_name’
dest_port
int - Service port numberkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Example:
>>> simulation.create_service('myservice', 'oob-mgmt-server:eth0', 22, service_type='ssh')
<Service myservice cc18d746-4cf0-4dd3-80c0-e7df68bbb782>
>>> simulation.create_service('myservice', simulation_interface, 22, service_type='ssh')
<Service myservice 9603d0d5-5526-4a0f-91b8-a600010d0091>
add_permission
Adds permission for a given user to this simulation.
Arguments:
email
str - Email address of the user being given permissionkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Example:
>>> simulation.add_permission('mrobertson@nvidia.com', write_ok=True)
<Permission 217bea68-7048-4262-9bbc-b98ab16c603e>
control
Sends a control command to the simulation.
Arguments:
action
str - Control commandkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
dict
- Response JSON
Example:
>>> simulation.control(action='destroy')
{'result': 'success'}
load
Alias for start()
start
Start/load the simulation
stop
Alias for store()
store
Store and power off the simulation
delete
Delete the simulation
SimulationApi
High-level interface for the Simulation API
duplicate
Duplicate/clone an existing simulation
Arguments:
simulation
str |Simulation
- Simulation or ID of the snapshot to be duplicatedkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
(Simulation
, dict): Newly created simulation and response JSON
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulations.duplicate(simulation=simulation)
<Simulation my_sim 5ff3f0dc-7db8-4938-8257-765c8e48623a>
get_citc_simulation
Get the active CITC reference simulation
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulations.get_citc_simulation()
<Simulation my_sim b9125419-7c6e-41db-bba9-7d647d63943e>
get
Get an existing simulation
Arguments:
simulation_id
str - Simulation IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulations.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Simulation my_sim 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing simulations
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulations.list()
[<Simulation sim1 c51b49b6-94a7-4c93-950c-e7fa4883591>, <Simulation sim2 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new simulation. The caller must provide either topology
or topology_data
.
Arguments:
topology
str |Topology
, optional -Topology
or IDtopology_data
str | fd, optional - Topology in DOT format. This can be passed as a string containing the raw DOT data, a path to the DOT file on your local disk, or as a file descriptor for a local filekwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulations.create(topology=topology, title='my_sim')
<Simulation my_sim 01298e0c-4ef1-43ec-9675-93160eb29d9f>
>>> air.simulations.create(topology_data='/tmp/my_net.dot', organization=my_org)
<Simulation my_sim c0a4c018-0b85-4439-979d-9814166aaeac>
>>> air.simulations.create(topology_data='graph "my_sim" { "server1" [ function="server" os="generic/ubuntu2204"] }', organization=my_org)
<Simulation my_sim b9c0c68e-d4bd-4e9e-8a49-9faf41efaf70>
>>> air.simulations.create(topology_data=open('/tmp/my_net.dot', 'r', encoding='utf-8')), organization=my_org)
<Simulation my_sim 86162934-baa7-4d9a-a826-5863f92b03ef>
SimulationInterface
Manage a simulation interface
json
Returns a JSON string representation of the simulation interface
refresh
Syncs the simulation interface with all values returned by the API
update
Update the simulation interface with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
SimulationInterfaceApi
High-level interface for the SimulationInterface API
get
Get an existing simulation interface
Arguments:
simulation_interface_id
str - SimulationInterface IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_interfaces.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<SimulationInterface 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing simulation interfaces
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_interfaces.list()
[<SimulationInterface c51b49b6-94a7-4c93-950c-e7fa4883591>, <SimulationInterface 3134711d-015e-49fb-a6ca-68248a8d4aff>]
SimulationNode
Manage a simulation node
json
Returns a JSON string representation of the simulation node
refresh
Syncs the simulation node with all values returned by the API
update
Update the simulation node with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
create_instructions
Create instructions for the SimulationNode
’s agent to execute
Arguments:
data
str | list - Instruction dataexecutor
str - Agent executor typekwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
dict
- Response JSON
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> simulation_node.create_instructions(data='echo foo', executor='shell')
{'id': '67f73552-ffdf-4e5f-9881-aeae227604a3'}
list_instructions
List all instructions for a SimulationNode
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> simulation_node.instructions.list()
[{'id': '56abc69b-489f-429a-aed9-600f26afc956'}, {'id': '7c9c3449-f071-4bbc-bb42-bef04e44d74e'}]
delete_instructions
Delete all instructions for a SimulationNode
Raises:
AirUnexpectedresponse
- Instruction delete failed
Example:
>>> simulation_node.instructions.delete()
control
Sends a control command to the SimulationNode
.
Arguments:
action
str - Control commandkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
dict
- Response JSON
Example:
>>> simulation_node.control(action='reset')
{'result': 'success'}
rebuild
Rebuild the SimulationNode
back to its initial state. All existing data will be lost.
reset
Reset the SimulationNode
SimulationNodeApi
Wrapper for the SimulationNode API
get
Get an existing simulation node
Arguments:
simulation_node_id
str - SimulationNode IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_nodes.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<SimulationNode my_sim 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing simulation nodes
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.simulation_nodes.list()
[<SimulationNode sim1 c51b49b6-94a7-4c93-950c-e7fa4883591>, <SimulationNode sim2 3134711d-015e-49fb-a6ca-68248a8d4aff>]
SSHKey
Manage an SSH key
delete
Delete the key. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the key
refresh
Syncs the key with all values returned by the API
SSHKeyApi
High-level interface for the SSHKey API
list
List existing keys
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.ssh_keys.list()
[<SSHKey mykey c51b49b6-94a7-4c93-950c-e7fa4883591>, <SSHKey test_key 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Add a new public key to your account
Arguments:
name
str - Descriptive name for the public keypublic_key
str - Public keykwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.ssh_keys.create(name='my_pub_key', public_key='<key_string>')
<SSHKey my_pub_key 01298e0c-4ef1-43ec-9675-93160eb29d9f>
Token
Manage an API token
TokenApi
High-level interface for the Token API
list
List existing tokens
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedResponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.api_tokens.list()
[<Token mytoken c51b49b6-94a7-4c93-950c-e7fa4883591>, <Token test_token 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Add a new api token to your account
Arguments:
name
str - Descriptive name for the public keykwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedResponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.api_tokens.create(name='my_api_token')
<Token my_api_token 01298e0c-4ef1-43ec-9675-93160eb29d9f>
delete
Delete the token
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
string
Raises:
AirUnexpectedresponse
- Received an unexpected response
from the Cumulus AIR API (404): {“detail”:“Not found."}
Example:
>>> air.api_tokens.delete()
'SUCCESS'
Topology
Manage a topology
delete
Delete the topology. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the topology
refresh
Syncs the topology with all values returned by the API
update
Update the topology with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
add_permission
Adds permission for a given user to this topology.
Arguments:
email
str - Email address of the user being given permissionkwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Example:
>>> topology.add_permission('mrobertson@nvidia.com', write_ok=True)
<Permission 217bea68-7048-4262-9bbc-b98ab16c603e>
TopologyApi
High-level interface for the Topology API
get
Get an existing topology
Arguments:
topology_id
str - Topology IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.topologies.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Topology my_network 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing topologies
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.topologies.list()
[<Topology my_network1 c51b49b6-94a7-4c93-950c-e7fa4883591>, <Topology my_network2 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new topology. The caller must provide either dot
(recommended) or json
.
Arguments:
dot
str | fd, optional - Topology in DOT format. This can be passed as a string containing the raw DOT data, a path to the DOT file on your local disk, or as a file descriptor for a local filejson
dict, optional - Topology in JSON format
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.topologies.create(dot='/tmp/my_net.dot')
<Topology my_net 01298e0c-4ef1-43ec-9675-93160eb29d9f>
>>> air.topologies.create(dot='graph "my sim" { "server1" [ function="server" os="generic/ubuntu1804"] }')
<Topology my_net 6256baa8-f54b-4190-85c8-1cc574590080>
>>> air.topologies.create(dot=open('/tmp/my_net.dot', 'r'))
<Topology my_net a3d09f12-56ff-4889-8e03-3b714d32c3e5>
Worker
Manage a worker
json
Returns a JSON string representation of the worker
refresh
Syncs the worker with all values returned by the API
update
Update the worker with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
set_available
Sets a worker’s available
value in AIR
Arguments:
available (bool)
WorkerApi
High-level interface for the Worker API
get
Get an existing worker
Arguments:
worker_id
str - Worker IDkwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.workers.get('3dadd54d-583c-432e-9383-a2b0b1d7f551')
<Worker worker01 3dadd54d-583c-432e-9383-a2b0b1d7f551>
list
List existing workers
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.workers.list()
[<Worker worker01 c51b49b6-94a7-4c93-950c-e7fa4883591>, <Worker worker02 3134711d-015e-49fb-a6ca-68248a8d4aff>]
create
Create a new worker
Arguments:
cpu
int - Number of vCPUs the worker can supportmemory
int - Amount of memory (in MB) a worker can supportstorage
int - Amount of storage (in GB) a worker can supportip_address
str - Internal IP addressport_range
str - Range of ports available on the workerusername
str - Worker username for API accesspassword
str - Worker password for API accesskwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.workers.create(cpu=100, memory=200000, storage=1000, ip_address='10.1.1.1', port_range='10000-30000', username='worker01', password='secret')
<Worker my_sim 01298e0c-4ef1-43ec-9675-93160eb29d9f>
UserConfig
Manage a UserConfig
delete
Delete the UserConfig. Once successful, the object should no longer be used and will raise
AirDeletedObject
when referenced.
Raises:
AirUnexpectedresponse
- Delete failed
json
Returns a JSON string representation of the UserConfig.
refresh
Syncs the UserConfig with all values returned by the API
update
Update the UserConfig with the provided data
Arguments:
kwargs
dict, optional - All optional keyword arguments are applied as key/value pairs in the request’s JSON payload
UserConfigApi
High-level interface for the UserConfig API
list
List existing UserConfig scripts
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
list
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.user_configs.list()
[<UserConfig userdata1 cloud-init-user-data 84ddf2da-7a09-4a3b-a2b9-09179f4668c8>]
create
Create a new UserConfig
Arguments:
name
str - UserConfig namekind
str - Must be ‘cloud-init-user-data’ or ‘cloud-init-meta-data’organization
str - Organization IDcontent
str - Plaintext content of the data-script or a path to an existing file or an open file handle
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> userdata = air.user_configs.create(name='userdata1', kind='cloud-init-user-data', organization=None, content='/tmp/userConfig-ex1.YML')
<UserConfig userdata1 cloud-init-user-data 09b168b8-f9dd-408c-b3c6-bbecf6a43a09>
>>> metadata = air.user_configs.create(name='metadata1', kind='cloud-init-meta-data', organization='3dadd54d-583c-432e-9383-a2b0b1d7f551', content='local-hostname: cloud-init-node')
<UserConfig metadata1 cloud-init-meta-data a19a8715-b46b-4599-81e6-379adebe4bb4>
get
Get an existing UserConfig
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as query parameters/filters
Returns:
Raises:
AirUnexpectedresponse
- API did not return a 200 OK
or valid response JSON
Example:
>>> air.user_configs.get(id='09b168b8-f9dd-408c-b3c6-bbecf6a43a09')
<UserConfig userdata1 cloud-init-user-data 09b168b8-f9dd-408c-b3c6-bbecf6a43a09>
update
Update specific properties of a UserConfig
Arguments:
kwargs
dict, optional - All other optional keyword arguments are applied as key/value pairs in the request’s JSON payload. Kind and Organization fields cannot be changed.
delete
Delete existing UserConfig script
Raises:
AirUnexpectedresponse
- Userconfig delete failed
Example:
>>> metadata = air.user_configs.create(name='metadata', kind='cloud-init-meta-data', organization=None, content='local-hostname: cloud-init-node')
>>> metadata.delete()