Exam Topics Covered: 2.1 Implement robust REST API error handling for time outs and rate limits
2.2 Implement control flow of consumer code for unrecoverable REST API errors
2.3 Identify ways to optimize API usage through HTTP cache controls
2.4 Construct an application that consumes a REST API that supports pagination
2.5 Describe the steps in the OAuth2 three-legged authorization code grant flow
The code below implements a loop that will retry three times to connect to the site, and when the site fails to respond after 5 seconds a ReadTimeout exception will be thrown. Alternatively, the URL can be modified to a non-existent site and in this case a ConnectionError will be caught and the error displayed.
import requests
# Try to connect for up to 5 seconds, then retry up to 3 times
timeout = 5
attempts = 3
# This site can be used for testing, the delay/10 adds a 10 second delay to the response
url = 'https://httpbin.org/delay/10'
# Comment the above url and uncomment below to test the unavailable host functionality
#url = 'https://alkfjdsaofjasld.com'
for i in range(attempts):
try:
resp = requests.get(url, timeout=timeout)
# If the request succeeded, then break out of loop
if resp.ok:
break
# This is what happens if the site is valid but doesn't respond in a timely manner
except requests.exceptions.ReadTimeout as err:
if i + 1 == attempts:
print(f'Retries exceeded, got error {err}')
# This is what happens if the site is completely unreachable
except requests.exceptions.ConnectionError as err:
if i + 1 == attempts:
print(f'Retries exceeded, got error {err}')
Retries exceeded, got error HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=5)
API implementations often impose a rate limit on the number of requests in some way in order to prevent against oversubscription of resources. This is often implemented as number of requests per minute. For example, Cisco's public DNA Center sandbox for some calls only allows 5 requests per minute. When the API limit is reached, the server will return HTTP status 429 Too Many Requests
.
Interestingly, the rate limiter may be applied to some endpoints but not others. During writing of the below script, it was discovered that the rate limit does not apply to the /network-device API calls. However, it does apply to calls to /client-detail.
import requests
from requests.auth import HTTPBasicAuth
import time
from pprint import pprint
username = "devnetuser"
password = "Cisco123!"
hostname = "sandboxdnac2.cisco.com"
headers = {"Content-Type": "application/json"}
# Use Basic Authentication
auth = HTTPBasicAuth(username, password)
# Request URL for the token
login_url = f"https://{hostname}/dna/system/api/v1/auth/token"
# Retrieve the token
resp = requests.post(login_url, headers=headers, auth=auth)
token = resp.json()['Token']
# Add the token to subsequent requests
headers['X-Auth-Token'] = token
url = f"https://{hostname}/dna/intent/api/v1/network-device"
resp = requests.get(url, headers=headers, auth=auth)
count = 0
# Loop over devices and get device by id
# Each time we reach five requests, pause for 60 seconds to avoid the rate limit
for i, device in enumerate(resp.json()['response']):
count += 1
device_count = len(resp.json()['response'])
print (f"REQUEST #{i+1}")
url = f"https://{hostname}/dna/intent/api/v1/network-device/{device['id']}"
req = requests.get(url, headers=headers, auth=auth)
pprint(req.json(), indent=2)
if count == 5 and (i+1) < device_count:
print("Sleeping for 60 seconds...")
time.sleep(60)
count = 0
REQUEST #1 { 'response': { 'apManagerInterfaceIp': '', 'associatedWlcIp': '', 'bootDateTime': '2020-03-12 16:05:32', 'collectionInterval': 'Global Default', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Supported', 'errorCode': None, 'errorDescription': None, 'family': 'Wireless Controller', 'hostname': '3504_WLC', 'id': '72dc1f0a-e4da-4ec3-a055-822416894dd5', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '72dc1f0a-e4da-4ec3-a055-822416894dd5', 'interfaceCount': '0', 'inventoryStatusDetail': '<status><general ' 'code="SUCCESS"/></status>', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '50:61:bf:57:2f:00', 'managementIpAddress': '10.10.20.51', 'memorySize': '3735220224', 'platformId': 'AIR-CT3504-K9', 'reachabilityFailureReason': '', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': 'FCW2218M0B1', 'series': 'Cisco 3500 Series Wireless LAN Controller', 'snmpContact': '', 'snmpLocation': '', 'softwareType': 'Cisco Controller', 'softwareVersion': '8.8.111.0', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 3504 Wireless LAN Controller', 'upTime': '102 days, 7:41:06.00', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #2 { 'response': { 'apManagerInterfaceIp': '', 'associatedWlcIp': '', 'bootDateTime': '2020-02-27 15:29:59', 'collectionInterval': 'Global Default', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Supported', 'errorCode': None, 'errorDescription': None, 'family': 'Switches and Hubs', 'hostname': 'leaf1', 'id': '3dd27ed6-44f9-486d-abaf-a779781431a0', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '3dd27ed6-44f9-486d-abaf-a779781431a0', 'interfaceCount': '0', 'inventoryStatusDetail': '<status><general ' 'code="SUCCESS"/></status>', 'lastUpdateTime': 1592873279404, 'lastUpdated': '2020-06-23 00:47:59', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:72:78:54:d1:00', 'managementIpAddress': '10.10.20.81', 'memorySize': 'NA', 'platformId': 'C9300-48U', 'reachabilityFailureReason': '', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': 'FCW2214L0VK', 'series': 'Cisco Catalyst 9300 Series Switches', 'snmpContact': '', 'snmpLocation': '', 'softwareType': 'IOS-XE', 'softwareVersion': '16.6.4a', 'tagCount': '0', 'tunnelUdpPort': None, 'type': 'Cisco Catalyst 9300 Switch', 'upTime': '116 days, 9:18:32.13', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #3 { 'response': { 'apManagerInterfaceIp': '', 'associatedWlcIp': '', 'bootDateTime': '2020-02-27 15:29:41', 'collectionInterval': 'Global Default', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Supported', 'errorCode': None, 'errorDescription': None, 'family': 'Switches and Hubs', 'hostname': 'leaf2.abc.inc', 'id': '5bc5b967-3f83-4195-891c-788f3e9048f3', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '5bc5b967-3f83-4195-891c-788f3e9048f3', 'interfaceCount': '0', 'inventoryStatusDetail': '<status><general ' 'code="SUCCESS"/></status>', 'lastUpdateTime': 1592873501745, 'lastUpdated': '2020-06-23 00:51:41', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '50:61:bf:ec:07:80', 'managementIpAddress': '10.10.20.82', 'memorySize': 'NA', 'platformId': 'C9300-48U', 'reachabilityFailureReason': '', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': 'FCW2214L0UZ', 'series': 'Cisco Catalyst 9300 Series Switches', 'snmpContact': '', 'snmpLocation': '', 'softwareType': 'IOS-XE', 'softwareVersion': '16.6.4a', 'tagCount': '0', 'tunnelUdpPort': None, 'type': 'Cisco Catalyst 9300 Switch', 'upTime': '116 days, 9:22:56.64', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #4 { 'response': { 'apManagerInterfaceIp': '', 'associatedWlcIp': '', 'bootDateTime': '2020-02-12 19:01:36', 'collectionInterval': 'Global Default', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Supported', 'errorCode': None, 'errorDescription': None, 'family': 'Switches and Hubs', 'hostname': 'spine1.abc.inc', 'id': '2f0b7d3b-c9e1-491e-a584-f272b5403719', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '2f0b7d3b-c9e1-491e-a584-f272b5403719', 'interfaceCount': '0', 'inventoryStatusDetail': '<status><general ' 'code="SUCCESS"/></status>', 'lastUpdateTime': 1592873316091, 'lastUpdated': '2020-06-23 00:48:36', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '70:01:b5:5d:1b:00', 'managementIpAddress': '10.10.20.80', 'memorySize': 'NA', 'platformId': 'WS-C3850-24P-L', 'reachabilityFailureReason': '', 'reachabilityStatus': 'Reachable', 'role': 'DISTRIBUTION', 'roleSource': 'AUTO', 'serialNumber': 'FCW2212D05S', 'series': 'Cisco Catalyst 3850 Series Ethernet Stackable ' 'Switch', 'snmpContact': '', 'snmpLocation': '', 'softwareType': 'IOS-XE', 'softwareVersion': '16.3.5b', 'tagCount': '0', 'tunnelUdpPort': None, 'type': 'Cisco Catalyst38xx stack-able ethernet switch', 'upTime': '131 days, 5:47:53.36', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #5 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-1', 'id': '8b5fb4b6-2c84-409f-9950-c2d12e06ed17', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '8b5fb4b6-2c84-409f-9950-c2d12e06ed17', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:02:00', 'managementIpAddress': '10.10.20.241', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0001', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'Global/HQ/Floor 17', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:24:13.360', 'waasDeviceMode': None}, 'version': '1.0'} Sleeping for 60 seconds... REQUEST #6 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-10', 'id': 'fe55667c-6de4-4657-b68c-60f17b21d23b', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': 'fe55667c-6de4-4657-b68c-60f17b21d23b', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:0b:00', 'managementIpAddress': '10.10.20.250', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0010', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'default-location ', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:22:56.380', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #7 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-2', 'id': 'd5069e6d-e5f5-4917-b522-1e5d93c66ea0', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': 'd5069e6d-e5f5-4917-b522-1e5d93c66ea0', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:03:00', 'managementIpAddress': '10.10.20.242', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0002', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'Global/HQ/Floor 17', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:24:13.360', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #8 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-3', 'id': 'da13b595-7ed9-4fc8-ba3a-d7b6cbff6dcc', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': 'da13b595-7ed9-4fc8-ba3a-d7b6cbff6dcc', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:04:00', 'managementIpAddress': '10.10.20.243', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0003', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'Global/HQ/Floor 17', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:23:05.360', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #9 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-4', 'id': 'f696638c-53fe-4b42-b6c1-a37d8892e736', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': 'f696638c-53fe-4b42-b6c1-a37d8892e736', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:05:00', 'managementIpAddress': '10.10.20.244', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0004', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'default-location ', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:18:57.370', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #10 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-5', 'id': '75d57d36-dade-40cb-aac3-b2267e7e0180', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '75d57d36-dade-40cb-aac3-b2267e7e0180', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:06:00', 'managementIpAddress': '10.10.20.245', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0005', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'default-location ', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:18:57.370', 'waasDeviceMode': None}, 'version': '1.0'} Sleeping for 60 seconds... REQUEST #11 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-6', 'id': '39cc2101-1a68-455e-a2a7-49d1cfb31835', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '39cc2101-1a68-455e-a2a7-49d1cfb31835', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:07:00', 'managementIpAddress': '10.10.20.246', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0006', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'default-location ', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:22:55.370', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #12 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-7', 'id': 'b2bf41bf-0e2f-4255-85ad-4ca403686b3a', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': 'b2bf41bf-0e2f-4255-85ad-4ca403686b3a', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:08:00', 'managementIpAddress': '10.10.20.247', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0007', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'default-location ', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:22:55.370', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #13 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-8', 'id': 'bafa2b45-b827-48d5-9022-8c66b5a5b18f', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': 'bafa2b45-b827-48d5-9022-8c66b5a5b18f', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:09:00', 'managementIpAddress': '10.10.20.248', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0008', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'default-location ', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:22:55.370', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #14 { 'response': { 'apManagerInterfaceIp': '10.10.20.51', 'associatedWlcIp': '10.10.20.51', 'bootDateTime': None, 'collectionInterval': 'NA', 'collectionStatus': 'Managed', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'null', 'errorDescription': None, 'family': 'Unified AP', 'hostname': 'T1-9', 'id': '85daf2da-571a-4570-9ce3-c68867ad891a', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': '85daf2da-571a-4570-9ce3-c68867ad891a', 'interfaceCount': '0', 'inventoryStatusDetail': 'NA', 'lastUpdateTime': 1592869592780, 'lastUpdated': '2020-06-22 23:46:32', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': '00:2b:01:00:0a:00', 'managementIpAddress': '10.10.20.249', 'memorySize': 'NA', 'platformId': 'AIR-AP1141N-A-K9', 'reachabilityFailureReason': 'NA', 'reachabilityStatus': 'Reachable', 'role': 'ACCESS', 'roleSource': 'AUTO', 'serialNumber': '1140K0009', 'series': 'Cisco 1140 Series Unified Access Points', 'snmpContact': '', 'snmpLocation': 'default-location ', 'softwareType': None, 'softwareVersion': '8.5.97.162', 'tagCount': '0', 'tunnelUdpPort': '16666', 'type': 'Cisco 1140 Unified Access Point', 'upTime': '96days 02:22:55.370', 'waasDeviceMode': None}, 'version': '1.0'} REQUEST #15 { 'response': { 'apManagerInterfaceIp': '', 'associatedWlcIp': '', 'bootDateTime': None, 'collectionInterval': 'Global Default', 'collectionStatus': 'Could Not Synchronize', 'deviceSupportLevel': 'Unsupported', 'errorCode': 'DEV-UNREACHED', 'errorDescription': 'SNMP timeouts are occurring with this ' 'device. Either the SNMP credentials are ' 'not correctly provided to controller or ' 'the device is responding slow and SNMP ' 'timeout is low. If it is a timeout issue, ' 'controller will attempt to progressively ' 'adjust the timeout in subsequent ' 'collection cycles to get device to ' 'managed state. User can also run ' 'discovery again only for this device ' 'using the discovery feature after ' 'adjusting the timeout and SNMP ' 'credentials as required. Or user can ' 'update the timeout and SNMP credentials ' 'as required using update credentials.', 'family': None, 'hostname': None, 'id': 'c0cfcb54-cac7-49bf-ad40-f437307426b0', 'instanceTenantId': '5e5a432575161200cc4ac95c', 'instanceUuid': 'c0cfcb54-cac7-49bf-ad40-f437307426b0', 'interfaceCount': '0', 'inventoryStatusDetail': '<status><general ' 'code="DEV_UNREACHED"/></status>', 'lastUpdateTime': 1592867299643, 'lastUpdated': '2020-06-22 23:08:19', 'lineCardCount': '0', 'lineCardId': '', 'location': None, 'locationName': None, 'macAddress': None, 'managementIpAddress': '192.0.2.1', 'memorySize': 'NA', 'platformId': None, 'reachabilityFailureReason': 'SNMP Connectivity Failed', 'reachabilityStatus': 'Unreachable', 'role': 'UNKNOWN', 'roleSource': 'AUTO', 'serialNumber': None, 'series': None, 'snmpContact': None, 'snmpLocation': None, 'softwareType': None, 'softwareVersion': None, 'tagCount': '0', 'tunnelUdpPort': None, 'type': None, 'upTime': None, 'waasDeviceMode': None}, 'version': '1.0'}
Public - any cache along the path may cache the content. Often paired with a max-age which indicates the number of seconds that the content can be considered fresh. Expires header could also be used to provide a specific date of expiry.
Private - only the client itself is permitted to cache the content. No caching allowed by intermediary cache engines between the client and the server.
No-Store - Forbids caching at all, not even by the client. Typically used for sensitive data, such as personal financial data.
Absent - no cache-control provided at all. RFC suggests using heuristic-based caching. Client makes an educated guess on how long to cache based on the "last modified" header.
The Python requests module does not natively support caching. The CacheControl package can be used, which can be installed with
pip install cachecontrol
.
Entity Tag (ETag) - A field in the HTTP response header that can be populated with a hash value, and which will be updated by the server if the content has changed. Clients can compare the cached hash value with the value in new requests to determine if the content has changed.
Some API calls may set a limit on the number of objects that are returned in a single call. In this case, the API should return paging details in the JSON body including the URL to request the next set of data as well as the previous set. If Previous is empty, then we are on the first set of data. If Next is empty, then we know we have reached the end of the dataset. Some API implementations follow RFC5988, which includes a Link header in the format:
Link: <https://webexapis.com/v1/people?displayName=Harold&max=10&before&after=Y2lzY29zcGFyazovL3VzL1BFT1BMRS83MTZlOWQxYy1jYTQ0LTRmZWQtOGZjYS05ZGY0YjRmNDE3ZjU>; rel="next"
The above example if from the Webex API, which implements RFC5988. This is described in the API documentation here: https://developer.webex.com/docs/api/basics
In the code below, first we get the Room IDs for the WebEx Teams rooms I am a member of. Then in Example #1, I retrieve the members from the DevNet Dev Support Questions room while limiting the number of objects to 5, and display the Link header. Example #2 shows how you can create a continuous function that follows the Link URL and displays the content. The While loop is broken when the Link header is no longer present, returning None when we try to retrieve it with headers.get('Link').
import requests
import re
api_path = "https://webexapis.com/v1"
# This is my personal access token that is only good for 12 hours.
# You can retrieve your token here: https://developer.webex.com/docs/api/getting-started
token = "ZTQ3ZWEzYmQtNGJkNS00NjU4LThjNmUtZmM1ZjJkZjA3ZDk3MmY0NjYzMjEtZmIx_PF84_consumer"
headers = {"Authorization": f"Bearer {token}"}
print('-' * 25 + 'ROOM IDs' + '-' * 25 + '\n')
# List the rooms, and collect the ID for the DevNet Support Questions room
get_rooms = requests.get(f"{api_path}/rooms", headers=headers)
for room in get_rooms.json()['items']:
print(room['title'], room['id'])
if room['title'] == "DevNet Dev Support Questions":
id = room['id']
# Pass the Room ID and Max number of records to return in as query parameters
# In the first example the room has 5000 members but we are limiting the
# returned objects to 5 with the "max" parameter
params = {"roomId": id, "max": 5}
print('-' * 25 + 'EXAMPLE #1' + '-' * 25 + '\n')
response = requests.get(f"{api_path}/memberships", headers=headers, params=params)
# The Link header indicates the URL to retrieve the next set of room members
# We can get this url from the `links` attribute of the response object
# It can also be retrieved from the headers, but would require extra parsing
# due to the formatting
print('NEXT URL:')
print(response.headers.get('Link'))
response.headers.get('Link')
'<https://webexapis.com/v1/memberships?roomId=Y2lzY29zcGFyazovL3VzL1JPT00vNGZlOTQ2MTAtZjA2MS0xMWU1LWI4Y2UtMTEzZjhkZmMxNGJl&max=5&cursor=cm9vbUlkPTRmZTk0NjEwLWYwNjEtMTFlNS1iOGNlLTExM2Y4ZGZjMTRiZSZsaW1pdD01JmJlZm9yZSZhZnRlcj0wMDI1ZWU0NS1lNjc5LTQwMGMtYjEzYi1jMGYxNWE3MjUzZjI=>; rel="next"'
response.links
{'next': {'url': 'https://webexapis.com/v1/memberships?roomId=Y2lzY29zcGFyazovL3VzL1JPT00vNGZlOTQ2MTAtZjA2MS0xMWU1LWI4Y2UtMTEzZjhkZmMxNGJl&max=5&cursor=cm9vbUlkPTRmZTk0NjEwLWYwNjEtMTFlNS1iOGNlLTExM2Y4ZGZjMTRiZSZsaW1pdD01JmJlZm9yZSZhZnRlcj0wMDI1ZWU0NS1lNjc5LTQwMGMtYjEzYi1jMGYxNWE3MjUzZjI=', 'rel': 'next'}}
print('-' * 25 + 'EXAMPLE #2' + '-' * 25 + '\n')
# This function will follow the Link URLs until there are no more, printing out
# the member display name and next URL at each iteration. This room only has
# 2 members, so setting the max parameter to 1 so that pagination occurs
def get_members(room_id):
params = {"roomId": room_id, "max": 1}
# Make the initial request and print the member name
response = requests.get(f"{api_path}/memberships", headers=headers, params=params)
print(response.json()['items'][0]['personDisplayName'])
# Loop until the Link header is empty or not present
while response.headers.get('Link'):
# Get the URL from the Link header
next_url = response.links['next']['url']
print(f"NEXT: {next_url}")
# Request the next set of data
response = requests.get(next_url, headers=headers)
if response.headers.get('Link'):
print(response.json()['items'][0]['personDisplayName'])
else:
print('No Link header, finished!')
# Execute the function using the Bot_Testing RoomID
get_members("Y2lzY29zcGFyazovL3VzL1JPT00vNjJmN2RjNDAtYTZhZi0xMWVhLTk1ZDctYzNlZGNhMDI5OGQx")
-------------------------ROOM IDs------------------------- Bot_Testing Y2lzY29zcGFyazovL3VzL1JPT00vNjJmN2RjNDAtYTZhZi0xMWVhLTk1ZDctYzNlZGNhMDI5OGQx DevNet Dev Support Questions Y2lzY29zcGFyazovL3VzL1JPT00vNGZlOTQ2MTAtZjA2MS0xMWU1LWI4Y2UtMTEzZjhkZmMxNGJl -------------------------EXAMPLE #1------------------------- NEXT URL: <https://webexapis.com/v1/memberships?roomId=Y2lzY29zcGFyazovL3VzL1JPT00vNGZlOTQ2MTAtZjA2MS0xMWU1LWI4Y2UtMTEzZjhkZmMxNGJl&max=5&cursor=cm9vbUlkPTRmZTk0NjEwLWYwNjEtMTFlNS1iOGNlLTExM2Y4ZGZjMTRiZSZsaW1pdD01JmJlZm9yZSZhZnRlcj0wMDI1ZWU0NS1lNjc5LTQwMGMtYjEzYi1jMGYxNWE3MjUzZjI=>; rel="next" -------------------------EXAMPLE #2------------------------- Matt Mullen NEXT: https://webexapis.com/v1/memberships?roomId=Y2lzY29zcGFyazovL3VzL1JPT00vNjJmN2RjNDAtYTZhZi0xMWVhLTk1ZDctYzNlZGNhMDI5OGQx&max=1&cursor=cm9vbUlkPTYyZjdkYzQwLWE2YWYtMTFlYS05NWQ3LWMzZWRjYTAyOThkMSZsaW1pdD0xJmJlZm9yZSZhZnRlcj1iYmRiZDljNC1hMTRkLTQwMTYtYjVjZi1jOGExNzY0MWI1YWQ bottymcbotalot NEXT: https://webexapis.com/v1/memberships?roomId=Y2lzY29zcGFyazovL3VzL1JPT00vNjJmN2RjNDAtYTZhZi0xMWVhLTk1ZDctYzNlZGNhMDI5OGQx&max=1&cursor=cm9vbUlkPTYyZjdkYzQwLWE2YWYtMTFlYS05NWQ3LWMzZWRjYTAyOThkMSZsaW1pdD0xJmJlZm9yZSZhZnRlcj1jY2MwZDI0Mi1lMDY3LTQ3YTItOGY5Mi1kMzYzNjBkMjA3ZjY No Link header, finished!
OAuth2 is an authorization framework that enables login and access to resources via a third-party. It is defined in RFC 6749: https://tools.ietf.org/html/rfc6749/. A good example of this implementation can be seen when logging into Cisco DevNet using Github credentials.
4 Main Components
The client, usually a web-based service, receives a request to access a protected resource. To access the resources, the client requires authorization from the resource owner.
The client redirects the resource owner's user-agent to the authorization server.
The authorization server authenticates the resource owner, confirms resource access, and gathers consent if not previously saved.
The authorization server redirects the resource owner's user agent to the client.
During the redirection process, the authorization server appends an authorization code.
The client receives the authorization code and authenticates to the authorization server to exchange the code for an access token.
Note that this example assumes a confidential client. Public clients are not required to authenticate.
If the authorization code is valid, the authorization server returns an access token (and a refresh token, if configured) to the client.
The client requests access to the protected resources from the resource server.
The resource server contacts the authorization server to validate the access token.
The authorization server validates the token and responds to the resource server.
If the token is valid, the resource server allows the client access to the protected resources.
Reference for the above: https://backstage.forgerock.com/docs/am/6.5/oauth2-guide/#figure-oauth2-authz
Real World Example: Logging into DevNet Sandbox using Github