Swagger/OpenAPI
Swagger is an open source set of specifications and tools for developing and describing REST APIs. RADKit Client can pull the OpenAPI specification from a Swagger enabled device, and use this information to perform REST API operations (Get, Post, Put, Delete) using the API paths provided in the specification. This feature guide first explains how to set up RADKit Service to access devices using Swagger. Then, it explains how to query the Swagger paths from the Client and how to process the results returned.
Note
The API authentication method and the location of the OpenAPI specification(s) are specific to every device type. For those devices that are not yet supported natively in RADKit, the alternative is to use the raw HTTP API and implement the appropriate authentication methods. For more details, see Raw HTTP API.
Note
Swagger is only supported on single devices at the moment.
Service configuration
To enable Swagger support, when either adding a new device, or editing an existing one, check
the Swagger
box under Available Management Protocols
. Subsequently, the Swagger
configuration section will appear:

Base URL
The Base URL
field can usually be left blank, however if the IP address or FQDN used to reach
the Swagger/OpenAPI endpoint is different from the one mentioned as Managed IP Address or Hostname
,
then the base URL has to be provided. The exact URL depends on the device type, as shown in the list
below (keep in mind that any load-balancer or reverse proxy on the path may further alter the URL,
so please test with a browser from the same location as the Service and make adjustments as needed):
SD-WAN vManage:
https://<address>:8443
ISE:
https://<address>:443
RADKit Service:
https://<address>:8081
CMS:
https://<address>:443
CVP:
https://<address>:8111
UCCE:
https://<address>:7890
Expressway:
https://<address>:443
HyperFlex:
https://<address>:443
FMC:
https://<address>:443
FTD:
https://<address>:443
Client operation
Devices Swagger status
When the Client loads the service inventory, it gets an internal attribute for each
device called swagger_config
which is set to either True
or False
,
depending on whether the Service has Swagger credentials configured for that device:
>>> vmanage = service.inventory['vmanage-1']
>>> vmanage.attributes.internal
<radkit_client.sync.device.DeviceAttributesDict object at 0x106b97220>
key value
------------------- -------------
description vManage
device_type VMANAGE
forwarded_tcp_ports 8443;443
host 172.18.124.60
http_config True
netconf_config False
snmp_version None
swagger_config True
terminal_config False
Updating the Swagger API
Every Device
object has a .swagger
attribute which
represents the Swagger API for that device. By default this
radkit_client.sync.swagger.SwaggerAPI
object has a status of UNKNOWN
:
>>> vmanage.swagger
[UNKNOWN] SwaggerAPI(status='UNKNOWN')
------ -------
status UNKNOWN
------ -------
For a list of all available status for Swagger API, see radkit_client.sync.SwaggerAPIStatus
.
If the device has the Swagger credentials configured on the Service side, its capabilities
can be retrieved from the Client using
Device.update_swagger()
:
>>> vmanage.update_swagger().wait()
[SUCCESS] FillerRequest(status='SUCCESS', rpc_name='get-swagger-paths/2.0')
---------------- ---------------------------------
sent_timestamp 2022-08-17 14:46:58
request_type Swagger paths
client_id radkit-user@example.com
service_id xxxx-yyyy-zzzz
result None
forwarder wss://prod.radkit-cloud.cisco.com/forwarder-2
e2ee_used True
compression_used zstd
---------------- ---------------------------------
The result
of this request contains more detailed information about the
swagger capabilities update.
>>> vmanage.update_swagger().wait().result
[SUCCESS] <radkit_client.sync.device.DeviceCapabilitiesUpdate object at 0xffff68f42a60>
----------- -------------------------
service_id xxxx-yyyy-zzzz
device_name vmanage
result Found 2516 swagger paths.
----------- -------------------------
If the retrieval is successful, the Swagger API status changes to AVAILABLE
:
>>> vmanage.swagger
[AVAILABLE] SwaggerAPI(status='AVAILABLE')
------ ---------
status AVAILABLE
------ ---------
Note that it’s also possible to call DeviceDict.update_swagger()
on a DeviceDict
to load the Swagger capabilities of
multiple devices at once.
Exploring Swagger paths
Once the Swagger API status becomes AVAILABLE
, the API paths supported by
Swagger can be retrieved from SwaggerAPI.paths
,
which is a dictionary where the key is the path and the value is a
radkit_client.sync.swagger.SwaggerPath
:
>>> device.swagger.paths
<radkit_client.sync.swagger.SwaggerPathsDict object at 0x7f102c5204f0>
path verbs
---------------------------------------- ----------------------
/admins GET, PUT, POST, DELETE
/admins/bulk PUT, POST, DELETE
/admins/change_password PUT
/admins/count GET
/admins/me GET
...
The supported API operations for a given path can be displayed with:
>>> vmanage.swagger.paths['/admin/user']
Path: /admin/user
Operations:
- [GET]
Get all users
(This operation takes no parameters)
- [POST]
Create a user
Pass payload using .post(json=...)
>>> vmanage.swagger.paths['/admin/user'].operations
<radkit_client.sync.swagger.SwaggerPathOperationsDict object at 0x110beb750>
verb description
------ -------------
GET Get all users
POST Create a user
>>> vmanage.swagger.paths['/admin/user'].operations['GET']
SwaggerPathOperation(description=description='Get all users')
----------- -------------
description Get all users
parameters (none)
----------- -------------
Note
When building the list of Swagger paths and their parameters, if RADKit Client encounters
a parameter name that is a reserved Python keyword, it will append an underscore _
to the
name; e.g. as
becomes as_
. Also, if a parameter name contains non-alphanumerical characters,
those will be replaced by an underscore _
.
Perform operations on an API path
To perform desired operations on an API path, use one of the supported API methods.
Here is an example to perform a get()
operation:
>>> vmanage.swagger.paths['/device/system/status']
Path: /device/system/status
Operations:
- [GET]
Get device system status list (Real Time)
Parameters:
- 'deviceId' [required]
Device IP
>>> req = vmanage.swagger.paths['/device/system/status'].get(deviceId = '6.1.1.11')
Some Swagger API paths have the required parameter in the API path itself. In the following example,
the deviceCategory
is a required parameter:
>>> vmanage.swagger.paths['/system/device/{deviceCategory}']
Path: /system/device/{deviceCategory}
Operations:
- [GET]
Get devices details. When {deviceCategory = controllers}, it returns vEdge sync status, vBond, vManage and vSmart. When {deviceCategory = vedges}, it returns all available vEdge routers
Parameters:
- 'deviceCategory' [required]
Device category
- 'model'
Device model
- 'state'
List of states
- 'uuid'
List of device uuid
- 'deviceIP'
List of device system IP
- 'validity'
List of device validity
- 'family'
The platform family to filter for
- 'siteId'
The Site Id to filter for
- 'topology'
The device topology to filter for
- 'tag'
The tag name to filter for
Since these API paths are essentially keys to the API paths dictionary, in order to perform the get()
operation with the required parameter for this API path, use the following:
>>> req = vmanage.swagger.paths['/system/device/{deviceCategory}'].get(deviceCategory='controllers').wait()
The get()
operation returns an instance of radkit_client.sync.request.FillerRequest
, which
represents the request sent from Client to Service for the Swagger API call.
>>> req
[DELIVERED] FillerRequest(status='DELIVERED', rpc_name='call-swagger-path/2.0')
---------------- --------------------------------------------------------------------------------
sent_timestamp 2022-08-17 19:43:22
request_type Swagger path query
client_id radkit_user@example.com
service_id xxxx-yyyy-zzzz
updates 0 total, 0 failed
result AsyncSwaggerResponse(device=AsyncDevice(name='vmanage-1', service_display-nam...
forwarder wss://prod.radkit-cloud.cisco.com/forwarder-2/
e2ee_used True
compression_used zstd
---------------- --------------------------------------------------------------------------------
Here is another example of performing a post()
operation to add a new user:
>>> new_user = {
"userName": "testuser", "password": "secret_pass",
"description": "a new user", "locale": "en_US", "group": ["netadmin"]
}
>>> req = vmanage.swagger.paths['/admin/user'].post(json=new_user).wait()
>>> req.result.status_code
200
>>> req.result.content
b'{}'
In this example, the status code returned by the API post()
call indicates the call was successful.
If the API call fails, then .result.content
would include the error message in the API response. Please see below
for more information on how to process the Swagger API response.
The post
, put
and patch
methods also support sending data in different formats than JSON:
# Request with binary data
>>> r = httpbin.swagger.paths['//post'].post(content=b"binary content").wait()
>>> r.result.json
{
"args": {},
"data": "binary content",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "14",
"Host": "httpbin.org",
"User-Agent": "python-httpx/0.23.0",
"X-Amzn-Trace-Id": "Root=1-6389a0f1-1bbceddf2d066f9b62fa537c",
},
"json": None,
"origin": "173.38.220.39",
"url": "https://httpbin.org/post",
}
# Request with form encoded data
>>> r = httpbin.swagger.paths['//post'].post(data={"key1": "value1", "key2": "value2"}).wait()
>>> r.result.json
{
"args": {},
"data": "",
"files": {},
"form": {"key1": "b'value1'", "key2": "b'value2'"},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "37",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-httpx/0.23.0",
"X-Amzn-Trace-Id": "Root=1-6389a129-71f0bf674ee14a2a69b0c263",
},
"json": None,
"origin": "173.38.220.39",
"url": "https://httpbin.org/post",
}
# Request with multipart file upload
>>> r = httpbin.swagger.paths['//post'].post(files={"upload-file": ("hello.py", open("hello.py", "rb"))}).wait()
>>> r.result.json
{
"args": {},
"data": "",
"files": {"upload-file": 'print("Hello World")\n'},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "208",
"Content-Type": "multipart/form-data; boundary=9d85fa88395b156f9170947f2bbd10a2",
"Host": "httpbin.org",
"User-Agent": "python-httpx/0.23.0",
"X-Amzn-Trace-Id": "Root=1-6389a842-21267add7cb6fc7764281e98",
},
"json": None,
"origin": "173.38.220.39",
"url": "https://httpbin.org/post",
}
Note
The examples above were created with the device which is proxy to the http://httpbin.org
Direct Swagger API access
Previously we used radkit_client.sync.swagger.SwaggerAPI.paths
to explore the Swagger API paths
and supported API operations. Alternatively, RADKit also allows direct access to an API path if the
path is known by other means, e.g., for SDWAN vManage by using the
vManage API Reference. The
supported operations are:
SwaggerAPI.get()
SwaggerAPI.put()
SwaggerAPI.post()
SwaggerAPI.delete()
SwaggerAPI.patch()
Here is an example of a direct API get()
operation on /device/system/status
with an input parameter
of deviceID
:
>>> req = vmanage.swagger.get('/device/system/status', {deviceId: "6.1.1.11"})
>>> from pprint import pprint
>>> pprint(req.result.json)
{'data': [{'board_type': 'Vedge-CSR',
'bootloader_version': 'Not applicable',
'build_number': 'Not applicable',
...
Processing the Swagger response
As with all RADKit requests, a Swagger request will have an associated result
object.
The Swagger response will be stored in result.content
as bytes. To convert it
into a Python object, the result.json
property can be used.
>>> req = vmanage.swagger.get('/device/system/status', {"deviceId": "6.1.1.11"})
>>> req.result.content
b'{"header":{"generatedOn":1660934774631,"viewKeys":{"uniqueKey":["vdevice-dataKey"],"preferenceKey":"grid-SystemStatus"},"columns":...
>>> from pprint import pprint
>>> pprint(req.result.json)
{'data': [{'board_type': 'Vedge-CSR',
'bootloader_version': 'Not applicable',
'build_number': 'Not applicable',
'chassis-serial-number': 'SSI130300YK',
'config_date/date-time-string': 'Fri Aug 19 14:46:14 EDT 2022',
...
Alternatively, the json object can be converted to a json string and then printed for a better visual representation:
>>> import json
>>> print(json.dumps(req.result.json, indent=4))
{
"header": {
"generatedOn": 1724706125439,
"viewKeys": {
"uniqueKey": [
"vdevice-dataKey"
],
"preferenceKey": "grid-SystemStatus"
},
...
Client metadata
Some Swagger clients offer metadata.
The metadata are fetched during Device.update_swagger()
and can be accessed by SwaggerAPI.metadata <radkit_client.sync.swagger.SwaggerAPI.metadata>:
>>> fmc.update_swagger().wait()
[SUCCESS] FillerRequest(status='SUCCESS', rpc_name='get-swagger-paths/2.0')
---------------- ---------------------------------
sent_timestamp 2022-08-17 14:46:58
request_type Swagger paths
client_id radkit-user@example.com
service_id xxxx-yyyy-zzzz
result None
forwarder wss://prod.radkit-cloud.cisco.com/forwarder-2
e2ee_used True
compression_used zstd
---------------- ---------------------------------
>>> pprint(fmc.swagger.metadata)
{'domain_id': '111',
'domain_uuid': 'e276abec-e0f2-11e3-8169-6d9ed49b625f',
'domains': {'Global': 'e276abec-e0f2-11e3-8169-6d9ed49b625f'},
'user_uuid': '68d03c42-d9bd-11dc-89f2-b7961d42c462'}