rekey license pool
1 TopicCLI tool for working with BIG-IQ Regkey license pools
Problem this snippet solves: A BIG-IQ license manager can import base regkeys into a regkey pool and activate them. It cannot import addon keys from a csv along with the base regkey or use a csv to install addon keys to previously Activated offerings. Doing a large number of these one at a time in the GUI can be time-consuming. This script makes it possible to feed the keys as arguments when running the script. It only installs one license at a time but makes it possible to loop over a file(s) to automate the process. This script can be used to install base regkeys with or without the addonkeys. It can also install addon keys to an existing offering. It cannot be run locally on a BIG-IQ because some modules are not available for import. How to use this snippet: ./reg_pool_tool.py -h usage: reg_pool_tool.py [-h] [-d] [-v] [-a ADDRESS] [-u USERNAME] [-p PASSWORD] [-l] [-o POOL_UUID] [-r REG_KEY] [-A ADD_ON_KEY_LIST] [-i INSTALL_POOL_UUID] [-m MODIFY_POOL_UUID] This is a tool for workingwith regkey pool on BIG-IQ optional arguments: -h, --help show this help message and exit -d, --debug enable debug -v, --verbose enable verbose for options that have it -a ADDRESS, --address ADDRESS IP address of the target host -u USERNAME, --username USERNAME username for auth to host -p PASSWORD, --password PASSWORD password for auth to host -l, --list-pools list the UUIDs for existing regkey pools, requires no args -o POOL_UUID, --offerings POOL_UUID take UUID of pool as arg and list the offerings for a pool use -v to also show the active modules -r REG_KEY, --regkey REG_KEY takes and stores the regkey for use in other options -A ADD_ON_KEY_LIST, --add-on-keys ADD_ON_KEY_LIST takes string of comma sep addon keys for use by other options -i INSTALL_POOL_UUID, --install-offering INSTALL_POOL_UUID takes pool UUID as arg and installs new offering,requires -r, -A can be used to install addon keys atthe same time -m MODIFY_POOL_UUID, --modify-offering-addons MODIFY_POOL_UUID takes pool UUID as arg and installs addon to offering,requires -A [addon_key_list] and -r reg_key Examples: List regkey pools: ./reg_pool_tool.py -a 192.0.44 -l List the offerings in a regkey pool: ./reg_pool_tool.py -a 192.0.2.44 -o 07c77c7f-3170-4b17-93de-31e4f39e4709 Installing a new regkey with addon: ./reg_pool_tool.py -a 192.0.2.44 -i 5678d528-daac-4430-a87e-b67b87acfe20 -r B6083-41023-70161-19033-9429393 -A -A F659828-1850547 Modifying an existing offering adding a new addon key: ./reg_pool_tool.py -a 192.0.2.44 -m 5678d528-daac-4430-a87e-b67b87acfe20 -r K1260-99690-76028-13047-3993585 -A J984388-8738340 Code : #!/usr/bin/env python """ reg_pool_tool.py Python Version: 2.7.15 """ from __future__ import print_function import argparse from base64 import b64encode import json #from pprint import pprint import sys import time import urllib3 import requests ### Arguments parsing section ### def cmd_args(): """Handles command line arguments given.""" parser = argparse.ArgumentParser(description='This is a tool for working' 'with regkey pool on BIG-IQ') parser.add_argument('-d', '--debug', action="store_true", default=False, help='enable debug') parser.add_argument('-v', '--verbose', action="store_true", default=False, help='enable verbose for options that have it') parser.add_argument('-a', '--address', action="store", dest="address", help='IP address of the target host') parser.add_argument('-u', '--username', action="store", dest="username", default='admin', help='username for auth to host') parser.add_argument('-p', '--password', action="store", dest="password", default='admin', help='password for auth to host') parser.add_argument('-l', '--list-pools', action="store_true", default=False, help='list the UUIDs for existing regkey pools, requires no args') parser.add_argument('-o', '--offerings', action="store", dest="pool_uuid", help='take UUID of pool as arg and list the offerings for a pool' ' use -v to also show the active modules') parser.add_argument('-r', '--regkey', action="store", dest="reg_key", help='takes and stores the regkey for use in other options') parser.add_argument('-A', '--add-on-keys', action="store", dest="add_on_key_list", help='takes string of comma sep addon keys for use by other options') parser.add_argument('-i', '--install-offering', action="store", dest="install_pool_uuid", help='takes pool UUID as arg and installs new offering,' 'requires -r, -A can be used to install addon keys at' 'the same time') parser.add_argument('-m', '--modify-offering-addons', action="store", dest="modify_pool_uuid", help='takes pool UUID as arg and installs addon to offering,' 'requires -A [addon_key_list] and -r reg_key') parsed_arguments = parser.parse_args() # debug set print parser info if parsed_arguments.debug is True: print(parsed_arguments) # required args here if parsed_arguments.address is None: parser.error('-a target address is required, ' 'use mgmt for local') if parsed_arguments.install_pool_uuid: if parsed_arguments.reg_key is None: parser.error('-i requires -r') if parsed_arguments.modify_pool_uuid: if parsed_arguments.add_on_key_list is None: parser.error('-m requires -A and -r') elif parsed_arguments.reg_key is None: parser.error('-m requires -A and -r') return parsed_arguments ### END ARGPARSE SECTION ### def get_auth_token(address, user, password, uri='/mgmt/shared/authn/login'): # -> unicode """Get and auth token( to be used but other requests""" credentials_list = [user, ":", password] credentials = ''.join(credentials_list) user_and_pass = b64encode(credentials).decode("ascii") headers = {'Authorization':'Basic {}'.format(user_and_pass), 'Content-Type':'application/json'} post_data = '{"username":"' + user + '","password":"' + password +'"}' url_list = ['https://', address, uri] url = ''.join(url_list) try: request_result = requests.post(url, headers=headers, data=post_data, verify=False) except requests.exceptions.ConnectionError as connection_error: print(connection_error) sys.exit(1) except requests.exceptions.RequestException as request_exception: print(request_exception) sys.exit(1) #returns an instance of unicode that is an auth token with 300 dec timeout return request_result.json()['token']['token'] def get(url, auth_token, debug=False, return_encoding='json'): """Generic GET function. The return_encoding can be:'text', 'json', 'content'(binary), or raw """ headers = {'X-F5-Auth-Token':'{}'.format(auth_token), 'Content-Type':'application/json'} get_result = requests.get(url, headers=headers, verify=False) if debug is True: print('GET request...') print('get_result.encoding: {}'.format(get_result.encoding)) print('get_result.status_code: {}'.format(get_result.status_code)) print('get_result.raise_for_status: {}'.format( get_result.raise_for_status())) if return_encoding == 'json': return get_result.json() elif return_encoding == 'text': return get_result.text elif return_encoding == 'content': return get_result.content elif return_encoding == 'raw': return get_result.raw() # requires 'stream=True' in request def post(url, auth_token, post_data, debug): """ generic POST function """ headers = {'X-F5-Auth-Token':'{}'.format(auth_token), 'Content-Type':'application/json'} #post_data = '{"key":"value"}' try: post_result = requests.post(url, post_data, headers=headers, verify=False, timeout=10) except requests.exceptions.ConnectionError as connection_error: print ("Error Connecting: {}".format(connection_error)) sys.exit(1) except requests.exceptions.RequestException as request_exception: print(request_exception) sys.exit(1) if debug is True: print('POST request...') print('post_result.encoding: {}'.format(post_result.encoding)) print('post_result.status_code: {}'.format(post_result.status_code)) print('post_result.raise_for_status: {}'.format( post_result.raise_for_status())) return post_result.json() def patch(url, auth_token, patch_data, debug): """generic PATCH function""" headers = {'X-F5-Auth-Token':'{}'.format(auth_token), 'Content-Type':'application/json'} #patch_data = '{"key":"value"}' try: patch_result = requests.patch(url, patch_data, headers=headers, verify=False, timeout=10) except requests.exceptions.ConnectionError as connection_error: print ("Error Connecting: {}".format(connection_error)) sys.exit(1) except requests.exceptions.RequestException as request_exception: print(request_exception) sys.exit(1) if debug is True: print('PATCH request...') print('patch_result.encoding: {}'.format(patch_result.encoding)) print('patch_result.status_code: {}'.format(patch_result.status_code)) print('patch_result.raise_for_status: {}'.format( patch_result.raise_for_status())) return patch_result.json() class RegPool: """work with regkey pools""" def __init__(self, bigiq_address, username, password, debug=False): self.address = bigiq_address self.user = username self.password = password self.token = get_auth_token(self.address, self.user, self.password, uri='/mgmt/shared/authn/login') self.debug = debug def list_pool(self): """ Lists existing regkey pools """ uri = '/mgmt/cm/device/licensing/pool/regkey/licenses' url_list = ['https://', self.address, uri] url = ''.join(url_list) pool_list_result = get(url, self.token, self.debug, return_encoding='json') # create a list of list [[ , ]] pool_list = [] for entry in pool_list_result['items']: pool_list.append([entry['id'], entry['name']]) return pool_list def list_offereings(self, regkey_pool_uuid): """Returns a list of offerings for the regkey pool UUID given""" url_list = ['https://', self.address, '/mgmt/cm/device/licensing/pool/regkey/licenses/', regkey_pool_uuid, '/offerings'] url = ''.join(url_list) offering_get_result = get(url, self.token, self.debug, return_encoding='json') offering_list_result = offering_get_result['items'] # returns list of dictionaries of offerings return offering_list_result def install_offering(self, regkey_pool_uuid, new_regkey, add_on_keys): """ :type regkey_pool_uuid: str :type new_regkey: str :type add_on_keys: str comma sep keys This fucntion installs a new base regkey and optional addon keys and installs, and attempts to activate. All status in printed by the function and there is no return statement. If it fails it will show that was well. """ uri = '/mgmt/cm/device/licensing/pool/regkey/licenses/' url_list = ['https://', self.address, uri, regkey_pool_uuid, '/offerings/'] url = ''.join(url_list) if add_on_keys: post_dict = { "regKey": new_regkey, "status": "ACTIVATING_AUTOMATIC", "addOnKeys": add_on_keys.split(','), "description" : "" } else: post_dict = { "regKey": new_regkey, "status": "ACTIVATING_AUTOMATIC", "description" : "" } # format dict to make sure it is json compliant payload = json.dumps(post_dict) try: post(url, self.token, payload, self.debug) print('\nSent base regkey {} to License server status:'.format(new_regkey)) except: print('Post to License server failed') raise # poll for "eulaText" poll_result = {} attempt = 0 # keep track of tries and give up exit script after 10 uri = '/mgmt/cm/device/licensing/pool/regkey/licenses/' url_list = ['https://', self.address, uri, regkey_pool_uuid, '/offerings/', new_regkey] url = ''.join(url_list) while "eulaText" not in poll_result.keys(): try: poll_result = get(url, self.token, self.debug, return_encoding='json') print('\npoll {} for {}'.format(attempt +1, new_regkey)) if "fail" in poll_result['message']: sys.exit(poll_result['message']) print(poll_result['status']) print(poll_result['message']) time.sleep(5) except: print('Poll for eula failed for regkey {}'.format(new_regkey)) raise attempt += 1 if attempt == 5: sys.exit('Giving up after 5 tries to poll for EULA for RegKey') print('Finished polling...') # since we have eula back we need to patch back the eula # update "status" in dict poll_result["status"] = "ACTIVATING_AUTOMATIC_EULA_ACCEPTED" uri = '/mgmt/cm/device/licensing/pool/regkey/licenses/' url_list = ['https://', self.address, uri, regkey_pool_uuid, '/offerings/', new_regkey] url = ''.join(url_list) patch_dict = {"status":poll_result['status'], "eulaText": poll_result['eulaText']} patch_payload = json.dumps(patch_dict) print('sending PATCH to accept EULA for {}'.format(new_regkey)) try: patch_result = patch(url, self.token, patch_payload, self.debug) print('{} for {}'.format(patch_result['message'], new_regkey)) print(patch_result.get('status', 'ERROR: Status Not found in path_result')) except: raise def modify_offering_addon(self, regkey_pool_uuid, new_regkey, add_on_keys): """ :type regkey_pool_uuid: str :type new_regkey: str :type add_on_keys: str comma sep keys Install addon keys to t previously installed Offereing """ uri = '/mgmt/cm/device/licensing/pool/regkey/licenses/' url_list = ['https://', self.address, uri, regkey_pool_uuid, '/offerings/', new_regkey] url = ''.join(url_list) patch_dict = {"status": "ACTIVATING_AUTOMATIC", "addOnKeys": add_on_keys.split(',')} payload = json.dumps(patch_dict) try: patch(url, self.token, payload, self.debug) print('\nAdding {} addons for offering {} to License server status:'.format( add_on_keys.split(','), new_regkey)) except: print('Post to License server failed') raise # poll for "eulaText" poll_result = {} attempt = 0 # keep track of tries and give up exit script after 10 uri = '/mgmt/cm/device/licensing/pool/regkey/licenses/' url_list = ['https://', self.address, uri, regkey_pool_uuid, '/offerings/', new_regkey] url = ''.join(url_list) while not poll_result.get('status'): try: poll_result = get(url, self.token, self.debug, return_encoding='json') print('\npoll {} for {}, Addons: {}'.format( attempt +1, new_regkey, add_on_keys.split(','))) if "fail" in poll_result['message']: sys.exit(poll_result['message']) print(poll_result['status']) print(poll_result['message']) time.sleep(5) except: print('Poll for eula failed for regkey {}'.format(new_regkey)) raise attempt += 1 if attempt == 5: sys.exit('Giving up after 5 tries to poll for EULA for RegKey') print('Reactivation complete') print(poll_result.get('status')) print('Finished polling...') if __name__ == "__main__": SCRIPT_NAME = sys.argv[0] # suppress ssl warning when disbling ssl verification with verify=False urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) OPT = cmd_args() # STATIC GLOBALS ADDRESS = OPT.address RG_OBJECT = RegPool(OPT.address, OPT.username, OPT.password, OPT.debug) # -l if OPT.list_pools: LICENSE_LIST = RG_OBJECT.list_pool for pool in LICENSE_LIST(): print('{} {}'.format(pool[0], pool[1])) # -o, if -v included will also show moodules if OPT.pool_uuid: POOL_OFFERINGS = RG_OBJECT.list_offereings(OPT.pool_uuid) print('{0:35} {1:20} {2:10}'.format('RegKey', 'Status', 'addOnKeys')) print(73 * '-') for offering in POOL_OFFERINGS: if 'addOnKeys' in offering: print('{0:35} {1:20} {2:10}'.format(offering['regKey'], offering['status'], 'YES')) # if verbose given list Active modules if OPT.verbose: active_modules = offering.get('licenseText', 'Not available').splitlines() for line in active_modules: if line.startswith('active module'): print(' {} '.format(line[:80])) else: # -v not given list without active module info print('{0:35} {1:20} {2:10}'.format(offering['regKey'], offering['status'], offering.get('addOnKeys'))) # -i install new offereing with or without an addon keys, requires -r if OPT.install_pool_uuid: RG_OBJECT.install_offering(OPT.install_pool_uuid, OPT.reg_key, OPT.add_on_key_list) # -m requires -r -A if OPT.modify_pool_uuid: RG_OBJECT.modify_offering_addon(OPT.modify_pool_uuid, OPT.reg_key, OPT.add_on_key_list)443Views0likes0Comments