#!/bin/env python #md5sum="35cea698b0803e58b55b0480d524ffcc" # # * Copyright (c) 2015 by Cisco Systems, Inc. # * All rights reserved. # # If any changes to this script file are made, please run the below command # in bash after modifications. # The above is the (embedded) md5sum of this file taken without this line, # can be # created this way if using a bash shell: # f=poap_script.py ; cat $f | sed '/^#md5sum/d' > $f.md5 ; sed -i "s/^#md5sum=.*/#md5sum=\"$(md5sum $f.md5 | sed 's/ .*//')\"/" $f # This way this script's integrity can be checked in case you do not trust # tftp's ip checksum. This integrity check is done by /isan/bin/poap.bin). # The integrity of the files downloaded later (images, config) is checked # by downloading the corresponding file with the .md5 extension and is # done by this script itself. import os import time import signal import re import string import sys import shutil import commands import traceback import ConfigParser import syslog import glob # **** Here are all variables that parametrize this script **** # ************************************************************* # system and kickstart images, configuration: location on server (src) and target (dst) image_version = "6.1.4" n3k_image_version = "5.0.3.U5.1a.133" n5k_image_version = "6.0.2.N3.0.265" n6k_image_version = "6.0.2.N3.0.283" image_dir_src = "/vinci-tftpboot/" # part of path to remove during copy n3k_system_image_src = "n3000-uk9.%s.bin" % n3k_image_version n7k_system_image_src = "n7000-s1-dk9.%s.bin" % image_version n5k_system_image_src = "n5000-uk9.%s.bin" % n5k_image_version n6k_system_image_src = "n6000-uk9.%s.bin" % n6k_image_version n3k_kickstart_image_src = "n3000-uk9-kickstart.%s.bin" % n3k_image_version n7k_kickstart_image_src = "n7000-s1-kickstart.%s.bin" % image_version n5k_kickstart_image_src = "n5000-uk9-kickstart.%s.bin" % n5k_image_version n6k_kickstart_image_src = "n6000-uk9-kickstart.%s.bin" % n6k_image_version titanium_system_image_src = "titan-122.s" titanium_kickstart_image_src = "titan-122.k" # REPLACE below with config path on serer and destination path on switch config_path = "/vinci-tftpboot/hybrid/" destination_path = "/bootflash/" # indicates whether first config file is empty or not emptyFirstFile = 1 # indicates whether first config file is empty or not FoundTemplateConfigFile = 0 # indicates whether files are copied or not configCopied = 0 systemImageCopied = 0 kickstartImageCopied = 0 kickstartImage = 0 systemImage = 0 templateConfigCopied = 0 databaseCopied = 0 # indicates whether the final config generated is available or not generatedConfig = 0 # Note that the template file will be obtained from the DB. If none is found, # then the default global template specified below will be used. config_file_src = "poap.cfg" config_file_dst = "poap_replay.cfg" config_file_dst_tmp = "poap_replay_tmp.cfg" # Destination file name for those lines in config which starts with hardware profile portmode or hardware profile tcam config_file_dst_first = "poap_1.cfg" # Desination file name for those lines in config which does not match above criterea. config_file_dst_second = "poap_2.cfg" #Destination image directory info image_dir_dst = "bootflash:" os_image_dir_dst = "/bootflash" system_image_dst = "%s/system.img" % image_dir_dst kickstart_image_dst = "%s/kickstart.img" % image_dir_dst os_system_image_dst = "%s/system.img" % os_image_dir_dst os_kickstart_image_dst = "%s/kickstart.img" % os_image_dir_dst # the copy scheduled-config command will copy to persistent location md5sum_ext_src = "md5" # extension of file containing md5sum of the one without ext. # there is no md5sum_ext_dst because one the target it is a temp file required_space = 250000 # Required space on /bootflash (for config and kick/system images) # Protocols available to download are scp/tftp/ftp/sftp protocol="scp" # protocol to use to download images/config # Host name and user credentials username = "root" # tftp server account ftp_username = "root" # ftp server account password = "cisco123" hostname = "90.90.90.166" # vrf info vrf = "management" if os.environ.has_key('POAP_VRF'): vrf=os.environ['POAP_VRF'] # Timeout info (from biggest to smallest image, should be f(image-size, protocol)) system_timeout = 3100 kickstart_timeout = 1900 config_timeout = 320 md5sum_timeout = 120 # cable-plan.README ################################################################################ #Cable plan can be downloaded and enforced during POAP or runtime. # #To download a global cable-plan for all nodes during POAP, do the following #1. Set "cabling_file_specified = 1" #2. Set the "cabling_file_path" where the cable plan can reside. #3. copy cabling_plan.xml in the path mentioned in "cabling_file_path". # # #Note the above 3 steps just downloads the cable plan during POAP, but does not enforce it. #To enforce the cable-plan, enable using CLIs in the config templates. #>feature lldp #>feature cable-managemnet #>fabric connectivity cable-plan enforce #>fabric connectivity cable-plan import bootflash:cabling_plan_global_poap.xml # #The import/enforce sequence does not matter, imports the device specific link entries from #the global plan to enforce locally. #Note: ##### #Cable Plan can be auto-generated based on current topology, using #cli "fabric connectivity cable-plan generate" #The generated plan is created in bootflash, can be used as a template, #to modify based on the expected topology. #While global cable-plan can be downloaded and enfored on all the nodes, #a non-global or a node-specific plan can also be downloaded and enforced at the sametime. #This is particularly useful if only certain no of ports needs to be validated on a specific node. #Modify the poap_database.cfg - specify the cable_plan file name in the corresponding host node #"cable_file_to_use=leaf1.xml" #Cable plan does not have to be present on both the ends for validation. It can just be enforced #on one node so long as LLDP is running on the peer nodes. #By default, cable plan is neither downloaded nor imported/enforced during boot-time, hence #explicitly follow the steps above to enforce cabling during POAP #Reach out cable-mgmt-dev@cisco.com for any questions #zed.cisco.com/confluence/display/IETC/Cable-Plan+User+Guide ############################################################################### # cable management defines start # Cable file is optional. Set to 1 to get cable file, zero if there is no cable file cabling_file_specified = 0; # Specify the global cable file name here: This is the file name in tftpserver cabling_file_src = "cabling_plan.xml" # Specify the file name you want stored on switch here cabling_file_dst = "cabling_plan_global_poap.xml" #directory on tftpserver where cable file resides cabling_file_path = "/vinci-tftpboot/" # Dont modify the following cabling_file_copied = 0; cabling_file_timeout = system_timeout # cable management defines end # POAP can use 6 modes to obtain the config file. # - 'static' - filename is static # - 'serial_number' - switch serial number is part of the filename # if serial-number is abc, then filename is conf_abc.cfg # - 'location' - CDP neighbor of interface on which DHCPDISCOVER arrived # is part of filename # if cdp neighbor's device_id=abc and port_id=111, # then filename is conf_abc_111.cfg # - 'mac' - use the interface # (mgmt 0 interface / Single MAC address for all the # front-panel interface) MAC address to derive the configuration filename # (Example: for MAC Address 00:11:22:AA:BB:CC" the default configuration # file looked for would be conf_001122AABBCC.cfg # - 'hostname' - Use the hostname from the DHCP OFFER to derive the # configuration file name # (Example: conf_N3K-Switch-1.cfg for hostname 'N3K-Switch-1' # - 'database' - config file is obtained from a database # Note: the next line can be overwritten by command-line arg processing later config_file_type = "serial_number" # parameters passed through environment: pid="" if os.environ.has_key('POAP_PID'): pid=os.environ['POAP_PID'] serial_number=None if os.environ.has_key('POAP_SERIAL'): serial_number=os.environ['POAP_SERIAL'] cdp_interface=None if os.environ.has_key('POAP_INTF'): cdp_interface=os.environ['POAP_INTF'] # will append date/timespace into the name later log_filename = "/bootflash/poap.log" t=time.localtime() now="%d_%d_%d" % (t.tm_hour, t.tm_min, t.tm_sec) # **** end of parameters **** # ************************************************************* # ***** argv parsing and online help (for test through cli) ****** # **************************************************************** cl_cdp_interface=None # Command Line version of cdp-interface cl_serial_number=None # can overwrite the corresp. env var cl_protocol=None # can overwride the script's default cl_download_only=None # dont write boot variables def parse_args(argv, help=None): global cl_cdp_interface, cl_serial_number, cl_protocol, protocol, cl_download_only while argv: x = argv.pop(0) # not handling duplicate matches... if cmp('cdp-interface'[0:len(x)], x) == 0: try: cl_cdp_interface = argv.pop(0) except: if help: cl_cdp_interface=-1 if len(x) != len('cdp-interface') and help: cl_cdp_interface=None continue if cmp('serial-number'[0:len(x)], x) == 0: try: cl_serial_number = argv.pop(0) except: if help: cl_serial_number=-1 if len(x) != len('serial-number') and help: cl_serial_number=None continue if cmp('protocol'[0:len(x)], x) == 0: try: cl_protocol = argv.pop(0); except: if help: cl_protocol=-1 if len(x) != len('protocol') and help: cl_protocol=None if cl_protocol: protocol=cl_protocol continue if cmp('download-only'[0:len(x)], x) == 0: cl_download_only = 1 continue print "Syntax Error|invalid token:", x exit(-1) ########### display online help (if asked for) ################# nb_args = len(sys.argv) if nb_args > 1: m = re.match('__cli_script.*help', sys.argv[1]) if m: # first level help: display script description if sys.argv[1] == "__cli_script_help": print "loads system/kickstart images and config file for POAP\n" exit(0) # argument help argv = sys.argv[2:] # dont count last arg if it was partial help (no-space-question-mark) if sys.argv[1] == "__cli_script_args_help_partial": argv = argv[:-1] parse_args(argv, "help") if cl_serial_number==-1: print "WORD|Enter the serial number" exit(0) if cl_cdp_interface==-1: print "WORD|Enter the CDP interface instance" exit(0) if cl_protocol==-1: print "tftp|Use tftp for file transfer protocol" print "ftp|Use ftp for file transfer protocol" print "scp|Use scp for file transfer protocol" exit(0) if not cl_serial_number: print "serial-number|The serial number to use for the config filename" if not cl_cdp_interface: print "cdp-interface|The CDP interface to use for the config filename" if not cl_protocol: print "protocol|The file transfer protocol" if not cl_download_only: print "download-only|stop after download, dont write boot variables" print "|Run it (use static name for config file)" # we are done exit(0) # *** now overwrite env vars with command line vars (if any given) # *** this can be used for testing the script using the command line argv = sys.argv[1:] parse_args(argv) if cl_serial_number: serial_number=cl_serial_number config_file_type = "serial_number" if cl_cdp_interface: cdp_interface=cl_cdp_interface config_file_type = "location" if cl_protocol: protocol=cl_protocol # figure out what kind of box we have (to download the correct images) from cisco import cli r=cli("show version") # n3k, n5k and n6k cli returns a two element list, second one is the result string if len(r)==2: lines=r[1].split("\n") else: lines=r.split("\n") idx = [i for i, line in enumerate(lines) if re.search('^.*cisco.*Chassis.*$', line)] if re.match(".*Nexus7.*",lines[idx[0]]): box="n7k" if re.match(".*Unknown Module.*",lines[idx[0]]): box="titanium" elif re.match(".*Nexus3.*",lines[idx[0]]): box="n3k" elif re.match(".*Nexus.56.*",lines[idx[0]]) or re.match(".*Nexus56.*",lines[idx[0]]) or re.match(".*Nexus6.*",lines[idx[0]]) or re.match(".*Nexus 6.*",lines[idx[0]]) or re.match(".*Nexus.24Q.*",lines[idx[0]]) or re.match(".*Nexus24Q.*",lines[idx[0]]) or re.match(".*Nexus.48Q.*",lines[idx[0]]) or re.match(".*Nexus48Q.*",lines[idx[0]]): box="n6k" elif re.match(".*Nexus.5.*",lines[idx[0]]) or re.match(".*Nexus5.*",lines[idx[0]]): box="n5k" if re.match(".*Unknown Module.*",lines[idx[0]]): box="titanium" else: box="unknown" print "box is", box if box=="unknown": exit(-1) if box=="n7k" or box=="n5k" or box=="titanium" or box=="n6k": set = "integrated" else: set = "standalone" if set=="standalone": from cisco import transfer poap_syslog_prefix = " " # setup log file and associated utils try: if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": log_filename = "%s_usb.%s" % (log_filename, now) else: log_filename = "%s.%s" % (log_filename, now) except Exception as inst: print inst poap_log_file = open(log_filename, "w+") #String2Mac Conversion def Str2Mac (poap_syslog_mac = ""): poap_syslog_mac = "%s:%s:%s:%s:%s:%s" % (poap_syslog_mac[0:2], poap_syslog_mac[2:4], poap_syslog_mac[4:6], poap_syslog_mac[6:8], poap_syslog_mac[8:10], poap_syslog_mac[10:12]) return poap_syslog_mac # Syslog Prefix def setSyslogPrefix(): global poap_syslog_prefix, poap_syslog_mac if os.environ.has_key('POAP_SERIAL'): poap_syslog_prefix = "S/N[%s]" % os.environ['POAP_SERIAL'] if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": if os.environ.has_key('POAP_RMAC'): poap_syslog_mac = "%s" % os.environ['POAP_RMAC'] poap_syslog_prefix = "%s-MAC[%s]" % (poap_syslog_prefix, poap_syslog_mac) return if os.environ.has_key('POAP_MGMT_MAC'): poap_syslog_mac = "%s" % os.environ['POAP_MGMT_MAC'] poap_syslog_prefix = "%s-MAC[%s]" % (poap_syslog_prefix, poap_syslog_mac) return else: if os.environ.has_key('POAP_MAC'): poap_syslog_mac = "%s" % os.environ['POAP_MAC'] poap_syslog_mac = Str2Mac (poap_syslog_mac) poap_syslog_prefix = "%s-MAC[%s]" % (poap_syslog_prefix, poap_syslog_mac) return setSyslogPrefix() def poap_cleanup_script_logs() : preserve_last_logs = 4 files = [] path = destination_path for infile in glob.glob(os.path.join(path, 'poap.log*')): files.append(infile) files.sort() files.reverse() count = 0 for file in files: count = count + 1 if count > preserve_last_logs: os.remove(file) def poap_log (info): global poap_syslog_prefix info = "%s - %s" % (poap_syslog_prefix, info) syslog.syslog(9, info) poap_log_file.write(info) poap_log_file.write("\n") poap_log_file.flush() print info sys.stdout.flush() def poap_log_close (): poap_log_file.close() def abort_cleanup_exit () : poap_log("INFO: cleaning up") cleanup_files() poap_log_close() exit(-1) # some argument sanity checks: if config_file_type == "serial_number" and serial_number == None: poap_log("ERR: serial-number required (to derive config name) but none given") exit(-1) if config_file_type == "location" and cdp_interface == None: poap_log("ERR: interface required (to derive config name) but none given") exit(-1) # Get final image name based on actual box # The variable box is the box platform, like n7k, n3k, n5k, n6k. # This is to generate the kickstart or system image src variable names defined # in the beginning of the file, e.g., n3k_system_image_src # For different sup, e.g., sup1, sup2, assign the correct image name to image # src variable system_image_src = eval("%s_%s" %(box , "system_image_src"), globals()) kickstart_image_src = eval("%s_%s" %(box , "kickstart_image_src"), globals()) # images are copied to temporary location first (dont want to # overwrite good images with bad ones). system_image_dst_tmp = "%s/system.img%s" % (image_dir_dst, ".new") kickstart_image_dst_tmp = "%s/kickstart.img%s" % (image_dir_dst, ".new") # setup the cli session cli("no terminal color persist"); cli("terminal dont-ask"); if box=="n7k" or box=="n5k" or box=="titanium" or box=="n6k": cli("terminal password %s" % password); # utility functions def run_cli (cmd): poap_log("CLI : %s" % cmd) if box=="n7k" or box=="n5k" or box=="titanium" or box=="n6k": r=cli(cmd) else: r=cli(cmd)[1] return r def rm_rf (filename): try: cli("delete %s" % filename) except: pass # signal handling def sig_handler_no_exit (signum, frame) : poap_log("INFO: SIGTERM Handler while configuring boot variables") def sigterm_handler (signum, frame): poap_log("INFO: SIGTERM Handler") abort_cleanup_exit() exit(1) signal.signal(signal.SIGTERM, sigterm_handler) # Procedure to split config file using global information def splitConfigFile (): global configCopied, config_file_dst, config_file_dst_first, config_file_dst_second, emptyFirstFile configFile = open("/bootflash/%s" % config_file_dst, "r") configFile_first = open("/bootflash/%s" % config_file_dst_first, "w+") configFile_second = open("/bootflash/%s" % config_file_dst_second, "w+") line = configFile.readline() while line != "": if not string.find(line, "system vlan", 0, 11) or not string.find(line, "interface breakout", 0, 18) or not string.find(line, "hardware profile tcam", 0, 21) or not string.find(line, "type fc", 0, 7) or not string.find(line, "fabric-mode 40G", 0, 15) or not string.find(line, "fabricpath mode transit", 0, 23) or not string.find(line, "fabric-mode 10G", 0, 15): poap_log("INFO: Generating config for splitConfig..") if not string.find(line, "fabricpath mode transit", 0, 23): configFile_first.write("install feature-set fabricpath\n") configFile_first.write("feature-set fabricpath\n") configFile_first.write(line) if emptyFirstFile == 1: emptyFirstFile = 0 else: configFile_second.write(line) line = configFile.readline() configFile.close() run_cli("delete %s" % config_file_dst) configCopied = 0 configFile_first.close() if emptyFirstFile == 1: run_cli("delete %s" % config_file_dst_first) configFile_second.close() return # transfers file, return True on success; on error exits unless 'fatal' is False in which case we return False def doCopy(protocol = "", host = "", source = "", dest = "", vrf = "management", login_timeout=10, user = "", password = "", fatal=True): rm_rf(dest) # modify source paths (tftp does not like full paths) global username, ftp_username if protocol=="tftp": source=source[len(image_dir_src):] if protocol=="ftp": username=ftp_username source=source[len(image_dir_src):] if box=="n7k" or box=="n5k" or box=="titanium" or box=="n6k": if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": poap_log("INFO: Copy %s from USB" %source) cmd = "copy %s %s" % (source, dest) print cmd else: cmd="copy %s://%s@%s/%s %s vrf %s" % (protocol, username, host, source, dest, vrf) print cmd try: run_cli(cmd) except: poap_log("WARN: Copy Failed: %s" % str(sys.exc_value).strip('\n\r')) if fatal: poap_log("ERR : aborting") abort_cleanup_exit() exit(1) return False return True else: try: if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": poap_log("INFO: Copy %s from USB" %source) os.system ("copy %s %s" % (source, dest)) else: transfer(protocol, host, source, dest, vrf, login_timeout, username, password) except Exception as inst: poap_log("WARN: Copy Failed: %s" % inst) if fatal: poap_log("ERR : aborting") abort_cleanup_exit() exit(1) return False return True def get_md5sum_src (file_name): md5_file_name_src = "%s.%s" % (file_name, md5sum_ext_src) md5_file_name_dst = "volatile:%s.poap_md5" % os.path.basename(md5_file_name_src) rm_rf(md5_file_name_dst) ret=doCopy(protocol, hostname, md5_file_name_src, md5_file_name_dst, vrf, md5sum_timeout, username, password, False) if ret == True: r=run_cli("show file %s" % md5_file_name_dst) match = re.search("^md5sum=([0-9a-f]+)\s+.*", r, re.MULTILINE) if match: sum = match.group(1) else: poap_log("INFO: Unable to get the md5 sum") return None poap_log("INFO: md5sum %s (.md5 file)" % sum) rm_rf(md5_file_name_dst) return sum return None def get_md5sum_dst (filename): sum=run_cli("show file %s md5sum" % filename).strip('\n') poap_log("INFO: md5sum %s (recalculated)" % sum) return sum def check_md5sum (filename_src, filename_dst, lname): md5sum_src = get_md5sum_src(filename_src) if md5sum_src: # we found a .md5 file on the server md5sum_dst = get_md5sum_dst(filename_dst) if md5sum_dst != md5sum_src: poap_log("ERR : MD5 verification failed for %s! (%s)" % (lname, filename_dst)) abort_cleanup_exit() # Will run our CLI command to test MD5 checksum and if files are valid images # This check is also performed while setting the boot variables, but this is an # additional check def get_md5_status (msg): lines=msg.split("\n") for line in lines: index=line.find("MD5") if (index!=-1): status=line[index+17:] return status def get_version (msg): lines=msg.split("\n") for line in lines: index=line.find("MD5") if (index!=-1): status=line[index+17:] index=line.find("kickstart:") if (index!=-1): index=line.find("version") ver=line[index:] return ver index=line.find("system:") if (index!=-1): index=line.find("version") ver=line[index:] return ver def get_image_version(image): version = "Unable to retrieve" try: out = cli("show version image %s" % image) # out = out[1] # This is a Hack; once the "plugin based. failed to get image swid" # is fixed this expect part can be removed. except: out = sys.exc_value out = out[0] match = re.search("image name:\s+([a-zA-Z\.\-0-9]+)\n", out, re.MULTILINE) if match: split_image = match.group(1).split('.') version = ".".join(split_image[1:len(split_image) -1]) return version def verify_images (): if box=="n5k" or box=="n6k": # This is to fix the n5k/n6k system image bug kick_v=get_image_version(kickstart_image_dst) sys_v=get_image_version(system_image_dst) if kick_v != sys_v: poap_log("ERR : Image version mismatch. (kickstart : %s) (system : %s)" % (kick_v, sys_v)) abort_cleanup_exit() return True kick_cmd="show version image %s" % kickstart_image_dst sys_cmd="show version image %s" % system_image_dst kick_msg=run_cli(kick_cmd) sys_msg=run_cli(sys_cmd) # n3k, n6k images do not provide md5 information if box=="n7k" or box=="titanium": kick_s=get_md5_status(kick_msg) sys_s=get_md5_status(sys_msg) kick_v=get_version(kick_msg) sys_v=get_version(sys_msg) if box=="n7k" or box=="titanium": print "MD5 status: %s and %s" % (kick_s, sys_s) if (kick_s == "Passed" and sys_s == "Passed"): # MD5 verification passed if(kick_v != sys_v): poap_log("ERR : Image version mismatch. (kickstart : %s) (system : %s)" % (kick_v, sys_v)) abort_cleanup_exit() else: poap_log("ERR : MD5 verification failed!") poap_log("%s\n%s" % (kick_msg, sys_msg)) abort_cleanup_exit() poap_log("INFO: Verification passed. (kickstart : %s) (system : %s)" % (kick_v, sys_v)) return True else: if kick_v != sys_v: poap_log("ERR : Image version mismatch. (kickstart : %s) (system : %s)" % (kick_v, sys_v)) abort_cleanup_exit() return True def cleanup_files (): global configCopied, databaseCopied, templateConfigCopied, generatedConfig, kickstartImageCopied, systemImageCopied, kickstartImage, systemImage, config_db_file_dst, config_file_dst_tmp, config_file_dst_second, config_file_dst_first, kickstart_image_dst_tmp, system_image_dst_tmp, config_file_dst, kickstart_image_dst, system_image_dst try: poap_log("FINISH: Clean up files.") if configCopied == 1: run_cli("delete %s" % config_file_dst) if databaseCopied == 1: run_cli("delete %s" % config_db_file_dst) if templateConfigCopied == 1: run_cli("delete %s" % config_file_dst_tmp) if generatedConfig == 1: run_cli("delete %s" % config_file_dst_second) if emptyFirstFile == 0: run_cli("delete %s" % config_file_dst_first) if kickstartImageCopied == 1: run_cli("delete %s" % kickstart_image_dst_tmp) if kickstartImage == 1: run_cli("delete %s" % kickstart_image_dst) if systemImageCopied == 1: run_cli("delete %s" % system_image_dst_tmp) if systemImage == 1: run_cli("delete %s" % system_image_dst) except: poap_log("Error in clean up files") pass #cable management: copy cabling file if specified def get_cable_mgmt_file() : poap_log( "INFO: Entered get_cable_mgmt_file") poap_log( " found cable file: %d " % cabling_file_specified) if cabling_file_specified == 0: poap_log( "INFO: No cable file specified") return org_file = cabling_file_dst if os.path.exists("/bootflash/%s" % org_file): poap_log( "INFO: Dst cable file already exists, move it: %s" % org_file) return poap_log( "INFO: Starting Copy of cable File: %s" % cabling_file_dst ) tmp_file = "%s.tmp" % org_file time = cabling_file_timeout if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": src = "usb1:%s" % (cabling_file_src) else: src = "%s%s" % (cabling_file_path, cabling_file_src) doCopy (protocol, hostname, src, org_file, vrf, time, username, password, tmp_file) poap_log( "INFO: Completed Copy of Cable File: %s " % cabling_file_dst) cabling_file_copied = 1 # get config file from server def get_config (): global username, hostname, config_path, config_file_src, config_file_dst, config_timeout, emptyFirstFile, password, generatedConfig, configCopied poap_log("INFO:#Starting Copy of Config File") if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": config_file_src = "usb1:%s" % (config_file_src) else: config_file_src = "%s%s" % (config_path, config_file_src) doCopy (protocol, hostname, config_file_src, config_file_dst, vrf, config_timeout, username, password) poap_log("INFO: Completed Copy of Config File") configCopied = 1 # get file's md5 from server (if any) and verify it, failure is fatal (exit) poap_log("INFO:#Check md5 of Configuration File") check_md5sum (config_file_src, config_file_dst, "config file") poap_log("INFO: Split config invoked....") splitConfigFile() generatedConfig = 1 return # get system image file from server def get_system_image (): global systemImageCopied, systemImage, image_dir_src, system_image_src poap_log( "INFO:#Starting Copy of System Image") if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": system_image_src = "usb1:%s" % (system_image_src) else: system_image_src = "%s/%s" % (image_dir_src, system_image_src) doCopy (protocol, hostname, system_image_src, system_image_dst_tmp, vrf, system_timeout, username, password) poap_log("INFO: Completed Copy of System Image" ) systemImageCopied = 1 # get file's md5 from server (if any) and verify it, failure is fatal (exit) poap_log("INFO:#Check md5 of system image") check_md5sum (system_image_src, system_image_dst_tmp, "system image") if os.path.exists(os_system_image_dst): os.remove(os_system_image_dst) run_cli ("move %s %s" % (system_image_dst_tmp, system_image_dst)) systemImageCopied = 0 systemImage = 1 # get kickstart image file from server def get_kickstart_image (): global kickstartImageCopied, kickstartImage, image_dir_src, kickstart_image_src poap_log( "INFO:#Starting Copy of Kickstart Image") if os.environ.has_key('POAP_PHASE') and os.environ['POAP_PHASE'] == "USB": kickstart_image_src = "usb1:%s" % (kickstart_image_src) else: kickstart_image_src = "%s/%s" % (image_dir_src, kickstart_image_src) doCopy (protocol, hostname, kickstart_image_src, kickstart_image_dst_tmp, vrf, kickstart_timeout, username, password) poap_log("INFO: Completed Copy of Kickstart Image") kickstartImageCopied = 1 # get file's md5 from server (if any) and verify it, failure is fatal (exit) poap_log("INFO:#Check md5 of kickstart image") check_md5sum (kickstart_image_src, kickstart_image_dst_tmp, "kickstart image") if os.path.exists(os_kickstart_image_dst): os.remove(os_kickstart_image_dst) run_cli ("move %s %s" % (kickstart_image_dst_tmp, kickstart_image_dst)) kickstartImageCopied = 0 kickstartImage = 1 def wait_box_online (): while 1: r=run_cli("show system internal ascii-cfg event-history | grep BOX_ONLINE") if r: break else: time.sleep(5) poap_log("INFO: Waiting for box online...") # install (make persistent) images and config def install_it (): global cl_download_only, kickstart_image_copied, system_image_copied, kickstartImage, systemImage if cl_download_only: exit(0) timeout = -1 # make sure box is online if box=="n7k" or box=="n5k" or box=="titanium" or box=="n6k": wait_box_online() poap_log("INFO: Setting the boot variables") try: run_cli ("config terminal ; boot kickstart %s" % kickstart_image_dst) run_cli ("config terminal ; boot system %s" % system_image_dst) # System may not be ready yet to apply 'copy r s' # If thats the case, wait for sometime and retry. command_successful = False timeout = 10 # minutes first_time = time.time() endtime = first_time + timeout * 60 #sec per min retry_delay = 30 # seconds while not command_successful: new_time = time.time() try: run_cli ("copy running-config startup-config") poap_log("INFO: successful") command_successful = True except SyntaxError: poap_log("WARNING: copy run to start failed") if new_time > endtime: poap_log("ERROR: time out waiting for \"copy run start\" to complete successfully") sys.exit(-1) poap_log("WARNING: retry in 30 seconds") time.sleep( retry_delay ) if emptyFirstFile is 0: run_cli ('copy bootflash:%s scheduled-config' % config_file_dst_first) poap_log("######### Copying the first scheduled cfg done ##########") run_cli ('copy bootflash:%s scheduled-config' % config_file_dst_second) poap_log("######### Copying the second scheduled cfg done ##########") except: poap_log("ERR : setting bootvars or copy run start failed!") poap_log("ERR: msg: %s" % str(sys.exc_value).strip('\n\r')) traceback.print_exc(file=sys.stdout) sys.stdout.flush() abort_cleanup_exit() poap_log("INFO: Configuration successful") # If we are at this stage, it means that there is no error. We dont want to # delete the system/kickstart images that were downloaded kickstartImage = 0 systemImage = 0 # Verify if free space is available to download config, kickstart and system images def verify_freespace (): out=run_cli("dir bootflash:") match = re.search("^\s+([0-9]+)\s+bytes\s+free$", out, re.MULTILINE) if match: freespace=int(match.group(1))/1024 else: poap_log("ERR: Unable to get free space") poap_log_file.close() abort_cleanup_exit() poap_log("INFO: free space is %s kB" % freespace ) if required_space > freespace: poap_log("ERR : Not enough space to copy the config, kickstart image and system image, aborting!") abort_cleanup_exit() # Procedure to set config_file based on switch serial number def set_config_file_src_serial_number (): global config_file_src config_file_src = "conf_%s.cfg" % serial_number poap_log("INFO: Selected config filename (serial-nb) : %s" % config_file_src) # Procedure to set config_file based on the interface MAC def set_config_file_src_mac(): global config_file_src if os.environ.has_key('POAP_MAC'): poap_log("Interface MAC %s" % os.environ['POAP_MAC']) config_file_src = "conf_%s.cfg" %(os.environ['POAP_MAC']) poap_log("Selected config file name : %s" % config_file_src) # Procedure to set config_file based on switch host name def set_config_file_src_hostname (): global config_file_src if os.environ.has_key('POAP_HOST_NAME'): poap_log("Host Name: [%s]" % os.environ['POAP_HOST_NAME']) config_file_src = "conf_%s.cfg" % (os.environ['POAP_HOST_NAME']) poap_log("Selected config file name : %s" % config_file_src) else: poap_log("Host Name information missing, falling back to static mode\n") poap_log("Selected config file name : %s" % config_file_src) # figure out config filename to download based on cdp neighbor info # sample output: # switch# show cdp neig # Capability Codes: R - Router, T - Trans-Bridge, B - Source-Route-Bridge # S - Switch, H - Host, I - IGMP, r - Repeater, # V - VoIP-Phone, D - Remotely-Managed-Device, # s - Supports-STP-Dispute, M - Two-port Mac Relay # # Device ID Local Intrfce Hldtme Capability Platform Port ID # Switch mgmt0 148 S I WS-C2960G-24T Gig0/2 # switch(Nexus-Switch) Eth1/1 150 R S I s Nexus-Switch Eth2/1 # switch(Nexus-Switch) Eth1/2 150 R S I s Nexus-Switch Eth2/2 # in xml: # # 83886080 # Switch # mgmt0 # 137 # switch # IGMP_cnd_filtering # cisco WS-C2960G-24TC-L # GigabitEthernet0/4 # def set_config_file_src_location(): global config_file_src cmd = "show cdp neighbors interface %s" % cdp_interface try: r = run_cli(cmd); except: poap_log("ERR: cant get neighbor info on %s", cdp_interface) exit(-1) lines=r.split("\n") try: idx = [i for i, line in enumerate(lines) if re.search('^.*Device-ID.*$', line)] ix=idx[0]+1 line=lines[ix] words=line.split() # Check if there is a wrap due to long output string, which breaks one # line output into multiple lines while len(words)<6: ix=ix+1 if ix