Device Failover
The Conductor API allows for collecting and modifying attributes in Conductor. The following script is an example of how to perform device failover within an overlay network.
Select a device in an overlay and replace it with a different device or device group.
Prerequisites
- Experience with Python and install Python packages
- RESTful API experience
- Conductor user account with API access enabled
Note: ICMP is used to monitor a host to determine when to failover. This requires a
sudoer or Administrator account to run.
Required Python Packages
multiping
requests
Note: Other imports in this script should be covered by Python's default packages. If
not included, you may have to install them manually.
Sample Script
#!/usr/bin/python3
from ipaddress import ip_address
import json
from multiping import MultiPing
import os
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import sys
import time
# Suppress cert warning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
url = "https://<Conductor IP>/api/v1/"
# pre-2.1.3 API headers
# headers = {
# 'x-person-token': '1234',
# 'x-person-email': 'api@temperednetworks.com',
# 'content-type': 'application/json'
# }
headers = {
'x-api-client-id': 'KWFCITL4VX_B-ZSdywD0Eg',
'x-api-token': 'grui5TWLvvic8mZ7fgglbA',
'content-type': 'application/json'
}
def generate_menu_select(cont, msg):
"""
Generate a menu structure from a list of dictionaries
:param cont: content to be processed
:param msg: message to be asked
:returns: selected object from list
"""
data = sorted(cont, key=lambda k: k['name'])
for d in data:
print('{0}) {1}'.format(data.index(d) + 1, d['name']))
i = input(msg)
try:
return(data[int(i) - 1])
except (IndexError, ValueError):
print('Selected input {0} not found or invalid'.format(i))
sys.exit()
def get_overlay():
"""
Get all overlays in Conductor
:returns: selected overlay network
"""
r = requests.get(url + "overlay_networks", headers=headers, verify=False)
if r.status_code == requests.codes.ok:
print('Collected overlays:')
return generate_menu_select(r.json(),
"Select overlay network to failover: ")
else:
print('Error getting overlay networks')
print(r.json())
sys.exit()
def get_object_in_overlay(devs, dgs, ovl_gps):
"""
Get all devices/device groups in the overlay. Move to own list.
:param devs: all devices
:param dgs: all device groups
:param ovl_gps: all devices/device groups in overlay
:returns: selected device/device group
"""
# devices and device groups
grps = [d for d in devs for o in ovl_gps if o == d['uuid']]
grps.extend([d for d in dgs for o in ovl_gps if o == d['uuid']])
print('Collected devices and device groups in overlay network:')
return generate_menu_select(grps, 'Select device/device group to replace: ')
def get_device_groups():
"""
Get all device groups in Conductor
:returns: all Conductor device groups
"""
r = requests.get(url + "device_groups", headers=headers, verify=False)
if r.status_code == requests.codes.ok:
return r.json()
else:
print('Error getting device groups')
print(r.json())
sys.exit()
def get_devices():
"""
Get all devices in Conductor
:returns: all Conductor devices
"""
r = requests.get(url + "devices", headers=headers, verify=False)
if r.status_code == requests.codes.ok:
return r.json()
else:
print('Error getting devices')
print(r.json())
sys.exit()
def add_device_to_overlay(ovl_uuid, d_uuid):
"""
Add a device to an overlay network
:param ovl_uuid: overlay network UUID
:param d_uuid: device UUID
"""
payload = {'network_id': ovl_uuid,
'device_group_ids': [d_uuid]}
r = requests.post(url + "overlay_network_devices", headers=headers,
data=json.dumps(payload), verify=False)
if not r.status_code == requests.codes.ok:
print('Error adding to overlay')
print(r.json())
sys.exit()
def remove_device_from_overlay(ovl_uuid, d_uuid):
"""
Remove a device from an overlay network
:param ovl_uuid: overlay network UUID
:param d_uuid: device UUID
"""
payload = {'network_id': ovl_uuid,
'device_group_ids': [d_uuid]}
r = requests.delete(url + "overlay_network_devices", headers=headers,
data=json.dumps(payload), verify=False)
if not r.status_code == requests.codes.ok:
print('Error removing from overlay')
print(r.json())
sys.exit()
def build_overlay_policy(ovl_uuid, d_uuid, ds_uuid):
"""
Build the overlay policy
:param ovl_uuid: overlay network UUID
:param d_uuid: device UUID
:param ds_uuid: UUIDs of devices in policy with previous device (target)
"""
for uuid in ds_uuid:
payload = {'network_id': ovl_uuid,
'device_group_1': d_uuid,
'device_group_2': uuid}
r = requests.post(url + "overlay_network_devices/trust", headers=headers,
data=json.dumps(payload), verify=False)
if not r.status_code == requests.codes.ok:
print('Error adding policy in overlay')
print(r.json())
def get_replacement_object(devs, dgs):
"""
Select which object to use as a replacement
:param devs: all devices
:param dgs: all device groups
:returns: device/device group JSON data
"""
selection = [{'name': 'Device'}, {'name': 'Device Group'}]
sel = generate_menu_select(selection, 'Type to replace with: ')
if sel['name'] == 'Device':
return generate_menu_select(devs, 'Select replacement device: ')
elif sel['name'] == 'Device Group':
return generate_menu_select(dgs, 'Select replacement device group: ')
def replace_overlay_object(ovl, target, replacement):
"""
Replace a given target with a replacement device/device group object
:param ovl: overlay JSON data
:param target: target device JSON data
:param replacement: replacement device JSON data
"""
policies = [p for p in [t['from'] for t in ovl['policy'] if t['to'] == target['uuid']]]
remove_device_from_overlay(ovl['uuid'], target['uuid'])
print('Removing device from overlay')
add_device_to_overlay(ovl['uuid'], replacement['uuid'])
print('Adding replacement device to overlay')
build_overlay_policy(ovl['uuid'], replacement['uuid'], policies)
print('Build overlay policy with replacement device')
def select_mon_target():
"""
Input an IP to monitor
:returns: IP address to monitor
"""
selection = True
mon_target = None
while selection:
mon_target = input('Enter IP target to monitor: ')
try:
ip_address(mon_target)
selection = False
except ValueError:
print('Invalid IP address.')
continue
return mon_target
def monitor_target(mon_target):
"""
Monitor the given target import ip
:param mon_target: IP address to monitor
"""
active = True
while active:
if mon_target:
mp = MultiPing([mon_target])
mp.send()
resp, no_resp = mp.receive(.1)
stamp = time.strftime('%Y-%m-%d %H:%M:%S')
if no_resp:
print('{0}: Monitor failed'.format(stamp))
break
else:
print('{0}: Ping monitor successful'.format(stamp))
time.sleep(1)
def main():
if not os.geteuid() == 0:
print('Must run as root or Administrator. Exiting...')
else:
# get content
ovl = get_overlay()
devs = get_devices()
dgs = get_device_groups()
# ask questions
target = get_object_in_overlay(devs, dgs, ovl['device_groups'])
replacement = get_replacement_object(devs, dgs)
# monitor ip
mon_target = select_mon_target()
monitor_target(mon_target)
# do work
replace_overlay_object(ovl, target, replacement)
print('Device failover completed')
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
sys.exit()