-
Notifications
You must be signed in to change notification settings - Fork 35
4. Sastre SDK
The Cisco SD-WAN Python SDK provides different layers of abstraction on top of vManage REST API:
- Rest object: Provides vManage session login, logout and executor (get, post, put, delete) functions with enhanced error handling and multi-tenant support.
- vManage models: Higher level models (python classes) that abstract Rest details such as API endpoint path, input parameters, etc.
- Sastre tasks: Programmatically executing any of Sastre tasks, with strong parameter validation via pydantic.
The following sections explore those options in more detail with commented code examples.
The lower level vManage API abstraction is provided by the Rest class in the cisco_sdwan.base.rest_api module.
from cisco_sdwan.base.rest_api import Rest
with Rest(base_url='https://198.18.1.10', username='admin', password='C1sco12345') as api:
print(api.server_version)
Code highlights:
- As execution enters the
with
block, vManage login is performed. Similarly, vManage logout is done on exit (of the with block). - If vManage is configured to listen on a non-default port, that can be provided in the
base_url
parameter. For instance, if vManage is on port 8443 one would usebase_url='https://198.18.1.10:8443'
. - Rest also accepts setting of a timeout value (default is 20s). In multi-tenant environments,
tenant_name
can be provided to select which tenant to login as, in case a tenant admin account is provided. - Inside the
with
block, theapi
(Rest) object is used to execute the basic REST methods: get, post, put, delete. - The following object properties are also available:
server_version
,is_multi_tenant
andis_provider
.
The following exceptions can be raised by Rest()
and its methods: RestAPIException()
, LoginFailedException()
or BadTenantException()
.
Consider a use-case to retrieve information about a device's control connections. This information is available via vManage realtime REST API; more specifically, a GET request to the '/device/control/connections' endpoint, with a deviceId
parameter set to the system IP from the device of interest.
Using the SDK low level APIs, this would translate to the following code:
import json
from cisco_sdwan.base.rest_api import Rest
def print_json(j_obj):
print(json.dumps(j_obj, indent=2))
with Rest(base_url='https://198.18.1.10', username='admin', password='C1sco12345') as api:
reply = api.get('device/control/connections', deviceId='10.3.0.1')
print_json(reply)
Code highlights:
- As before, inside the
with
block we have an authenticated session to vManage, available via theapi
object. - The
api
get method is used to perform a REST GET call to vManage. All positional parameters are joined to define the URL. Keyword arguments are used to define URL parameters. - Considering the above,
api.get('device/control/connections', deviceId='10.3.0.1')
translates into the following REST call: GET 'https://198.18.1.10/dataservice/device/control/connections?deviceId=10.3.0.1' - The same result would be obtained with
api.get('device', 'control', 'connections', deviceId='10.3.0.1')
. Also, leading and trailing '/'s are stripped and not significant. - The get method returns the JSON object that is returned by vManage, which contains the information about 10.3.0.1's control connections.
- Function
print_json
is included just to make it easier to display the JSON object.
The next layer of abstraction is provided by vManage models from the cisco_sdwan.base.models_vmanage
module.
The following code achieves a similar result as the previous example but leveraging the DeviceControlConnections
class:
from cisco_sdwan.base.rest_api import Rest
from cisco_sdwan.base.models_vmanage import DeviceControlConnections
with Rest(base_url='https://198.18.1.10', username='admin', password='C1sco12345') as api:
control_connections = DeviceControlConnections.get(api, deviceId='10.3.0.1')
print(control_connections)
Code highlights:
-
DeviceControlConnections.get
returns an instance ofDeviceControlConnections
, populated with the values retrieved from vManage. - Printing
DeviceControlConnections
will display the JSON data payload. This is equivalent toprint_json(reply['data'])
in the previous example.
So far you are probably not seeing much benefit when compared to using the Rest object. However, DeviceControlConnections
is an OperationalItem
, which can be iterated over as a table-like structure by using the following properties and methods:
- Method
field_info
: Returns metadata about one or more columns of the table. By default, that is the column title. Optionally, the keyword parameterinfo
can be used to select a different type of metadata, such asinfo='dataType'
, which would return the column data types. - Method
field_value_iter
: Iterate over the different rows of the table. - Property
field_names
: A tuple containing all available columns, or field names. These can be used to discover which columns to select and use as parameters forfield_info
andfield_value_iter
.
from cisco_sdwan.base.rest_api import Rest
from cisco_sdwan.base.models_vmanage import DeviceControlConnections
with Rest(base_url='https://198.18.1.10', username='admin', password='C1sco12345') as api:
control_connections = DeviceControlConnections.get(api, deviceId='10.3.0.1')
header = control_connections.field_info('system_ip', 'local_color', 'remote_color', 'state', 'uptime_date')
print(f"{header[0]:14} {header[1]:12} {header[2]:12} {header[3]:5} {header[4]}")
for row in control_connections.field_value_iter('system_ip', 'local_color', 'remote_color', 'state', 'uptime_date'):
print(f"{row.system_ip:14} {row.local_color:12} {row.remote_color:12} {row.state:5} {row.uptime_date}")
Code highlights:
- Method
field_info
accepts field names as positional arguments. The list of available field names can be obtained fromcontrol_connections.field_names
. Field_info returns a tuple where each entry corresponds to the title for the corresponding field name (or column name). - Similarly to
field_info
,field_value_iter
accepts field names as positional arguments. It returns an iterator of rows. Each row is a named tuple.
The following shows an example of the output from code snippet above:
Peer System IP Local Color Remote Color State Up Since
12.12.12.12 mpls default up 1646081880000
12.12.12.12 biz-internet default up 1646081880000
22.22.22.22 mpls default up 1646081880000
22.22.22.22 biz-internet default up 1646081880000
10.10.10.10 mpls default up 1646318580000
Finally, method field_value_iter
also accepts conversion functions as keyword arguments. For instance, uptime_date
can be converted from timestamp to a more user-friendly format.
This is illustrated in the updated version of our code below, which includes datetime_format
as a function to convert uptime_date
to the "%Y-%m-%d %H:%M:%S %Z" format.
from datetime import datetime, timezone
from cisco_sdwan.base.rest_api import Rest
from cisco_sdwan.base.models_vmanage import DeviceControlConnections
def datetime_format(timestamp):
if timestamp is None:
return ""
dt = datetime.fromtimestamp(float(timestamp) / 1000, tz=timezone.utc)
return f"{dt:%Y-%m-%d %H:%M:%S %Z}"
with Rest(base_url='https://198.18.1.10', username='admin', password='C1sco12345') as api:
control_connections = DeviceControlConnections.get(api, deviceId='10.3.0.1')
header = control_connections.field_info('system_ip', 'local_color', 'remote_color', 'state', 'uptime_date')
print(f"{header[0]:14} {header[1]:12} {header[2]:12} {header[3]:5} {header[4]}")
for row in control_connections.field_value_iter('system_ip', 'local_color', 'remote_color', 'state', 'uptime_date', uptime_date=datetime_format):
print(f"{row.system_ip:14} {row.local_color:12} {row.remote_color:12} {row.state:5} {row.uptime_date}")
This change would provide the following output:
Peer System IP Local Color Remote Color State Up Since
12.12.12.12 mpls default up 2022-03-03 16:22:00 UTC
12.12.12.12 biz-internet default up 2022-03-03 16:22:00 UTC
22.22.22.22 mpls default up 2022-03-03 16:24:00 UTC
22.22.22.22 biz-internet default up 2022-03-03 16:24:00 UTC
10.10.10.10 mpls default up 2022-03-03 14:43:00 UTC
Moving further up the stack of vManage REST API abstraction layers, we can programmatically call full Sastre tasks. Tasks encapsulate workflows which typically include interactions with multiple vManage models.
The following example shows how to programmatically call a backup task. This task retrieves all known vManage configuration models (or a selected subset), saving as JSON files in the local filesystem.
import logging
from cisco_sdwan.base.rest_api import Rest
from cisco_sdwan.tasks.implementation import TaskBackup, BackupArgs
# Setup logging to visualize progress
logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")
# Equivalent to 'sdwan backup all --no-rollover --save-running'
task_args = BackupArgs(
save_running = True,
no_rollover = True,
workdir = 'backup_test',
tags = ['all']
)
with Rest(base_url='https://198.18.1.10', username='admin', password='C1sco12345') as api:
task = TaskBackup()
task_output = task.runner(task_args, api)
if task_output:
print('\n\n'.join(str(entry) for entry in task_output))
task.log_info(f'Task completed {task.outcome("successfully", "with caveats: {tally}")}')
Code highlights:
- Tasks have loggers, which can be integrated into your application logging configuration.
- Each task has a corresponding pydantic model that is used to validate its input parameters.
BackupArgs
capture arguments for the backup task.ValidationError
exception is raised on validation failures. - Task execution happens on
task.runner
- Some tasks may have an output (intended to be presented to the user). That is not the case for the backup task, just showcasing the standard procedure to deal with any output that may be returned by the task.
Since logging is configured to output informational-level logging to stdout, running the example code generates the following output:
[INFO] Backup task: vManage URL: "https://198.18.1.10" -> Local workdir: "backup_test"
[INFO] Saved vManage server information
[INFO] Done CFS device configuration vManage
[INFO] Done RFS device configuration vManage
[INFO] Done CFS device configuration vSmart-1
<snip>
[INFO] Saved prefix list index
[INFO] Done prefix list DefaultRoute
[INFO] Done prefix list InfrastructureRoutes
[INFO] Saved local-domain list index
[INFO] Done local-domain list DCLOUD
[INFO] Task completed successfully