Python script that clusters two BIG-IPs
Problem this snippet solves:
This script automates the clustering of two BIG-IPs and performs some pre-clustering validation (NTP, CM device configuration, matching TMOS)
How to use this snippet:
Configure your BIG-IPs hostnames, CM device characteristics, NTP, etc and execute the script.
Code :
#!/usr/bin/env python3 # Import the proper python modules we'll need import datetime import time import requests import sys import json from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # # Declare functions # def abort_script(reason): print('*** Aborting script execution! ***') if len(str(reason)) > 0: print('ERROR: ' + str(reason)) sys.exit(2) def icontrol_get(host,username,password,path): apiCall = requests.session() apiCall.headers.update({'Content-type':'application/json'}) apiCall.auth = (username,password) apiUri = 'https://' + host + '/mgmt/tm' + path try: apiResponse = apiCall.get(apiUri,verify=False) except requests.exceptions.RequestException as e: abort_script(str(e)) return(apiResponse.text) def icontrol_post(host,username,password,path,api_payload): apiCall = requests.session() apiCall.headers.update({'Content-type':'application/json'}) apiCall.auth = (username,password) apiUri = 'https://' + host + '/mgmt/tm' + path try: apiResponse = apiCall.post(apiUri,verify=False,data=json.dumps(api_payload)) except requests.exceptions.RequestException as e: abort_script(str(e)) return(apiResponse.text) def icontrol_put(host,username,password,path,api_payload): apiCall = requests.session() apiCall.headers.update({'Content-type':'application/json'}) apiCall.auth = (username,password) apiUri = 'https://' + host + '/mgmt/tm' + path try: apiResponse = apiCall.put(apiUri,verify=False,data=json.dumps(api_payload)) except requests.exceptions.RequestException as e: abort_script(str(e)) return(apiResponse.text) def icontrol_patch(host,username,password,path,api_payload): apiCall = requests.session() apiCall.headers.update({'Content-type':'application/json'}) apiCall.auth = (username,password) apiUri = 'https://' + host + '/mgmt/tm' + path try: apiResponse = apiCall.patch(apiUri,verify=False,data=json.dumps(api_payload)) except requests.exceptions.RequestException as e: abort_script(str(e)) return(apiResponse.text) def icontrol_delete(host,username,password,path,object): apiCall = requests.session() apiCall.headers.update({'Content-type':'application/json'}) apiCall.auth = (username,password) apiUri = 'https://' + host + '/mgmt/tm' + path + object try: apiResponse = apiCall.delete(apiUri,verify=False) except requests.exceptions.RequestException as e: abort_script(str(e)) return(apiResponse.text) def icontrol_test_connection(host,username,password): apiCall = requests.session() apiCall.headers.update({'Content-type':'application/json'}) apiCall.auth = (username,password) apiUri = 'https://' + host + '/mgmt/tm/sys/clock' try: apiResponse = apiCall.get(apiUri,verify=False) except requests.exceptions.RequestException as e: abort_script(str(e)) if '"kind"' in apiResponse.text: testresult = True else: testresult = False return(testresult) def icontrol_save_config(host,username,password): apiCall = requests.session() apiCall.headers.update({'Content-type':'application/json'}) apiCall.auth = (username,password) apiUri = 'https://' + host + '/mgmt/tm/sys/config' try: apiResponse = apiCall.post(apiUri,verify=False,data=json.dumps({'command':'save'})) except requests.exceptions.RequestException as e: abort_script(str(e)) return(apiResponse.text) # # We will save variables in the primary and standby variables, which we'll define now # primary = {} standby = {} ############################### # Pre-clustering Verification ############################### # # Ask for the cluster member mgmt IP addresses, usernames and passwords # primary['mgmt_ip'] = input('Enter the management IP address of the primary node: ') primary['username'] = input('Enter the username for the primary node: ') primary['password'] = input('Password: ') standby['mgmt_ip'] = input('Enter the management IP address of the secondary node: ') standby['username'] = input('Enter the username for the secondary node: ') standby['password'] = input('Password: ') print('Enter the desired device group name on the BIG-IPs') cluster_group_name = input('[If you\'re unsure, enter failover-bigip]: ') # # Test iControl REST connectivity # print('Testing API connectivity to the primary node') primary_test = icontrol_test_connection(primary['mgmt_ip'],primary['username'],primary['password']) if primary_test == True: print(' Successfully connected to the primary node') else: abort_script('Could not connect to the primary node during API check') print('Testing API connectivity to the secondary node') secondary_test = icontrol_test_connection(standby['mgmt_ip'],standby['username'],standby['password']) if secondary_test == True: print(' Successfully connected to the secondary node') else: abort_script('Could not connect to the secondary node during API check') # # Gather details about each node # apiResponse = icontrol_get(primary['mgmt_ip'],primary['username'],primary['password'],'/sys/global-settings/') primary['hostname'] = json.loads(apiResponse)['hostname'] apiResponse = icontrol_get(standby['mgmt_ip'],standby['username'],standby['password'],'/sys/global-settings/') standby['hostname'] = json.loads(apiResponse)['hostname'] apiResponse = icontrol_get(primary['mgmt_ip'],primary['username'],primary['password'],'/cm/device/') primary['cm_devices'] = json.loads(apiResponse)['items'] for current_cm_device in primary['cm_devices']: if current_cm_device['selfDevice'] == 'true': primary['cm_properties'] = current_cm_device primary['cm_name'] = current_cm_device['name'] primary['sw_version'] = current_cm_device['version'] primary['sw_build'] = current_cm_device['build'] apiResponse = icontrol_get(standby['mgmt_ip'],standby['username'],standby['password'],'/cm/device/') standby['cm_devices'] = json.loads(apiResponse)['items'] for current_cm_device in standby['cm_devices']: if current_cm_device['selfDevice'] == 'true': standby['cm_properties'] = current_cm_device standby['cm_name'] = current_cm_device['name'] standby['sw_version'] = current_cm_device['version'] standby['sw_build'] = current_cm_device['build'] # # Verify that the TMOS versions match between nodes # if not (primary['sw_version'] == standby['sw_version']) and (primary['sw_build'] == standby['sw_build']): abort_script('Software mismatch on cluster members!') # # Verify that the CM device has been configured from default # if primary['cm_name'] == 'bigip1' or standby['cm_name'] == 'bigip1': abort_script('A cluster member was found to have the default CM device name. Please run the configuration script.') # # Verify NTP is configured # print('Checking for NTP server configuration on primary') apiResponse = icontrol_get(primary['mgmt_ip'],primary['username'],primary['password'],'/sys/ntp') if 'servers' in apiResponse: primary['ntp_servers'] = json.loads(apiResponse)['servers'] primary['timezone'] = json.loads(apiResponse)['timezone'] print(' Success!') else: abort_script('NTP not configured on cluster member!') print('Checking for NTP server configuration on secondary') apiResponse = icontrol_get(standby['mgmt_ip'],standby['username'],standby['password'],'/sys/ntp') if 'servers' in apiResponse: standby['ntp_servers'] = json.loads(apiResponse)['servers'] standby['timezone'] = json.loads(apiResponse)['timezone'] print(' Success!') else: abort_script('NTP not configured on cluster member!') # # Verify host clocks are in sync # print('Verifying device clock sync') apiResponse = icontrol_get(primary['mgmt_ip'],primary['username'],primary['password'],'/sys/clock') primary['clock_timestamp']=json.loads(apiResponse)['entries']['https://localhost/mgmt/tm/sys/clock/0']['nestedStats']['entries']['fullDate']['description'][:16] apiResponse = icontrol_get(standby['mgmt_ip'],standby['username'],standby['password'],'/sys/clock') standby['clock_timestamp'] = json.loads(apiResponse)['entries']['https://localhost/mgmt/tm/sys/clock/0']['nestedStats']['entries']['fullDate']['description'][:16] if primary['clock_timestamp'] != standby['clock_timestamp']: abort_script('Device clocks are out of sync!') else: print(' Success!') # # Show details about each node and ask user for final verification to continue # print('\nPrimary details') print(' - Mgmt IP: ' + primary['mgmt_ip']) print(' - Hostname: ' + primary['hostname']) print(' - Cluster Device Name: ' + primary['cm_name']) print(' - Version ' + primary['sw_version'] + ', build ' + primary['sw_build']) print(' - Time zone: ' + primary['timezone']) print(' - NTP servers: ' + str(primary['ntp_servers'])) print('\nSecondary details') print(' - Mgmt IP: ' + standby['mgmt_ip']) print(' - Hostname: ' + standby['hostname']) print(' - Cluster Device Name: ' + standby['cm_name']) print(' - Version ' + standby['sw_version'] + ', build ' + standby['sw_build']) print(' - Time zone: ' + standby['timezone']) print(' - NTP servers: ' + str(standby['ntp_servers'])) print('\nThis is the last chance to abort before issuing the clustering commands!\n') confirmation = input('Enter YES to continue ') if not confirmation == 'YES': abort_script('User cancelled at final confirmation!') ####################### # Clustering Commands ####################### # # Save the config on each device # print('Saving the device configuration on the primary') save_result = icontrol_save_config(primary['mgmt_ip'],primary['username'],primary['password']) if '"kind"' in save_result: print(" Configuration saved successfully.") else: abort_script("Configuration could not be successfully saved! " + save_result) print('Saving the device configuration on the secondary') save_result = icontrol_save_config(standby['mgmt_ip'],standby['username'],standby['password']) if '"kind"' in save_result: print(" Configuration saved successfully.") else: abort_script("Configuration could not be successfully saved! " + save_result) # # Save a pre-clustering UCS on each box for rollback # ucs_filename = 'pre-cluster-script-{:%Y-%m-%d-%H%M%S}.ucs'.format(datetime.datetime.utcnow()) print('Saving UCS to /var/local/ucs/' + ucs_filename + ' on primary device') ucs_create_result = icontrol_post(primary['mgmt_ip'],primary['username'],primary['password'],'/sys/ucs',{'command':'save','name':ucs_filename}) print('Saving UCS to /var/local/ucs/' + ucs_filename + ' on secondary device') ucs_create_result = icontrol_post(standby['mgmt_ip'],standby['username'],standby['password'],'/sys/ucs',{'command':'save','name':ucs_filename}) # # Add the secondary to the primary as a peer # print('Adding the nodes to the trust domain') apiResponse = icontrol_post(primary['mgmt_ip'],primary['username'],primary['password'],'/cm/add-to-trust',{ 'command':'run', 'name':'Root', 'caDevice':True, 'device':standby['mgmt_ip'], 'deviceName':standby['cm_name'], 'username':standby['username'], 'password':standby['password'] }) if '"kind":"tm:cm:add-to-trust:runstate"' in apiResponse: print(' Command issued successfully but clustering must be verified!') else: abort_script('Error! ' + apiResponse) # # Verify status of members # print('Verifying trust sync status on the primary') apiResponse = icontrol_get(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/sync-status') if '"mode":{"description":"trust-only"}' in apiResponse: print(' Devices are in sync!') else: print('Sleeping 60 seconds to allow devices to discover and sync') time.sleep(60) apiResponse = icontrol_get(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/sync-status') if '"mode":{"description":"trust-only"}' in apiResponse: print(' Devices are in sync!') else: abort_script('Error! Devices not syncing! ' + apiResponse) print('Verifying trust sync status on the secondary') apiResponse = icontrol_get(standby['mgmt_ip'], standby['username'], standby['password'], '/cm/sync-status') if '"mode":{"description":"trust-only"}' in apiResponse: print(' Devices are in sync!') else: print('Sleeping 60 seconds to allow devices to discover and sync') time.sleep(60) apiResponse = icontrol_get(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/sync-status') if '"mode":{"description":"trust-only"}' in apiResponse: print(' Devices are in sync!') else: abort_script('Error! Devices not syncing!' + apiResponse) # # Create the device group # print('Creating the ' + cluster_group_name + ' device group') apiResponse = icontrol_post(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/device-group',{ 'name':cluster_group_name, 'type':'sync-failover' }) if '"kind":"tm:cm:device-group:device-groupstate"' in apiResponse: print(' Success!') elif '"01020066:3:' in apiResponse: print(' WARNING! Device group already created! Ignoring and continuing script execution!') else: abort_script('Error! ' + apiResponse) # # Add both devices to the device group # print('Adding the primary node to the device group') apiResponse = icontrol_post(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/device-group/' + cluster_group_name + '/devices',{ 'name':primary['cm_name'] }) if '"kind":"tm:cm:device-group:devices:devicesstate"' in apiResponse: print(' Success!') elif '"code":409,"message":"01020037:3:' in apiResponse: print(' WARNING! Device is already a member of the device group! Ignoring and continuing script execution!') else: abort_script('Error! ' + apiResponse) print('Adding the secondary node to the device group') apiResponse = icontrol_post(standby['mgmt_ip'], standby['username'], standby['password'], '/cm/device-group/' + cluster_group_name + '/devices',{ 'name':standby['cm_name'] }) if '"kind":"tm:cm:device-group:devices:devicesstate"' in apiResponse: print(' Success!') elif '"code":409,"message":"01020037:3:' in apiResponse: print(' WARNING! Device is already a member of the device group! Ignoring and continuing script execution!') else: abort_script('Error! ' + apiResponse) # # Enable auto-sync on both members # print('Enabling auto-sync on the primary member') apiResponse = icontrol_patch(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/device-group/' + cluster_group_name,{ 'autoSync':'enabled' }) if '"kind":"tm:cm:device-group:device-groupstate"' and '"autoSync":"enabled"' in apiResponse: print(' Success!') else: abort_script('Error! ' + apiResponse) print('Enabling auto-sync on the secondary member') apiResponse = icontrol_patch(standby['mgmt_ip'], standby['username'], standby['password'], '/cm/device-group/' + cluster_group_name,{ 'autoSync':'enabled' }) if '"kind":"tm:cm:device-group:device-groupstate"' and '"autoSync":"enabled"' in apiResponse: print(' Success!') else: abort_script('Error! ' + apiResponse) # # Save the config on each device # print('Saving the device configuration on the primary') save_result = icontrol_save_config(primary['mgmt_ip'],primary['username'],primary['password']) if '"kind"' in save_result: print(" Configuration saved successfully.") else: abort_script("Configuration could not be successfully saved! " + save_result) print('Saving the device configuration on the secondary') save_result = icontrol_save_config(standby['mgmt_ip'],standby['username'],standby['password']) if '"kind"' in save_result: print(" Configuration saved successfully.") else: abort_script("Configuration could not be successfully saved! " + save_result) ############################### # Post-clustering steps ############################### # # Force the initial sync # print('Forcing configuration sync between peers') apiResponse = icontrol_post(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/config-sync',{ 'command':'run', 'utilCmdArgs':'to-group ' + cluster_group_name }) if '"kind":"tm:cm:config-sync:runstate"' in apiResponse: print(' Command issued successfully but sync status must be verified!') else: abort_script('Error! ' + apiResponse) # # Verify sync status of members # print('Verifying trust sync status on the primary') apiResponse = icontrol_get(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/sync-status') if '"description":"failover-bigip (In Sync): All devices in the device group are in sync"': print(' Success!') else: print('Sleeping 30 seconds to allow devices to fully sync') time.sleep(30) if '"description":"failover-bigip (In Sync): All devices in the device group are in sync"': print(' Devices are in sync!') else: abort_script('Error! Devices not syncing!') print('Verifying trust sync status on the primary') apiResponse = icontrol_get(primary['mgmt_ip'], primary['username'], primary['password'], '/cm/sync-status') if '"description":"failover-bigip (In Sync): All devices in the device group are in sync"': print(' Success!') else: print('Sleeping 30 seconds to allow devices to fully sync') time.sleep(30) if '"description":"failover-bigip (In Sync): All devices in the device group are in sync"': print(' Devices are in sync!') else: abort_script('Error! Devices not syncing!') # # Force the primary to active state, if necessary # print('Foricng the primary node to active status') apiResponse = icontrol_post(standby['mgmt_ip'], standby['username'], standby['password'], '/sys/failover',{ 'command':'run', 'standby':True, 'traffic-group':'traffic-group-1' }) if '"kind":"tm:sys:failover:runstate"' in apiResponse: print(' Success!') else: abort_script('Error! ' + apiResponse) ################ # End of Script ################ print('\nEnd of execution. Script has completed.') SystemExit()
Tested this on version:
12.1Published Aug 13, 2019
Version 1.0G-Rob
Employee
Joined May 16, 2019
G-Rob
Employee
Joined May 16, 2019
No CommentsBe the first to comment