diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-05-11 09:04:53 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-05-13 09:34:21 +0000 |
commit | 47553c43d71b7b1144f912ab9679f5b60e858fa2 (patch) | |
tree | 08378beaeeea8f9bb2686d3037c7b6f5062bb948 /docs/labs/lab06-provisioning/mlag_issu.py | |
parent | Initial commit. (diff) | |
download | cvprac-upstream/1.3.1+dfsg.tar.xz cvprac-upstream/1.3.1+dfsg.zip |
Adding upstream version 1.3.1+dfsg.upstream/1.3.1+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | docs/labs/lab06-provisioning/mlag_issu.py | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/docs/labs/lab06-provisioning/mlag_issu.py b/docs/labs/lab06-provisioning/mlag_issu.py new file mode 100644 index 0000000..307d418 --- /dev/null +++ b/docs/labs/lab06-provisioning/mlag_issu.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# +# python3 mlag_issu <upgrade inventory file> <MLAG peer to upgrade: 'peer1' or 'peer2'>" +# +# # Example of upgrade inventory file (YAML) +# cvp_hosts: +# - 192.168.0.191 +# - 192.168.0.192 +# - 192.168.0.193 +# cvp_username: cvpadmin +# target_eos_version: 4.25.4M +# target_terminattr_version: 1.13.6 +# mlag_couples: +# - peer1: leaf101-1 +# peer2: leaf101-2 +# - peer1: leaf102-1 +# peer2: leaf102-2 +# +# Note: upgrades are performed in parallel + +import sys +import time +import string +import random +from getpass import getpass +import requests +from requests.packages.urllib3.exceptions import InsecureRequestWarning +from datetime import datetime +from cvprac.cvp_client import CvpClient +from cvprac.cvp_client_errors import CvpLoginError, CvpApiError +from pprint import pprint +from operator import itemgetter +import yaml + +class CvpDeviceUpgrader(object): + def __init__(self, hosts, username, password): + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + self.cvp_hosts = hosts + self.cvp_user = username + self.cvp_password = password + self.session = self._open_cvp_session() + + def _open_cvp_session(self): + try: + client = CvpClient() + client.connect( + nodes=self.cvp_hosts, + username=self.cvp_user, + password=self.cvp_password, + request_timeout=300, + connect_timeout=30 + ) + return(client) + except CvpLoginError as e: + print(f"Cannot connect to CVP API: {e}") + exit() + + def create_mlag_issu_change_control(self, taskIDs, deviceIDs): + cc_id = f"CC_{datetime.now().strftime('%Y%m%d_%H%M%S')}" + pre_upgrade_stage = {'stage': [{ + 'id': f"preU_{cc_id}", + 'name': 'pre_upgrade', + 'stage_row':[{'stage': [{ + 'id': ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(9)), + 'action': { + 'name': 'mlaghealthcheck', + 'timeout': 0, + 'args': { + 'DeviceID': device_id + } + } + } for device_id in deviceIDs]}] + }]} + upgrade_stage = {'stage': [{ + 'id': f"U_{cc_id}", + 'name': 'upgrade', + 'stage_row': [{'stage': [{ + 'id': task_id, + 'action': { + 'name': 'task', + 'args': { + 'TaskID': task_id + } + } + } for task_id in taskIDs]}] + }]} + post_upgrade_stage = {'stage': [{ + 'id': f"postU_{cc_id}", + 'name': 'post_upgrade', + 'stage_row': [{'stage': [{ + 'id': ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(9)), + 'action': { + 'name': 'mlaghealthcheck', + 'timeout': 0, + 'args': { + 'DeviceID': device_id + } + } + } for device_id in deviceIDs]}] + }]} + cc_data = {'config': { + 'id': cc_id, + 'name': f"Change Control {cc_id}", + 'root_stage': { + 'id': 'root', + 'name': f"Change Control {cc_id} root", + 'stage_row': [pre_upgrade_stage, upgrade_stage, post_upgrade_stage], + } + }} + try: + res = self.session.post('/api/v3/services/ccapi.ChangeControl/Update', + data=cc_data, + timeout=self.session.api.request_timeout + ) + except Exception as e: + print(str(e)) + return(None) + print(f"Change control {res[0]['id']} created at {res[0]['update_timestamp']}") + return(res[0]['id']) + + def get_mlag_issu_change_control_logs(self, ccID, startTime): + end_time = int(time.time() * 1000) + cc_logs_data = {'category': 'ChangeControl', + 'objectKey': ccID, + 'dataSize': 15000, + 'startTime': startTime, + 'endTime': end_time + } + logs = self.session.post('/cvpservice/audit/getLogs.do', + data=cc_logs_data, + timeout=self.session.api.request_timeout + ) + for log in sorted(logs['data'], key=itemgetter('dateTimeInLongFormat')): + if log['subObjectName'] and 'Command(s)' not in log['activity']: + log_date = datetime.fromtimestamp(log['dateTimeInLongFormat']/1000) + print(f"{log_date} {log['subObjectName']}: {log['activity']}") + return(end_time + 1) + + def run_mlag_issu_change_control(self, ccID): + print(f"Automatic approval of change control {ccID}") + self.session.api.approve_change_control(ccID, datetime.utcnow().isoformat() + 'Z') + time.sleep(2) + print(f"Starting the execution of change control {ccID}") + start_time = int(time.time() * 1000) + self.session.api.execute_change_controls([ccID]) + time.sleep(2) + cc_status = self.session.api.get_change_control_status(ccID)[0]['status'] + start_time = self.get_mlag_issu_change_control_logs(ccID, start_time) + while cc_status['state'] == 'Running': + time.sleep(30) + cc_status = self.session.api.get_change_control_status(ccID)[0]['status'] + start_time = self.get_mlag_issu_change_control_logs(ccID, start_time) + print(f"Change control {ccID} final status: {cc_status['state']}") + if cc_status['error']: + print(f"Change control {ccID} had the following errors: {cc_status['error']}") + else: + print(f"Change control {ccID} completed without errors") + +def main(): + if len(sys.argv) != 3: + print(f"Usage: python3 {sys.argv[0]} <input file path> <MLAG peer to upgrade: peer1/peer2>") + exit() + try: + with open(sys.argv[1], 'r') as yf: + params = yaml.safe_load(yf) + except Exception as e: + print(e) + exit() + cvp_password = getpass(prompt=f"CVP password for user {params['cvp_username']}: ") + cvpdu = CvpDeviceUpgrader( + hosts=params['cvp_hosts'], + username=params['cvp_username'], + password=cvp_password + ) + image_bundle = None + for bundle in cvpdu.session.api.get_image_bundles()['data']: + eos_match = False + terminattr_match = False + for img in bundle['imageIds']: + if params['target_eos_version'] in img: + eos_match = True + elif params['target_terminattr_version'] in img: + terminattr_match = True + if eos_match and terminattr_match: + image_bundle = bundle + break + if image_bundle is None: + print(f"Cannot find an image bundle with EOS {params['target_eos_version']} and TerminAttr {params['target_terminattr_version']}") + exit() + hostnames = [couple[sys.argv[2]] for couple in params['mlag_couples']] + devices_to_upgrade = list() + inventory = cvpdu.session.api.get_inventory() + for hostname in hostnames: + provisioned = False + for dev in inventory: + if dev['hostname'] == hostname: + provisioned = True + devices_to_upgrade.append(dev) + break + if not provisioned: + print(f"Device with hostname {hostname} is not provisioned in CVP") + if not devices_to_upgrade: + print('none of the mentioned devices is provisioned in CVP') + exit() + print(f"Devices to upgrade: {', '.join([dev['hostname'] for dev in devices_to_upgrade])}") + task_ids = list() + for device in devices_to_upgrade: + response = cvpdu.session.api.apply_image_to_device(image_bundle, device)['data'] + if response['status'] == 'success': + task_ids.extend(response['taskIds']) + device_ids = [device['serialNumber'] for device in devices_to_upgrade] + cc_id = cvpdu.create_mlag_issu_change_control(task_ids, device_ids) + if cc_id is None: + print('Failed to create the MLAG ISSU change control') + exit() + time.sleep(2) + cvpdu.run_mlag_issu_change_control(cc_id) + +if __name__ == '__main__': + main() |