Advertisement
Guest User

Untitled

a guest
May 21st, 2019
692
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 76.06 KB | None | 0 0
  1. #!/bin/env python
  2. #md5sum="c9ae39c710740d1847dcf49182b1a057"
  3. """
  4. If any changes are made to this script, please run the below command
  5. in bash shell to update the above md5sum. This is used for integrity check.
  6. f=poap.py ; cat $f | sed '/^#md5sum/d' > $f.md5 ; sed -i \
  7. "s/^#md5sum=.*/#md5sum=\"$(md5sum $f.md5 | sed 's/ .*//')\"/" $f
  8. """
  9.  
  10. import glob
  11. import os
  12. import pkgutil
  13. import re
  14. import shutil
  15. import signal
  16. import sys
  17. import syslog
  18. import time
  19. from time import gmtime, strftime
  20. import tarfile
  21. import errno
  22.  
  23. try:
  24. import subprocess as sp
  25. except ImportError:
  26. sp = None
  27.  
  28. try:
  29. from cisco import cli
  30. from cisco import transfer
  31. legacy = True
  32. except ImportError:
  33. from cli import *
  34. legacy = False
  35.  
  36. """
  37. Increasing the script timeout to handle the legacy nxos
  38. versions where upgrading will take time
  39. """
  40. # script_timeout=1800
  41. # --- Start of user editable settings ---
  42. # Host name and user credentials
  43. options = {
  44. "username": "root",
  45. "password": "password",
  46. "hostname": "192.168.1.5",
  47. "transfer_protocol": "tftp",
  48. "mode": "serial_number",
  49. "target_system_image": "nxos.7.0.3.I4.7.bin",
  50.  
  51. }
  52.  
  53.  
  54. def download_scripts_and_agents():
  55. """
  56. Downloads user scripts, agents, and data after downloading config, but before installing
  57. the target image. If an application is to be installed in the native Linux environment,
  58. uncomment and update the call to install_shell_script() as needed.
  59. The parameters are as follows:
  60. source_path: the server path or source path where the source file lives (e.g. /tftpboot)
  61. source_file: the name of the file on the server / source to download (e.g. agents.tar)
  62. dest_path: optional (defaults to /bootflash) parameter to specify where to download to
  63. any intermediate directories will automatically be created.
  64. dest_file: optional (defaults to same name as source) parameter to specify the target name
  65. unpack: optional (defaults to False) parameter specifying if we should attempt to extract
  66. this file. The command 'tar -xf <target> -C /bootflash' is used for extraction.
  67. delete_after_unpack: optional (defaults to False) parameter specifying that we delete
  68. the target file after successful extraction.
  69. """
  70. source_location = options["user_app_path"]
  71. # poap_log("Downloading scripts and agents from %s" % source_location)
  72.  
  73. """
  74. Download the agent "monitor_dummy.py" from options["user_app_path"] to
  75. /bootflash/monitor_agent and rename it to "monitor_agent.py"
  76. """
  77. # download_user_app(source_location, "monitor_dummy.py", "/bootflash/monitor_agent/",
  78. # "monitor_agent.py")
  79.  
  80. """
  81. Download a tarball containing several agents from /var/lib/tftpboot/agent_bundles to
  82. /bootflash/agent_root; don't rename the tarball. Then unpack this tarball
  83. (tar -xf <tarball> -C /bootflash). If the tarball contains a directory called "agents" with
  84. 3 agents inside of it, they will be unpacked to /bootflash/agents. Delete the tarball
  85. after successfully extracting the files.
  86. """
  87. # download_user_app("/var/lib/tftpboot/agent_bundles", "agent_directory.tar",
  88. # "/bootflash/agent_root", unpack=True, delete_after_unpack=True)
  89.  
  90. """
  91. Download a shell script / agent called bootflash_agent.sh from options["user_app_path"]
  92. to /bootflash. Leave it named "bootflash_agent.sh"
  93. """
  94. # download_user_app(source_location, "bootflash_agent.sh")
  95.  
  96. """
  97. Download a shell script / agent called "bootflash_agent.sh" from options["user_app_path"]
  98. to /bootflash, but rename it "different_name_agent.sh"
  99. """
  100. # download_user_app(source_location, "bootflash_agent.sh", "/bootflash",
  101. # "different_name_agent.sh")
  102.  
  103. """
  104. Install a shell script that has already been downloaded
  105. """
  106. # install_shell_script("/bootflash", "poap_install")
  107.  
  108.  
  109. def download_user_app(source_path, source_file, dest_path="/bootflash", dest_file="", unpack=False,
  110. delete_after_unpack=False):
  111. """
  112. Downloads a user application, script, or data item.
  113. source_path: the source directory the file we want to download resides in
  114. source_file: the source file to download
  115. dest_path: optional parameter specifying where we want to copy the file to
  116. dest_file: optional parameter specifying what we want to name the file after we copy it
  117. unpack: optional boolean parameter specifying if we want to attempt to extract this file
  118. delete_after_unpack: optional boolean parameter specifying if we want to delete the tarball
  119. """
  120. # If the user doesn't specify the destination filename, use the same one as the source
  121. if dest_file == "":
  122. dest_file = source_file
  123.  
  124. src = os.path.join(source_path, source_file)
  125. dst = os.path.join(dest_path, dest_file)
  126.  
  127. tmp_file = "%s.tmp" % dst
  128.  
  129. # Create intermediate directories if required for agents
  130. try:
  131. os.makedirs(dest_path)
  132. except OSError as e:
  133. if e.errno != errno.EEXIST or not os.path.isdir(dest_path):
  134. raise
  135.  
  136. do_copy(src, dst, options["timeout_copy_user"], tmp_file)
  137.  
  138. if unpack is True:
  139. poap_log("Unpacking %s to /bootflash" % dst)
  140.  
  141. unpack_success = False
  142. # Before Python 2.7 (no subprocess module at all)
  143. if sp is None:
  144. try:
  145. os.system("tar -xf %s -C /bootflash" % dst)
  146. unpack_success = True
  147. except Exception as e:
  148. poap_log("Failed to unpack %s: %s" % (dst, str(e)))
  149. else:
  150. try:
  151. out = sp.check_output("tar -xf %s -C /bootflash" % dst, shell=True,
  152. stderr=sp.STDOUT)
  153. unpack_success = True
  154. except AttributeError as e:
  155. try:
  156. os.system("tar -xf %s -C /bootflash" % dst)
  157. unpack_success = True
  158. except Exception as e:
  159. poap_log("Failed to unpack %s: %s" % (dst, str(e)))
  160. except OSError as e:
  161. poap_log("Failed to unpack %s: %s" % (dst, str(e)))
  162. except sp.CalledProcessError as e:
  163. poap_log("Failed to unpack %s: %s (%s)" % (dst, str(e), e.output))
  164.  
  165. # Now that we know we extracted the contents, cleanup the tarball
  166. if unpack_success is True and delete_after_unpack is True:
  167. remove_file(dst)
  168.  
  169.  
  170. def install_shell_script(source_path, source_file):
  171. """
  172. Install a user shell script.
  173. The users's shell script is copied to /etc/init.d, and chkconfig is run
  174. so that the user's script will get executed when the switch boots.
  175. source_path: the source directory the file we want to install resides in
  176. source_file: the source file to install
  177. """
  178.  
  179. fullpath = os.path.join(source_path, source_file)
  180.  
  181. poap_log("Installing %s" % fullpath)
  182.  
  183. try:
  184. os.system("cp %s /etc/init.d" % fullpath)
  185. except Exception as e:
  186. poap_log("Failed to copy %s: %s" % (fullpath, str(e)))
  187. else:
  188. poap_log("Copy %s to /etc/init.d succeeded" % fullpath)
  189.  
  190. for i in range (0, 10):
  191. try:
  192. os.system("/usr/sbin/chkconfig --add %s" % source_file)
  193. except Exception as e:
  194. poap_log("Failed to chkconfig add %s: %s" % (source_file, str(e)))
  195. else:
  196. poap_log("Chkconfig add %s succeeded" % source_file)
  197. if (os.system("ls /etc/rc3.d/*%s" % source_file) == 0):
  198. poap_log("Chkconfig file exists, exiting retry loop")
  199. break
  200. else:
  201. poap_log("Chkconfig file does not exist, sleeping and looping")
  202. time.sleep(2)
  203.  
  204. for i in range (0, 10):
  205. try:
  206. os.system("/usr/sbin/chkconfig --level 3 %s on" % source_file)
  207. except Exception as e:
  208. poap_log("Failed to chkconfig level 3 %s on: %s" % (source_file, str(e)))
  209. else:
  210. poap_log("Chkconfig level 3 %s on succeeded" % source_file)
  211. if (os.system("ls /etc/rc3.d/*%s" % source_file) == 0):
  212. poap_log("Chkconfig file exists, exiting retry loop")
  213. break
  214. else:
  215. poap_log("Chkconfig file does not exist, sleeping and looping")
  216. time.sleep(2)
  217.  
  218. os.system("sync")
  219. time.sleep(5)
  220.  
  221.  
  222. def set_defaults_and_validate_options():
  223. """
  224. Sets all the default values and creates needed variables for POAP to work.
  225. Also validates that the required options are provided
  226. """
  227. global options
  228.  
  229. # Initialize globals
  230. init_globals()
  231.  
  232. # Required parameters
  233. if "options" not in globals():
  234. abort("Options dictionary was not defined!")
  235.  
  236. # Check which mode we're using
  237. if options.get("mode", "") == "personality":
  238. required_parameters = set()
  239. else:
  240. required_parameters = set(["target_system_image"])
  241.  
  242. # USB doesn't require remote credentials
  243. if os.environ.get("POAP_PHASE", None) != "USB":
  244. # Remote download needs more parameters
  245. required_parameters.add("username")
  246. required_parameters.add("password")
  247. required_parameters.add("hostname")
  248.  
  249. # If we are missing any required parameters
  250. missing_parameters = required_parameters.difference(options.keys())
  251. if len(missing_parameters) != 0:
  252. poap_log("Required parameters are missing:")
  253. abort("Missing %s" % ", ".join(missing_parameters))
  254.  
  255. # Set the POAP mode
  256. set_default("mode", "serial_number")
  257.  
  258. # Required space to copy config kickstart and system image in KB
  259. set_default("required_space", 100000)
  260. # Transfer protocol (http, ftp, tftp, scp, etc.)
  261. set_default("transfer_protocol", "tftp")
  262. # Directory where the config resides
  263. set_default("config_path", "/") #This needs to be / because the tftp server goes straight to #the tftpboot directory
  264. # Target image and its path (single image is default)
  265. set_default("target_system_image", "")
  266. set_default("target_image_path", "/")
  267. set_default("target_kickstart_image", "")
  268. # Destination image and its path
  269. set_default("destination_path", "/bootflash")
  270. set_default("destination_system_image", options["target_system_image"])
  271. set_default("destination_kickstart_image", options["target_kickstart_image"])
  272. set_default("destination_midway_system_image", "midway_system.bin")
  273. set_default("destination_midway_kickstart_image", "midway_kickstart.bin")
  274.  
  275. # User app path
  276. set_default("user_app_path", "/var/lib/tftpboot/")
  277.  
  278. # MD5 Verification
  279. set_default("disable_md5", True)
  280.  
  281. # Midway system and kickstart source file name.
  282. # This should be a 6.x U6 or greater dual image.
  283. # Required only if moving from pre 6.x U6 image to 7.x/higher image.
  284. set_default("midway_system_image", "")
  285. set_default("midway_kickstart_image", "")
  286. # --- USB related settings ---
  287. # USB slot info. By default its USB slot 1, if not specified specifically.
  288. # collina2 has 2 usb ports. To enable poap in usb slot 2 user has to set the
  289. # value of usbslot to 2.
  290. set_default("usb_slot", 1)
  291. # Source file name of Config file
  292. set_default("source_config_file", "poap.cfg")
  293.  
  294. set_default("vrf", os.environ["POAP_VRF"])
  295. set_default("destination_config", "poap_conf.cfg")
  296. set_default("split_config_first", "poap_1.cfg")
  297. set_default("split_config_second", "poap_2.cfg")
  298.  
  299. # Timeout info (in seconds)
  300. # Not applicable for TFTP protocol. POAP script timeout can help
  301. # in the case of TFTP.
  302. set_default("timeout_config", 120) # 2 minutes
  303. set_default("timeout_copy_system", 2100) # 35 minutes
  304. set_default("timeout_copy_kickstart", 900) # 15 minutes
  305. set_default("timeout_copy_personality", 900) # 15 minutes
  306. set_default("timeout_copy_user", 900) # 15 minutes
  307.  
  308. # Personality
  309. set_default("personality_path", "/tftpboot")
  310. set_default("source_tarball", "personality.tar")
  311. set_default("destination_tarball", options["source_tarball"])
  312. set_default("compact_image", False)
  313.  
  314. # Check that options are valid
  315. validate_options()
  316.  
  317.  
  318. def validate_options():
  319. """
  320. Validates that the options provided by the user are valid.
  321. Aborts the script if they are not.
  322. """
  323. if os.environ.get("POAP_PHASE", None) == "USB" and options["mode"] == "personality":
  324. abort("POAP Personality is not supported via USB!")
  325.  
  326. # Compare the list of what options users have to what options we actually support.
  327. supplied_options = set(options.keys())
  328. # Anything extra shouldn't be there
  329. invalid_options = supplied_options.difference(valid_options)
  330. for option in invalid_options:
  331. poap_log("Invalid option detected: %s (check spelling, capitalization, and underscores)" %
  332. option)
  333. if len(invalid_options) > 0:
  334. abort()
  335.  
  336.  
  337. def set_default(key, value):
  338. """
  339. Sets a value in the options dictionary if it isn't already set by the user
  340. """
  341. global options
  342. global valid_options
  343.  
  344. if key not in options:
  345. options[key] = value
  346.  
  347. # Track this is a valid option so we can validate that customers didn't
  348. # mistype any options
  349. valid_options.add(key)
  350.  
  351.  
  352. def abort(msg=None):
  353. """
  354. Aborts the POAP script execution with an optional message.
  355. """
  356. global log_hdl
  357.  
  358. if msg is not None:
  359. poap_log(msg)
  360.  
  361. cleanup_files()
  362. close_log_handle()
  363. exit(1)
  364.  
  365.  
  366. def close_log_handle():
  367. """
  368. Closes the log handle if it exists
  369. """
  370. if "log_hdl" in globals() and log_hdl is not None:
  371. log_hdl.close()
  372.  
  373.  
  374. def init_globals():
  375. """
  376. Initializes all the global variables that are used in this POAP script
  377. """
  378. global log_hdl, syslog_prefix
  379. global empty_first_file, single_image, multi_step_install
  380. global valid_options
  381. global del_system_image, del_kickstart_image
  382.  
  383. # A list of valid options
  384. valid_options = set(["username", "password", "hostname"])
  385. log_hdl = None
  386. syslog_prefix = ""
  387. # indicates whether first config file is empty or not
  388. empty_first_file = 1
  389. # flag to indicate single or dual image
  390. single_image = False
  391. # flag to indicate whether or not we need to load intermediate images to get to our target
  392. multi_step_install = False
  393.  
  394. # confirm image deletion
  395. del_system_image = True
  396. # confirm image deletion
  397. del_kickstart_image = True
  398.  
  399.  
  400. def format_mac(syslog_mac=""):
  401. """
  402. Given mac address is formatted as XX:XX:XX:XX:XX:XX
  403. """
  404. syslog_mac = "%s:%s:%s:%s:%s:%s" % (
  405. syslog_mac[0:2], syslog_mac[2:4], syslog_mac[4:6],
  406. syslog_mac[6:8], syslog_mac[8:10],
  407. syslog_mac[10:12])
  408. return syslog_mac
  409.  
  410.  
  411. def set_syslog_prefix():
  412. """
  413. Sets the appropriate POAP syslog prefix string based on
  414. POAP config mode.
  415. """
  416. global syslog_prefix
  417. if 'POAP_SERIAL' in os.environ:
  418. syslog_prefix = "S/N[%s]" % os.environ['POAP_SERIAL']
  419. if 'POAP_PHASE' in os.environ:
  420. if os.environ['POAP_PHASE'] == "USB":
  421. if 'POAP_RMAC' in os.environ:
  422. poap_syslog_mac = "%s" % os.environ['POAP_RMAC']
  423. syslog_prefix = "%s-MAC[%s]" % (
  424. syslog_prefix, poap_syslog_mac)
  425. return
  426. if 'POAP_MGMT_MAC' in os.environ:
  427. poap_syslog_mac = "%s" % os.environ['POAP_MGMT_MAC']
  428. syslog_prefix = "%s-MAC[%s]" % (
  429. syslog_prefix, poap_syslog_mac)
  430. return
  431. else:
  432. if 'POAP_MAC' in os.environ:
  433. poap_syslog_mac = "%s" % os.environ['POAP_MAC']
  434. poap_syslog_mac = format_mac(poap_syslog_mac)
  435. syslog_prefix = "%s-MAC[%s]" % (
  436. syslog_prefix, poap_syslog_mac)
  437. return
  438.  
  439.  
  440. def poap_cleanup_script_logs():
  441. """
  442. Deletes all the POAP log files in bootflash leaving
  443. recent 4 files.
  444. """
  445. file_list = sorted(glob.glob(os.path.join("/bootflash", '*poap*script.log')), reverse=True)
  446. poap_log("Found %d POAP script logs" % len(file_list))
  447.  
  448. logs_for_removal = file_list[4:]
  449. for old_log in logs_for_removal:
  450. remove_file(old_log)
  451.  
  452.  
  453. def poap_log(info):
  454. """
  455. Log the trace into console and poap_script log file in bootflash
  456. Args:
  457. file_hdl: poap_script log bootflash file handle
  458. info: The information that needs to be logged.
  459. """
  460. global log_hdl, syslog_prefix
  461.  
  462. # Don't syslog passwords
  463. parts = re.split("\s+", info.strip())
  464. for (index, part) in enumerate(parts):
  465. # blank out the password after the password keyword (terminal password *****, etc.)
  466. if part == "password" and len(parts) >= index+2:
  467. parts[index+1] = "<removed>"
  468.  
  469. # Recombine for syslogging
  470. info = " ".join(parts)
  471.  
  472. # We could potentially get a traceback (and trigger this) before
  473. # we have called init_globals. Make sure we can still log successfully
  474. try:
  475. info = "%s - %s" % (syslog_prefix, info)
  476. except NameError:
  477. info = " - %s" % info
  478.  
  479. syslog.syslog(9, info)
  480. if "log_hdl" in globals() and log_hdl is not None:
  481. log_hdl.write("\n")
  482. log_hdl.write(info)
  483. log_hdl.flush()
  484.  
  485.  
  486. def remove_file(filename):
  487. """
  488. Removes a file if it exists and it's not a directory.
  489. """
  490. if os.path.isfile(filename):
  491. try:
  492. os.remove(filename)
  493. except (IOError, OSError) as e:
  494. poap_log("Failed to remove %s: %s" % (filename, str(e)))
  495.  
  496.  
  497. def cleanup_file_from_option(option, bootflash_root=False):
  498. """
  499. Removes a file (indicated by the option in the POAP options) and removes it
  500. if it exists.
  501. We handle the cases where the variable is unused (defaults to None) or
  502. where the variable is not set yet (invoking this cleanup before we init)
  503. """
  504. if options.get(option) is not None:
  505. if bootflash_root:
  506. path = "/bootflash"
  507. else:
  508. path = options["destination_path"]
  509.  
  510. remove_file(os.path.join(path, options[option]))
  511. remove_file(os.path.join(path, "%s.tmp" % options[option]))
  512.  
  513.  
  514. def cleanup_files():
  515. """
  516. Cleanup all the POAP created files.
  517. """
  518. global options, log_hdl
  519. global del_system_image, del_kickstart_image
  520.  
  521. poap_log("Cleanup all files")
  522.  
  523. # Destination config
  524. cleanup_file_from_option("destination_config")
  525. # Temporary split configs
  526. cleanup_file_from_option("split_config_first", True)
  527. cleanup_file_from_option("split_config_second", True)
  528. # Destination system or NXOS image
  529. if del_system_image is True:
  530. cleanup_file_from_option("destination_system_image")
  531. # Destination kickstart image
  532. if del_kickstart_image is True:
  533. cleanup_file_from_option("destination_kickstart_image")
  534. # Destination config
  535. cleanup_file_from_option("destination_config")
  536.  
  537.  
  538. def sig_handler_no_exit(signum, stack):
  539. """
  540. A signal handler for the SIGTERM signal. Does not exit
  541. """
  542. poap_log("INFO: SIGTERM Handler while configuring boot variables")
  543.  
  544.  
  545. def sigterm_handler(signum, stack):
  546. """
  547. A signal handler for the SIGTERM signal. Cleans up and exits
  548. """
  549. poap_log("INFO: SIGTERM Handler")
  550. cleanup_files()
  551. log_hdl.close()
  552. exit(1)
  553.  
  554.  
  555. def split_config_not_needed():
  556. """Checks if splitting the config into two config files is needed. This is needed on older
  557. images that require a reload to apply certain configs (e.g. TCAM changes). If we're on an
  558. image newer than or equal to 7.0(3)I4(1), we don't need reloads to apply configs
  559. """
  560. global options
  561.  
  562. """
  563. Device running in n3k mode still requires splitting of config.
  564. """
  565. if not 'START' in open('/tmp/first_setup.log').readline():
  566. return False
  567.  
  568. nxos_major = 0
  569. nxos_rev = 0
  570.  
  571. # (nxos, 7, 0, 3, I4, 1, bin)
  572. # (n9000-dk9, 6, 1, 2, I1, 1, bin)
  573.  
  574. parts = options['target_system_image'].split(".")
  575. # number of parts should above 7 as above for us to check if its supported
  576. if len(parts) < 7:
  577. return False
  578.  
  579. if parts[0] != "nxos":
  580. return False
  581.  
  582. try:
  583. nxos_major = int(parts[1])
  584. if nxos_major < 7:
  585. return False
  586. elif nxos_major > 7:
  587. return True
  588. except ValueError:
  589. return False
  590. # NXOS 7x
  591.  
  592. try:
  593. nxos_minor = int(parts[2])
  594. if nxos_minor > 0:
  595. return True
  596. except ValueError:
  597. return False
  598. # NXOS 7.0x
  599.  
  600. try:
  601. nxos_rev = int(parts[3])
  602. if nxos_rev > 3:
  603. return True
  604. elif nxos_rev < 3:
  605. return False
  606. except ValueError:
  607. return False
  608. # NXOS 7.0.3.x
  609.  
  610. if parts[4] >= "I4":
  611. return True
  612. # NXOS 7.0.3.I3 or less
  613. return False
  614.  
  615. def mtc_shut_member_ports(line, config_file_first):
  616. """
  617. In case bundling of any ports is done for mtc we shut down all the member
  618. ports. Apply speed 40000 followed by no shut on the first bundle port.
  619. Rest of the port configuration is handled as part of second config file.
  620. """
  621. global empty_first_file
  622. intf = map(int, re.findall('\d+', line))[0]
  623. port = map(int, re.findall('\d+', line))[1]
  624. if(port!=0):
  625. config_file_first.write('interface Ethernet' + str(intf) + '/' + str(port) + '\n')
  626. config_file_first.write("shut\n")
  627. config_file_first.write('interface Ethernet' + str(intf) + '/' + str(port+1) + '\n')
  628. config_file_first.write("shut\n")
  629. config_file_first.write('interface Ethernet' + str(intf) + '/' + str(port+2) + '\n')
  630. config_file_first.write("shut\n")
  631. config_file_first.write('interface Ethernet' + str(intf) + '/' + str(port+3) + '\n')
  632. config_file_first.write("shut\n")
  633. config_file_first.write('interface Ethernet' + str(intf) + '/' + str(port) + '\n')
  634. config_file_first.write("speed 40000\n")
  635. config_file_first.write("no shut\n")
  636. empty_first_file=0
  637. return 1
  638. else:
  639. return 0
  640.  
  641. def is_mtc():
  642. """
  643. Checks is the box is mtc or not using show module cli.
  644. MTC boxes are have mod id number as 3548
  645. """
  646. sh_mod_output = cli("show module")
  647. if(sh_mod_output.find("3548") != -1):
  648. return 1
  649.  
  650. def split_config_file():
  651. """
  652. Splits the downloaded switch configuration file into two files.
  653. File1: Contains lines of config that require switch reboot.
  654. File2: Contains lines of config that doesn't require switch reboot
  655. """
  656. poap_log("Split Config file")
  657. global empty_first_file, log_hdl, single_image, res_temp_flag, res_flag_dontprint
  658. res_temp_flag = 0
  659. res_flag_dontprint = 0
  660. skip_split_config = False
  661.  
  662. config_file = open(os.path.join(options["destination_path"],
  663. options["destination_config"]), "r")
  664. config_file_first = open(os.path.join("/bootflash",
  665. options["split_config_first"]), "w+")
  666. config_file_second = open(os.path.join("/bootflash",
  667. options["split_config_second"]), "w+")
  668.  
  669. # If we don't require extra reloads for commands (newer images), skip this
  670. # splitting of commands (and break the below loop immediately)
  671. if split_config_not_needed():
  672. config_file_second.write(config_file.read())
  673. line = ""
  674. poap_log("Skip split config as it isn't needed with %s" % options["target_system_image"])
  675. else:
  676. line = config_file.readline()
  677.  
  678. while line != "":
  679. if (line.find("interface Ethernet") == 0):
  680. intf_eth_line=line
  681. if res_temp_flag == 1 and not skip_split_config:
  682. if line.find("arp-ether") != -1 \
  683. or line.find("copp") != -1\
  684. or line.find("e-ipv6-qos") != -1\
  685. or line.find("e-ipv6-racl") != -1\
  686. or line.find("e-mac-qos") != -1\
  687. or line.find("e-qos") != -1\
  688. or line.find("e-qos-lite") != -1\
  689. or line.find("e-racl") != -1\
  690. or line.find("fcoe-egress") != -1\
  691. or line.find("fcoe-ingress") != -1\
  692. or line.find("fex-ifacl") != -1\
  693. or line.find("fex-ipv6-ifacl") != -1\
  694. or line.find("fex-ipv6-qos") != -1\
  695. or line.find("fex-mac-ifacl") != -1\
  696. or line.find("fex-mac-qos") != -1\
  697. or line.find("fex-qos") != -1\
  698. or line.find("fex-qos-lite") != -1\
  699. or line.find("ifacl") != -1\
  700. or line.find("ipsg") != -1\
  701. or line.find("ipv6-ifacl") != -1\
  702. or line.find("ipv6-l3qos") != -1\
  703. or line.find("ipv6-qos") != -1\
  704. or line.find("ipv6-racl") != -1\
  705. or line.find("ipv6-vacl") != -1\
  706. or line.find("ipv6-vqos") != -1\
  707. or line.find("l3qos") != -1\
  708. or line.find("l3qos-lite") != -1\
  709. or line.find("mac-ifacl") != -1\
  710. or line.find("mac-l3qos") != -1\
  711. or line.find("mac-qos") != -1\
  712. or line.find("mac-vacl") != -1\
  713. or line.find("mac-vqos") != -1\
  714. or line.find("mcast-performance") != -1\
  715. or line.find("mcast_bidir") != -1\
  716. or line.find("mpls") != -1\
  717. or line.find("n9k-arp-acl") != -1\
  718. or line.find("nat") != -1\
  719. or line.find("ns-ipv6-l3qos") != -1\
  720. or line.find("ns-ipv6-qos") != -1\
  721. or line.find("ns-ipv6-vqos") != -1\
  722. or line.find("ns-l3qos") != -1\
  723. or line.find("ns-mac-l3qos") != -1\
  724. or line.find("ns-mac-qos") != -1\
  725. or line.find("ns-mac-vqos") != -1\
  726. or line.find("ns-qos") != -1\
  727. or line.find("ns-vqos") != -1\
  728. or line.find("openflow") != -1\
  729. or line.find("openflow-ipv6") != -1\
  730. or line.find("qos") != -1\
  731. or line.find("qos-lite") != -1\
  732. or line.find("racl") != -1\
  733. or line.find("redirect") != -1\
  734. or line.find("redirect-tunnel") != -1\
  735. or line.find("rp-ipv6-qos") != -1\
  736. or line.find("rp-mac-qos") != -1\
  737. or line.find("rp-qos") != -1\
  738. or line.find("rp-qos-lite") != -1\
  739. or line.find("sflow") != -1\
  740. or line.find("span") != -1\
  741. or line.find("span-sflow") != -1\
  742. or line.find("vacl") != -1 \
  743. or line.find("vpc-convergence") != -1 \
  744. or line.find("vqos") != -1 \
  745. or line.find("vqos-lite") != -1:
  746. config_file_first.write(line)
  747. res_flag_dontprint = 1
  748. else:
  749. res_temp_flag = 0
  750. if line.startswith("hardware profile tcam resource template"):
  751. res_temp_flag = 1
  752. if ((line.find("speed 40000") != -1) and is_mtc()):
  753. mtc_shut_member_ports(intf_eth_line, config_file_first)
  754. #Don't include speed in second config file.
  755. res_flag_dontprint = 1
  756. if res_temp_flag == 0 and line.startswith("system vlan") \
  757. or line.startswith("hardware profile portmode") \
  758. or line.startswith("hardware profile forwarding-mode warp") \
  759. or line.startswith("hardware profile forwarding-mode openflow-hybrid") \
  760. or line.startswith("hardware profile forwarding-mode openflow-only") \
  761. or line.startswith("hardware profile tcam") \
  762. or line.startswith("type fc") \
  763. or line.startswith("fabric-mode 40G") \
  764. or line.startswith("system urpf") \
  765. or line.startswith("no system urpf") \
  766. or line.startswith("hardware profile ipv6") \
  767. or line.startswith("system routing") \
  768. or line.startswith("hardware profile multicast service-reflect") \
  769. or line.startswith("ip service-reflect mode") \
  770. or line.startswith("udf") \
  771. or line.startswith("hardware profile unicast enable-host-ecmp"):
  772. config_file_first.write(line)
  773. if empty_first_file is 1:
  774. poap_log("setting empty file to 0 for line %s" % line)
  775. empty_first_file = 0
  776. elif res_flag_dontprint == 0:
  777. config_file_second.write(line)
  778. res_flag_dontprint = 0
  779. line = config_file.readline()
  780.  
  781. # for poap across images set boot varible in the first config file
  782. poap_log("value of empty file is %d " % empty_first_file)
  783. if single_image is True:
  784. single_image_path = os.path.join(options["destination_path"],
  785. options["destination_system_image"])
  786. single_image_path = single_image_path.replace("/bootflash", "bootflash:", 1)
  787. if empty_first_file is 0:
  788. cmd = "boot nxos %s" % single_image_path
  789. poap_log("writing boot command: %s to first config file" % cmd)
  790. config_file_first.write("%s\n" % cmd)
  791. else:
  792. cmd = "boot nxos %s" % single_image_path
  793. poap_log("writing boot command: %s to second config file" % cmd)
  794. config_file_second.write("%s\n" % cmd)
  795.  
  796. config_file.close()
  797. remove_file(os.path.join(options["destination_path"], options["destination_config"]))
  798. config_file_first.close()
  799. if empty_first_file is 1:
  800. remove_file(os.path.join(options["destination_path"], options["split_config_first"]))
  801. config_file_second.close()
  802.  
  803.  
  804. def md5sum(filename):
  805. """
  806. Compute the md5 value for the file that is copied/downloaded.
  807. """
  808. if filename.startswith('/bootflash'):
  809. filename = filename.replace('/bootflash', 'bootflash:', 1)
  810.  
  811. poap_log("file name is %s" % filename)
  812. md5_sum = "Unknown"
  813. md5_output = cli("show file %s md5sum" % filename)
  814. if legacy:
  815. """
  816. Fetch the last entry from findall as some of the older nexus
  817. images had issues in fetching the value
  818. """
  819. result = re.findall(r'([a-fA-F\d]{32})', md5_output[1])
  820. if len(result) > 0:
  821. md5_sum = result[len(result) - 1]
  822. else:
  823. result = re.search(r'([a-fA-F\d]{32})', md5_output)
  824. if result is not None:
  825. md5_sum = result.group(1)
  826.  
  827. poap_log("INFO: md5sum %s (recalculated)" % md5_sum)
  828. return md5_sum
  829.  
  830.  
  831. def verify_md5(md5given, filename):
  832. """
  833. Verifies if the md5 value fetched from .md5 file matches with
  834. the md5 computed on the copied/downloaded file.
  835. Args:
  836. md5given: md5 value fetched from .md5 file
  837. filename: Name of the file that is copied/downloaded.
  838. """
  839. poap_log("Verifying MD5 checksum")
  840. if not os.path.exists("%s" % filename):
  841. poap_log("ERROR: File %s does not exit" % filename)
  842. return False
  843.  
  844. md5calculated = md5sum(filename)
  845.  
  846. try:
  847. file_size = os.path.getsize(filename)
  848. except OSError:
  849. poap_log("WARN: Failed to get size of %s" % filename)
  850. file_size = "Unknown"
  851.  
  852. poap_log("Verifying MD5 checksum of %s (size %s)" % (filename, file_size))
  853. poap_log(" md5given = %s md5calculated = %s" % (
  854. md5given, md5calculated))
  855. if md5given == md5calculated:
  856. poap_log("MD5 match for file = {0}".format(filename))
  857. return True
  858. poap_log("MD5 mis-match for file = {0}".format(filename))
  859. return False
  860.  
  861.  
  862. def get_md5(filename):
  863. """
  864. Fetches the md5 value from .md5 file.
  865. Args:
  866. keyword: Keyword to look for in .md5 file
  867. filename: .md5 filename
  868. """
  869. # Get the MD5 file
  870. md5_filename = "%s.md5" % filename
  871.  
  872. if not os.path.exists(os.path.join(options["destination_path"], md5_filename)):
  873. abort("MD5 file is missing (%s does not exist!)"
  874. % os.path.join(options["destination_path"], filename))
  875.  
  876. file_hdl = open(os.path.join(options["destination_path"], md5_filename), "r")
  877. line = file_hdl.readline()
  878. while line != "":
  879. if line.find("md5sum", 0, len("md5sum")) != -1:
  880. line = line.split("=")[1].strip()
  881. file_hdl.close()
  882. return line
  883. if line.find(filename) != -1:
  884. line = re.split("\s+", line)[0]
  885. file_hdl.close()
  886. return line
  887. else:
  888. poap_log("Found non-MD5 checksum in %s: %s" % (md5_filename, line))
  889. line = file_hdl.readline()
  890. file_hdl.close()
  891. return ""
  892.  
  893. def get_bootflash_size():
  894. """
  895. Gets the bootflash size in KB from CLI.
  896. Output is handled differently for 6.x and 7.x or higher version.
  897. """
  898. cli_output = cli("show version")
  899. if legacy:
  900. result = re.search(r'bootflash:\s+(\d+)', cli_output[1])
  901. if result is not None:
  902. return int(result.group(1))
  903. else:
  904. result = re.search(r'bootflash:\s+(\d+)', cli_output)
  905. if result is not None:
  906. return int(result.group(1))
  907. poap_log("Unable to get bootflash size")
  908.  
  909.  
  910. def do_copy(source="", dest="", login_timeout=20, dest_tmp="", compact=False):
  911. """
  912. Copies the file provided from source to destination. Source could
  913. be USB or external server. Appropriate copy function is required
  914. based on whether the switch runs 6.x or 7.x or higher image.
  915. """
  916. poap_log("Copying file options source=%s destination=%s "
  917. "login_timeout=%s destination_tmp=%s" % (source,
  918. os.path.join(options["destination_path"],
  919. dest), login_timeout, dest_tmp))
  920.  
  921. remove_file(os.path.join(options["destination_path"], dest_tmp))
  922.  
  923. if os.environ.get("POAP_PHASE", None) == "USB":
  924. copy_src = os.path.join("/usbslot%s" % (options["usb_slot"]), source)
  925.  
  926. dest_tmp = os.path.join(options["destination_path"], dest_tmp)
  927. if os.path.exists(copy_src):
  928. poap_log("%s exists" % source)
  929. poap_log("Copying from %s to %s" % (copy_src, dest_tmp))
  930. shutil.copy(copy_src, dest_tmp)
  931. else:
  932. abort("/usbslot%d/%s does NOT exist" % (options["usb_slot"], source))
  933. else:
  934. protocol = options["transfer_protocol"]
  935. host = options["hostname"]
  936. user = options["username"]
  937. password = options["password"]
  938. dest_tmp = os.path.join(options["destination_path"], dest_tmp)
  939. copy_tmp = dest_tmp.replace("/bootflash", "bootflash:", 1)
  940. vrf = options["vrf"]
  941. poap_log("Transfering using %s from %s to %s hostname %s vrf %s" % (
  942. protocol, source, copy_tmp, host, vrf))
  943. if legacy:
  944. try:
  945. transfer(protocol, host, source, copy_tmp, vrf, login_timeout,
  946. user, password)
  947. # The transfer module doesn't fail if bootflash runs out of space.
  948. # This is a bug with the already shipped transfer module, and there's
  949. # no return code or output that indicates this has happened. Newer
  950. # images have the "terminal password" CLI that lets us avoid this.
  951. poap_log("Copy done using transfer module. Please check size below")
  952. except Exception as e:
  953. # Handle known cases
  954. if "file not found" in str(e):
  955. abort("Copy failed: %s" % str(e))
  956. elif "Permission denied" in str(e):
  957. abort("Copy of %s failed: permission denied" % source)
  958. else:
  959. raise
  960. else: # Add the destination path
  961.  
  962. copy_cmd = "terminal dont-ask ; terminal password %s ; " % password
  963. if compact is True:
  964. copy_cmd += "copy %s://%s@%s%s %s compact vrf %s" % (
  965. protocol, user, host, source, copy_tmp, vrf)
  966. else:
  967. copy_cmd += "copy %s://%s@%s%s %s vrf %s" % (
  968. protocol, user, host, source, copy_tmp, vrf)
  969. poap_log("Command is : %s" % copy_cmd)
  970. try:
  971. cli(copy_cmd)
  972. except Exception as e:
  973. poap_log("Copy failed: %s" % str(e))
  974. # scp compact can fail due to reasons of current image version or
  975. # platform do not support it; Try normal scp in such cases
  976. if compact is True and ("Syntax error while parsing" in str(e) or
  977. "Compaction is not supported on this platform" in str(e)):
  978. return False
  979. # Remove extra junk in the message
  980. elif "no such file" in str(e):
  981. abort("Copy of %s failed: no such file" % source)
  982. elif "Permission denied" in str(e):
  983. abort("Copy of %s failed: permission denied" % source)
  984. elif "No space left on device" in str(e):
  985. abort("No space left on device")
  986. else:
  987. raise
  988.  
  989. try:
  990. file_size = os.path.getsize(dest_tmp)
  991. except OSError:
  992. poap_log("WARN: Failed to get size of %s" % dest_tmp)
  993. file_size = "Unknown"
  994.  
  995. poap_log("*** Downloaded file is of size %s ***" % file_size)
  996.  
  997. dest = os.path.join(options["destination_path"], dest)
  998. try:
  999. os.rename(dest_tmp, dest)
  1000. except KeyError as e:
  1001. abort("Failed to rename %s to %s: %s" % (dest_tmp, dest, str(e)))
  1002.  
  1003. poap_log("Renamed %s to %s" % (dest_tmp, dest))
  1004. return True
  1005.  
  1006.  
  1007. def create_destination_directories():
  1008. """
  1009. Creates the destination directory if it doesn't exist. For example, if the user
  1010. specifies /bootflash/poap/files as the destination path, this function will create
  1011. the directory "poap" and the directory "files" in the hierarchy above if they don't
  1012. already exist.
  1013. """
  1014. try:
  1015. os.makedirs(options["destination_path"])
  1016. except OSError as e:
  1017. if e.errno != errno.EEXIST or not os.path.isdir(options["destination_path"]):
  1018. raise
  1019.  
  1020.  
  1021. def copy_md5_info(file_path, file_name):
  1022. """
  1023. Copies file with .md5 extension into bootflash
  1024. Args:
  1025. file_path: Directory where .md5 file resides
  1026. file_name: Name of the .md5 file
  1027. """
  1028. poap_log("Copying MD5 information")
  1029.  
  1030. md5_file_name = "%s.md5" % file_name
  1031. if os.path.exists(os.path.join(options["destination_path"], md5_file_name)):
  1032. remove_file(os.path.join(options["destination_path"], md5_file_name))
  1033.  
  1034. tmp_file = "%s.tmp" % md5_file_name
  1035. timeout = options["timeout_config"]
  1036. src = os.path.join(file_path, md5_file_name)
  1037. poap_log("Starting Copy of MD5. src = %s dest = %s" % (
  1038. src, os.path.join(options["destination_path"], md5_file_name)))
  1039. if os.environ['POAP_PHASE'] != "USB":
  1040. poap_log("File transfer_protocol = %s" % options["transfer_protocol"])
  1041.  
  1042. do_copy(src, md5_file_name, timeout, tmp_file)
  1043.  
  1044.  
  1045. def copy_config():
  1046. """
  1047. Copies the configuration from the USB device or remote server to the switch
  1048. If the mode is personality, the configuration is actually inside the tarball,
  1049. so we skip copying any config from USB or the server at this point of execution
  1050. """
  1051. if options["mode"] != "personality":
  1052. copy_remote_config()
  1053.  
  1054.  
  1055. def copy_remote_config():
  1056. """
  1057. Copies switch configuration file and verifies if the md5 of the config
  1058. matches with the value present in .md5 file downloaded.
  1059. """
  1060. poap_log("Copying config file")
  1061. global empty_first_file
  1062. org_file = options["destination_config"]
  1063. if options["disable_md5"] is False:
  1064. copy_md5_info(options["config_path"], options["source_config_file"])
  1065. md5_sum_given = get_md5(options["source_config_file"])
  1066. remove_file(os.path.join(options["destination_path"], "%s.md5" %
  1067. options["source_config_file"]))
  1068. if md5_sum_given and os.path.exists(os.path.join(options["destination_path"], org_file)):
  1069. if verify_md5(md5_sum_given, os.path.join(options["destination_path"], org_file)):
  1070. poap_log("File %s already exists and MD5 matches" %
  1071. os.path.join(options["destination_path"], org_file))
  1072. split_config_file()
  1073. return
  1074. elif not md5_sum_given:
  1075. poap_log("MD5 sum given is invalid: %s" % md5_sum_given)
  1076. poap_log("INFO: Starting Copy of Config File to %s" % os.path.join(options["destination_path"],
  1077. org_file))
  1078. tmp_file = "%s.tmp" % org_file
  1079. timeout = options["timeout_config"]
  1080. src = os.path.join(options["config_path"], options["source_config_file"])
  1081.  
  1082. do_copy(src, org_file, timeout, tmp_file)
  1083.  
  1084. if options["disable_md5"] is False:
  1085. if md5_sum_given and not verify_md5(md5_sum_given,
  1086. os.path.join(options["destination_path"], org_file)):
  1087. abort("#### config file %s MD5 verification failed #####\n" % os.path.join(
  1088. options["destination_path"], org_file))
  1089. split_config_file()
  1090. poap_log("INFO: Completed copy of config file to %s" %
  1091. os.path.join(options["destination_path"], org_file))
  1092.  
  1093.  
  1094. def target_system_image_is_currently_running():
  1095. """
  1096. Checks if the system image that we would try to download is the one that's
  1097. currently running. Not used if MD5 checks are enabled.
  1098. """
  1099. version = get_version()
  1100. if legacy is False:
  1101. image_parts = [part for part in re.split("[\.()]", version) if part]
  1102. image_parts.insert(0, "nxos")
  1103. image_parts.append("bin")
  1104.  
  1105. running_image = ".".join(image_parts)
  1106.  
  1107. poap_log("Running: '%s'" % running_image)
  1108. poap_log("Target: '%s'" % options["target_system_image"])
  1109.  
  1110. return running_image == options["target_system_image"]
  1111.  
  1112. return False
  1113.  
  1114.  
  1115. def copy_system():
  1116. """
  1117. Copies system/nxos image and verifies if the md5 of the image matches
  1118. with the value present in .md5 file downloaded.
  1119. """
  1120. global del_system_image
  1121. md5_sum_given = None
  1122.  
  1123. if options["disable_md5"] is True and target_system_image_is_currently_running():
  1124. poap_log("Currently running image is target image. Skipping system image download")
  1125. return
  1126.  
  1127. # do compact scp of system image if bootflash size is <= 2GB and "compact_image" option is enabled
  1128. if get_bootflash_size() <= 2000000 and options["compact_image"] is True and options["transfer_protocol"] is "scp":
  1129. poap_log("INFO: Try image copy with compact option...")
  1130. do_compact = True
  1131. else:
  1132. do_compact = False
  1133.  
  1134. org_file = options["destination_system_image"]
  1135. if options["disable_md5"] is False:
  1136. copy_md5_info(options["target_image_path"], options["target_system_image"])
  1137. md5_sum_given = get_md5(options["target_system_image"])
  1138. remove_file(os.path.join(options["destination_path"], "%s.md5" %
  1139. options["target_system_image"]))
  1140. poap_log("MD5 for system image from server: %s" % md5_sum_given)
  1141. if md5_sum_given and os.path.exists(os.path.join(options["destination_path"], options["target_system_image"])):
  1142. if verify_md5(md5_sum_given, os.path.join(options["destination_path"], options["target_system_image"])):
  1143. poap_log("File %s already exists and MD5 matches" %
  1144. os.path.join(options["destination_path"], options["target_system_image"]))
  1145. """
  1146. For multi-level install when the target system image is already
  1147. present in the box, overwrite midway_system image name that is
  1148. stored in destination_system_image with target_system_image.
  1149. """
  1150. options["destination_system_image"] = options["target_system_image"]
  1151. del_system_image = False
  1152. return
  1153. elif not md5_sum_given:
  1154. abort("Invalid MD5 from server: %s" % md5_sum_given)
  1155. else:
  1156. poap_log("File %s does not exist on switch" % options["target_system_image"])
  1157.  
  1158. tmp_file = "%s.tmp" % org_file
  1159. timeout = options["timeout_copy_system"]
  1160.  
  1161. # For personality we use the personality path for everything
  1162. src = os.path.join(options["target_image_path"], options["target_system_image"])
  1163.  
  1164. poap_log("INFO: Starting Copy of System Image")
  1165.  
  1166. ret = do_copy(src, org_file, timeout, tmp_file, do_compact)
  1167. if do_compact is True and ret is False:
  1168. poap_log("INFO: compact copy failed; Try normal copy...")
  1169. do_compact = False
  1170. do_copy(src, org_file, timeout, tmp_file)
  1171.  
  1172. if options["disable_md5"] is False and md5_sum_given and do_compact is False:
  1173. if not verify_md5(md5_sum_given,
  1174. os.path.join(options["destination_path"], org_file)):
  1175. abort("#### System file %s MD5 verification failed #####\n" % os.path.join(
  1176. options["destination_path"], org_file))
  1177. poap_log("INFO: Completed Copy of System Image to %s" % os.path.join(
  1178. options["destination_path"], org_file))
  1179.  
  1180.  
  1181. def copy_kickstart():
  1182. """
  1183. Copies kickstart image and verifies if the md5 of the image matches
  1184. with the value present in .md5 file downloaded.
  1185. """
  1186. global del_kickstart_image
  1187. poap_log("Copying kickstart image")
  1188. org_file = options["destination_kickstart_image"]
  1189. if options["disable_md5"] is False:
  1190. copy_md5_info(options["target_image_path"], options["target_kickstart_image"])
  1191. md5_sum_given = get_md5(options["target_kickstart_image"])
  1192. remove_file(os.path.join(options["destination_path"], "%s.md5" %
  1193. options["target_kickstart_image"]))
  1194. if md5_sum_given and os.path.exists(os.path.join(options["destination_path"], options["target_kickstart_image"])):
  1195. if verify_md5(md5_sum_given, os.path.join(options["destination_path"], options["target_kickstart_image"])):
  1196. poap_log("INFO: File %s already exists and MD5 matches" %
  1197. os.path.join(options["destination_path"], options["target_kickstart_image"]))
  1198. """
  1199. For multi-level install when the target kickstart image is already
  1200. present in the box, overwrite midway_kickstart image name that is
  1201. stored in destination_kickstart_image with target_kickstart_image.
  1202. """
  1203. options["destination_kickstart_image"] = options["target_kickstart_image"]
  1204. del_kickstart_image = False
  1205. return
  1206.  
  1207. tmp_file = "%s.tmp" % org_file
  1208. timeout = options["timeout_copy_kickstart"]
  1209. src = os.path.join(options["target_image_path"], options["target_kickstart_image"])
  1210. do_copy(src, org_file, timeout, tmp_file)
  1211.  
  1212. if options["disable_md5"] is False and md5_sum_given:
  1213. if not verify_md5(md5_sum_given,
  1214. os.path.join(options["destination_path"], org_file)):
  1215. abort("#### Kickstart file %s%s MD5 verification failed #####\n" % (
  1216. options["destination_path"], org_file))
  1217.  
  1218. poap_log("INFO: Completed Copy of Kickstart Image to %s" % (
  1219. os.path.join(options["destination_path"], org_file)))
  1220.  
  1221.  
  1222. def install_images_7_x():
  1223. """
  1224. Invoked when trying to install a 7.x or higher image. Boot variables are
  1225. set appropriately and the startup config is updated.
  1226. """
  1227. poap_log("Checking if bios upgrade is needed")
  1228. if is_bios_upgrade_needed():
  1229. poap_log("Installing new BIOS (will take up to 5 minutes. Don't abort)")
  1230. install_bios()
  1231.  
  1232. poap_log("Installing NXOS image")
  1233.  
  1234. system_image_path = os.path.join(options["destination_path"],
  1235. options["destination_system_image"])
  1236. system_image_path = system_image_path.replace("/bootflash", "bootflash:", 1)
  1237.  
  1238. try:
  1239. poap_log("config terminal ; boot nxos %s" % system_image_path)
  1240. cli("config terminal ; boot nxos %s" % system_image_path)
  1241. except Exception as e:
  1242. poap_log("Failed to set NXOS boot variable to %s" % system_image_path)
  1243. abort(str(e))
  1244.  
  1245. command_successful = False
  1246. timeout = 10 # minutes
  1247. first_time = time.time()
  1248. endtime = first_time + timeout * 60 # sec per min
  1249. retry_delay = 30 # seconds
  1250. while not command_successful:
  1251. new_time = time.time()
  1252. try:
  1253. cli("copy running-config startup-config")
  1254. command_successful = True
  1255. except SyntaxError:
  1256. poap_log("WARNING: copy run to start failed")
  1257. if new_time > endtime:
  1258. poap_log("ERROR: time out waiting for \"copy run start\" to complete successfully")
  1259. exit(-1)
  1260. poap_log("WARNING: retry in 30 seconds")
  1261. time.sleep(retry_delay)
  1262.  
  1263. poap_log("INFO: Configuration successful")
  1264.  
  1265.  
  1266. # Procedure to install both kickstart and system images
  1267. def install_images():
  1268. """
  1269. Invoked when trying to install a 6.x based image. Bootvariables
  1270. are set appropriately.
  1271. If two step installation is true, just set the bootvariables and
  1272. do no update startup-config so that step two of POAP is triggered.
  1273. """
  1274. kickstart_path = os.path.join(options["destination_path"],
  1275. options["destination_kickstart_image"])
  1276. kickstart_path = kickstart_path.replace("/bootflash", "bootflash:", 1)
  1277.  
  1278. system_path = os.path.join(options["destination_path"],
  1279. options["destination_system_image"])
  1280. system_path = system_path.replace("/bootflash", "bootflash:", 1)
  1281.  
  1282. poap_log("Installing kickstart and system images")
  1283. poap_log("######### Copying the boot variables ##########")
  1284. cli("config terminal ; boot kickstart %s" % kickstart_path)
  1285. cli("config terminal ; boot system %s" % system_path)
  1286.  
  1287. command_successful = False
  1288. timeout = 10 # minutes
  1289. first_time = time.time()
  1290. endtime = first_time + timeout * 60 # sec per min
  1291. retry_delay = 30 # seconds
  1292. while not command_successful:
  1293. new_time = time.time()
  1294. try:
  1295. cli("copy running-config startup-config")
  1296. command_successful = True
  1297. except SyntaxError:
  1298. poap_log("WARNING: copy run to start failed")
  1299. if new_time > endtime:
  1300. poap_log("ERROR: time out waiting for \"copy run start\" to complete successfully")
  1301. exit(-1)
  1302. poap_log("WARNING: retry in 30 seconds")
  1303. time.sleep(retry_delay)
  1304.  
  1305. poap_log("INFO: Configuration successful")
  1306.  
  1307. if multi_step_install is True:
  1308. cli("config terminal ; terminal dont-ask ; write erase")
  1309. poap_log("Midway image copy/setting done")
  1310. exit(0)
  1311. else:
  1312. poap_log("Multi-level install not set, installed images")
  1313.  
  1314.  
  1315. def verify_freespace():
  1316. """
  1317. Checks if the available space in bootflash is sufficient enough to
  1318. download config and required images.
  1319. """
  1320. poap_log("Verifying freespace in bootflash")
  1321. s = os.statvfs("/bootflash/")
  1322. freespace = (s.f_bavail * s.f_frsize) / 1024
  1323. poap_log("Free bootflash space is %s" % freespace)
  1324.  
  1325. if options["required_space"] > freespace:
  1326. abort("*** Not enough bootflash space to continue POAP ***")
  1327.  
  1328.  
  1329. def set_cfg_file_serial():
  1330. """
  1331. Sets the name of the switch config file to download based on chassis
  1332. serial number. e.g conf_FOC3825R1ML.cfg
  1333. """
  1334. poap_log("Setting source cfg filename based-on serial number")
  1335.  
  1336. if 'POAP_SERIAL' in os.environ:
  1337. poap_log("serial number %s" % os.environ['POAP_SERIAL'])
  1338. options["source_config_file"] = "conf.%s" % os.environ['POAP_SERIAL']
  1339. poap_log("Selected conf file name : %s" % options["source_config_file"])
  1340.  
  1341.  
  1342. def set_cfg_file_mac():
  1343. """
  1344. Sets the name of the switch config file to download based on interface
  1345. MAC address. e.g conf_7426CC5C9180.cfg
  1346. """
  1347. poap_log("Setting source cfg filename based on the interface MAC")
  1348. if os.environ.get("POAP_PHASE", None) == "USB":
  1349. if options["usb_slot"] is 2:
  1350. poap_log("usb slot is 2")
  1351.  
  1352. config_file = "conf_%s.cfg" % os.environ['POAP_RMAC']
  1353. poap_log("Router MAC conf file name : %s" % config_file)
  1354. if os.path.exists("/usbslot%d/%s" % (usbslot, config_file)):
  1355. options["source_config_file"] = config_file
  1356. poap_log("Selected conf file name : %s" % options["source_config_file"])
  1357. return
  1358. config_file = "conf_%s.cfg" % os.environ['POAP_MGMT_MAC']
  1359. poap_log("MGMT MAC conf file name : %s" % config_file)
  1360. if os.path.exists("/usbslot%d/%s" % (options["usb_slot"], config_file)):
  1361. options["source_config_file"] = config_file
  1362. poap_log("Selected conf file name : %s" % options["source_config_file"])
  1363. return
  1364. else:
  1365. if 'POAP_MAC' in os.environ:
  1366. poap_log("Interface MAC %s" % os.environ['POAP_MAC'])
  1367. options["source_config_file"] = "conf_%s.cfg" % os.environ['POAP_MAC']
  1368. poap_log("Selected conf file name : %s" % options["source_config_file"])
  1369.  
  1370.  
  1371. def set_cfg_file_host():
  1372. """
  1373. Sets the name of the switch config file to download based on hostname
  1374. received in the DHCP option. e.g conf_TestingSw.cfg
  1375. """
  1376. poap_log("Setting source cfg filename based on switch hostname")
  1377. if 'POAP_HOST_NAME' in os.environ:
  1378. poap_log("Host Name: [%s]" % os.environ['POAP_HOST_NAME'])
  1379. options["source_config_file"] = "conf_%s.cfg" % os.environ['POAP_HOST_NAME']
  1380. else:
  1381. poap_log("Host Name information missing, falling back to static mode")
  1382. poap_log("Selected conf file name : %s" % options["source_config_file"])
  1383.  
  1384.  
  1385. def set_cfg_file_location():
  1386. """
  1387. Sets the name of the switch config file to download based on cdp
  1388. information. e.g conf_switch_Eth1_32.cfg
  1389. """
  1390. poap_log("Setting source cfg filename")
  1391. poap_log("show cdp neighbors interface %s" % os.environ['POAP_INTF'])
  1392. cdp_output = cli("show cdp neighbors interface %s" % os.environ['POAP_INTF'])
  1393.  
  1394. if legacy:
  1395. cdp_lines = cdp_output[1].split("\n")
  1396. else:
  1397. cdp_lines = cdp_output.split("\n")
  1398.  
  1399. if len(cdp_lines) == 0:
  1400. abort("No CDP neighbor output for %s" % os.environ['POAP_INTF'])
  1401.  
  1402. if re.match("\s*Note:", cdp_lines[0]):
  1403. abort("No CDP neighbors found for %s" % os.environ['POAP_INTF'])
  1404.  
  1405. i = 0
  1406. while i < len(cdp_lines):
  1407. if cdp_lines[i].startswith("Device-ID"):
  1408. break
  1409. i += 1
  1410. else:
  1411. abort("Improper CDP output (missing heading): %s" % "\n".join(cdp_lines))
  1412. i += 1
  1413.  
  1414. cdp_info = [info for info in re.split("\s+", " ".join(cdp_lines[i:])) if info != ""]
  1415.  
  1416. switch_name_tuple = cdp_info[0].split("(")
  1417.  
  1418. # Split the serial number if it exists. Sometimes the serial number doesn't exist so
  1419. # just take the hostname as it is
  1420. if len(switch_name_tuple) in [1, 2]:
  1421. switch_name = switch_name_tuple[0]
  1422. else:
  1423. abort("Improper CDP output (name and serial number malformed): %s" % "\n".join(cdp_lines))
  1424.  
  1425. i = 0
  1426. while i < len(cdp_info):
  1427. # 7x code prints out total entries
  1428. if cdp_info[i] == "Total":
  1429. intf_name = cdp_info[i-1]
  1430. break
  1431. i += 1
  1432. else:
  1433. # 3K 6x and older releases don't print this info
  1434. intf_name = cdp_info[-1]
  1435.  
  1436. options["source_config_file"] = "conf_%s_%s.cfg" % (switch_name, intf_name)
  1437. options["source_config_file"] = options["source_config_file"].replace("/", "_")
  1438. poap_log("Selected conf file name : %s" % options["source_config_file"])
  1439.  
  1440.  
  1441. def get_version():
  1442. """
  1443. Gets the image version of the switch from CLI.
  1444. Output is handled differently for 6.x and 7.x or higher version.
  1445. """
  1446. cli_output = cli("show version")
  1447. if legacy:
  1448. result = re.search(r'system.*version\s*(.*)\n', cli_output[1])
  1449. if result is not None:
  1450. return result.group(1)
  1451. else:
  1452. result = re.search(r'NXOS.*version\s*(.*)\n', cli_output)
  1453. if result is not None:
  1454. return result.group(1)
  1455. poap_log("Unable to get switch version")
  1456.  
  1457.  
  1458. def get_bios_version():
  1459. """
  1460. Gets the BIOS version of the switch from CLI.
  1461. Output is handled differently for 6.x and 7.x/higher version.
  1462. """
  1463. cli_output = cli("show version")
  1464. if legacy:
  1465. result = re.search(r'BIOS.*version\s*(.*)\n', cli_output[1])
  1466. if result is not None:
  1467. return 07.56 #result.group(1)
  1468. else:
  1469. result = re.search(r'BIOS.*version\s*(.*)\n', cli_output)
  1470. if result is not None:
  1471. return 07.56 #result.group(1)
  1472. poap_log("Unable to get switch Bios version")
  1473.  
  1474.  
  1475. def install_bios():
  1476. """
  1477. Upgrade the bios when moving from 6.X to 7.X
  1478. """
  1479. single_image_path = os.path.join(options["destination_path"],
  1480. options["destination_system_image"])
  1481. single_image_path = single_image_path.replace("/bootflash", "bootflash:", 1)
  1482.  
  1483. bios_upgrade_cmd = "config terminal ; terminal dont-ask"
  1484. bios_upgrade_cmd += " ; install all nxos %s bios" % single_image_path
  1485. try:
  1486. cli(bios_upgrade_cmd)
  1487. except Exception as e:
  1488. s = os.statvfs("/bootflash/")
  1489. freespace = (s.f_bavail * s.f_frsize)
  1490. total_size = (s.f_blocks * s.f_frsize)
  1491. percent_free = (float(freespace) / float(total_size)) * 100
  1492. poap_log("%0.2f%% bootflash free" % percent_free)
  1493. abort("Bios install failed: %s" % str(e))
  1494.  
  1495. poap_log("Bios successfully upgraded to version %s" % get_bios_version())
  1496.  
  1497.  
  1498. def is_bios_upgrade_needed():
  1499. """
  1500. Check if bios upgrade is required. It's required when the current
  1501. bios is not 3.x and image upgrade is from 6.x to 7.x or higher
  1502. """
  1503. global single_image
  1504. last_upgrade_bios = 3
  1505. ver = get_version()
  1506. bios = get_bios_version()
  1507. poap_log("Switch is running version %s with bios version %s"
  1508. " image %s single_image %d" % (ver, bios, options["target_system_image"],
  1509. single_image))
  1510. if re.match("nxos.", options["target_system_image"]):
  1511. poap_log("Upgrading to a nxos image")
  1512. try:
  1513. bios_number = float(bios)
  1514. except ValueError:
  1515. major, minor = bios.split(".", 1)
  1516. try:
  1517. bios_number = int(major)
  1518. except ValueError:
  1519. poap_log("Could not convert BIOS '%s' to a number, using text match")
  1520. bios_number = bios
  1521. try:
  1522. chassis_out = cli("show chassis-family")
  1523. chassis = chassis_out.split()
  1524. if chassis[-1] == 'Fretta':
  1525. last_upgrade_bios = 1
  1526. except:
  1527. poap_log("Could not find chassis family.")
  1528.  
  1529. poap_log("Comparing present BIOS version %d with base version %d" % (bios_number, last_upgrade_bios))
  1530. if bios_number < last_upgrade_bios:
  1531. poap_log("Bios needs to be upgraded as switch is "
  1532. "running older bios version")
  1533. return True
  1534. poap_log("Bios upgrade not needed")
  1535. return False
  1536.  
  1537. def find_upgrade_index_from_match(image_info):
  1538. """
  1539. Given the current image match (in the form of a re match object), we
  1540. extract the version information and see where on the upgrade path it lies.
  1541. The returned index will indicate which upgrade is the next one.
  1542. The recommended upgrade path for N3K is below:
  1543. * older than 5.0(3)U5(1)
  1544. * 5.0(3)U5(1)
  1545. * 6.0(2)U6(2a)
  1546. * 6.0(2)U6(7)
  1547. * newer than 6.0(2)U6(7)
  1548. N9K images have always been greater revisions than anything in this path so this
  1549. method will return len(upgrade_path) on N9K
  1550. """
  1551. upgrade_path = [('5', '0', '3', '5', '1'), ('6', '0', '2', '6', '2a'),
  1552. ('6', '0', '2', '6', '7')]
  1553.  
  1554. major = image_info.group(1)
  1555. minor = image_info.group(2)
  1556. revision = image_info.group(3)
  1557. #Ignore the branch and release details for 9.2 or higher versions
  1558. if major < 9:
  1559. branch = image_info.group(4)
  1560. release = image_info.group(5)
  1561. else:
  1562. branch=0
  1563. release=0
  1564.  
  1565. i = 0
  1566.  
  1567. while i < len(upgrade_path):
  1568. # Major
  1569. if major < upgrade_path[i][0]:
  1570. return i
  1571. elif major > upgrade_path[i][0]:
  1572. i += 1
  1573. # Minor
  1574. elif minor < upgrade_path[i][1]:
  1575. return i
  1576. elif minor > upgrade_path[i][1]:
  1577. i += 1
  1578. # Revision
  1579. elif revision < upgrade_path[i][2]:
  1580. return i
  1581. elif revision > upgrade_path[i][2]:
  1582. i += 1
  1583. # Branch
  1584. elif branch < upgrade_path[i][3]:
  1585. return i
  1586. elif branch > upgrade_path[i][3]:
  1587. i += 1
  1588. # Release
  1589. elif release < upgrade_path[i][4]:
  1590. return i
  1591. elif release > upgrade_path[i][4]:
  1592. i += 1
  1593. else:
  1594. #Exact match, return next index
  1595. return i + 1
  1596. return i
  1597.  
  1598.  
  1599. def get_currently_booted_image_filename():
  1600. match = None
  1601. if legacy:
  1602. try:
  1603. output = cli("show version")[1]
  1604. except Exception as e:
  1605. abort("Show version failed: %s" % str(e))
  1606.  
  1607. match = re.search("system image file is:\s+(.+)", output)
  1608. else:
  1609. try:
  1610. output = cli("show version")
  1611. except Exception as e:
  1612. abort("Show version failed: %s" % str(e))
  1613.  
  1614. match = re.search("NXOS image file is:\s+(.+)", output)
  1615.  
  1616. if match:
  1617. directory, image = os.path.split(match.group(1))
  1618. return image.strip()
  1619. return ""
  1620.  
  1621.  
  1622. def set_next_upgrade_from_user():
  1623. """
  1624. Forces a 2 step upgrade if initiated by the user. We first check if we're currently on the
  1625. midway image, and if not, we make that the next image we're going to boot into.
  1626. """
  1627. global multi_step_install
  1628.  
  1629. current_image_file = get_currently_booted_image_filename()
  1630.  
  1631. # The currently booted image file is not the midway destination file. That means
  1632. # that we did not already do the midway step, and we need to go there next
  1633. if options["destination_midway_system_image"] != current_image_file:
  1634. poap_log("Destination midway image %s is not currently booted (%s)" %
  1635. (options["destination_midway_system_image"], current_image_file))
  1636.  
  1637. options["target_kickstart_image"] = options["midway_kickstart_image"]
  1638. options["target_system_image"] = options["midway_system_image"]
  1639.  
  1640. version = get_version()
  1641. poap_log("Next upgrade is to %s from %s (forced by user)" %
  1642. (options["midway_system_image"], version))
  1643.  
  1644. # Keep overwriting midway images
  1645. options["destination_kickstart_image"] = options["destination_midway_kickstart_image"]
  1646. options["destination_system_image"] = options["destination_midway_system_image"]
  1647. multi_step_install = True
  1648. else:
  1649. poap_log("Already on the midway image, upgrading to %s next." %
  1650. options["target_system_image"])
  1651.  
  1652.  
  1653. def set_next_upgrade_from_upgrade_path():
  1654. """Checks the currently running image and the target image to see where on the upgrade path
  1655. the images lie. If there's a recommended upgrade between the current image and the target image
  1656. then that recommended upgrade is used as a midway image to jump between the current image
  1657. and the target. Setting the "midway_system_image" and "midway_kickstart_image" options will
  1658. override the upgrade that is used if we detect that we need to follow the upgrade path.
  1659. """
  1660. global options
  1661. global multi_step_install
  1662.  
  1663. # 5.0(3)U5(1), 6.0(2)U6(2a), 6.0(2)U6(7), 7.0(3)I3(1)
  1664. upgrade_images = [["n3000-uk9-kickstart.5.0.3.U5.1.bin", "n3000-uk9.5.0.3.U5.1.bin"],
  1665. ["n3000-uk9-kickstart.6.0.2.U6.2a.bin", "n3000-uk9.6.0.2.U6.2a.bin"],
  1666. ["n3000-uk9-kickstart.6.0.2.U6.7.bin", "n3000-uk9.6.0.2.U6.7.bin"]]
  1667.  
  1668. # Check currently running image
  1669. version = get_version()
  1670.  
  1671. image_info = re.match("(\d+)\.(\d+)\((\d+)\)[A-Z]+(\d+)\((\w+)\)", version)
  1672. if image_info is None:
  1673. #try the regex match for 9.2(1) or higher version scheme
  1674. image_info = re.match("(\d+)\.(\d+)\((\d+)\)", version)
  1675. if image_info is None:
  1676. abort("Failed to extract image information from %s" % version)
  1677.  
  1678. current_idx = find_upgrade_index_from_match(image_info)
  1679.  
  1680. # Check the target image
  1681. image_info = re.search("[\w-]+\.(\d+)\.(\d+)\.(\d+)\.[A-Z]+(\d+)\.(\w+)",
  1682. options["target_system_image"])
  1683.  
  1684. if image_info is None:
  1685. #try the regex match for 9.2 or higher version scheme
  1686. image_info = re.search("[\w-]+\.(\d+)\.(\d+)\.(\d+)", options["target_system_image"])
  1687. if image_info is None:
  1688. poap_log("Failed to match target image: %s" % options["target_system_image"])
  1689. exit(1)
  1690.  
  1691. target_idx = find_upgrade_index_from_match(image_info)
  1692.  
  1693. if (target_idx - current_idx) > 0:
  1694. poap_log("Multi-level install is set")
  1695.  
  1696. # Update the target image with the upgrade path midway images
  1697. options["target_kickstart_image"] = upgrade_images[current_idx][0]
  1698. options["target_system_image"] = upgrade_images[current_idx][1]
  1699.  
  1700. poap_log("Next upgrade is %s from %s" % (str(upgrade_images[current_idx][1]), version))
  1701.  
  1702. # Keep overwriting midway images
  1703. options["destination_kickstart_image"] = options["destination_midway_kickstart_image"]
  1704. options["destination_system_image"] = options["destination_midway_system_image"]
  1705.  
  1706. multi_step_install = True
  1707. else:
  1708. poap_log("Multi-level install is not needed")
  1709.  
  1710.  
  1711. def download_personality_tarball():
  1712. """
  1713. Downloads the personality tarball and verifies if the md5 of the tar
  1714. matches with the value .md5 file downloaded.
  1715. """
  1716. md5_sum_given = None
  1717. if options["disable_md5"] is False:
  1718. copy_md5_info(options["personality_path"], options["destination_tarball"])
  1719. md5_sum_given = get_md5(options["destination_tarball"])
  1720. remove_file(os.path.join(options["destination_path"], "%s.md5" %
  1721. options["destination_tarball"]))
  1722. poap_log("MD5 for tar from server: %s" % md5_sum_given)
  1723. if md5_sum_given and os.path.exists(os.path.join(options["destination_path"],
  1724. options["destination_tarball"])):
  1725. if verify_md5(md5_sum_given, os.path.join(options["destination_path"],
  1726. options["destination_tarball"])):
  1727. poap_log("File %s already exists and MD5 matches" %
  1728. os.path.join(options["destination_path"],
  1729. options["destination_tarball"]))
  1730. return
  1731. elif not md5_sum_given:
  1732. abort("Invalid MD5 from server: %s" % md5_sum_given)
  1733. else:
  1734. poap_log("File %s does not exist on switch" %
  1735. options["destination_tarball"])
  1736.  
  1737. tarball_path = os.path.join(options["personality_path"], options["source_tarball"])
  1738. tmp_file = "%s.tmp" % options["destination_tarball"]
  1739. do_copy(tarball_path, options["destination_tarball"],
  1740. options["timeout_copy_personality"], tmp_file)
  1741.  
  1742. if options["disable_md5"] is False and md5_sum_given:
  1743. if not verify_md5(md5_sum_given, os.path.join(options["destination_path"],
  1744. options["destination_tarball"])):
  1745. abort("#### Tar file %s MD5 verification failed #####\n" %
  1746. os.path.join(options["destination_path"],
  1747. options["destination_tarball"]))
  1748. poap_log("INFO: Completed Copy of Tar file to %s" %
  1749. os.path.join(options["destination_path"],
  1750. options["destination_tarball"]))
  1751.  
  1752.  
  1753. def get_system_image_from_tarball():
  1754. """
  1755. Extracts the system image name from the tarball
  1756. """
  1757. global options
  1758.  
  1759. tarball_path = os.path.join(options["destination_path"], options["destination_tarball"])
  1760.  
  1761. tar = tarfile.open(tarball_path)
  1762.  
  1763. file_list = tar.getnames()
  1764. for file_name in file_list:
  1765. # Legacy personality support
  1766. match = re.search("IMAGEFILE_(.+)", file_name)
  1767. if match:
  1768. options["target_system_image"] = match.group(1)
  1769. # File container way
  1770. elif os.path.basename(file_name) == "IMAGEFILE":
  1771. options["target_system_image"] = tar.extractfile(file_name).read().strip()
  1772.  
  1773. if options.get("target_system_image") is None:
  1774. abort("Failed to find system image filename from tarball")
  1775.  
  1776. poap_log("Using %s as the system image" % options["target_system_image"])
  1777.  
  1778.  
  1779. def override_options_for_personality():
  1780. """
  1781. Overrides the existing options with the personality specific ones
  1782. """
  1783. global options
  1784.  
  1785. options["target_image_path"] = options["personality_path"]
  1786. poap_log("target_image_path option set to personality_path (%s)" % options["personality_path"])
  1787. if options["destination_system_image"]:
  1788. options["destination_system_image"] = options["target_system_image"]
  1789.  
  1790.  
  1791. def initialize_personality():
  1792. """
  1793. Initializes personality. Downloads the tarball and extracts the system image
  1794. """
  1795. download_personality_tarball()
  1796. get_system_image_from_tarball()
  1797. override_options_for_personality()
  1798.  
  1799.  
  1800. def setup_mode():
  1801. """
  1802. Sets the config file name based on the mode
  1803. """
  1804. supported_modes = ["location", "serial_number", "mac", "hostname",
  1805. "personality", "raw"]
  1806. if options["mode"] == "location":
  1807. set_cfg_file_location()
  1808. elif options["mode"] == "serial_number":
  1809. set_cfg_file_serial()
  1810. elif options["mode"] == "mac":
  1811. set_cfg_file_mac()
  1812. elif options["mode"] == "hostname":
  1813. set_cfg_file_host()
  1814. elif options["mode"] == "personality":
  1815. initialize_personality()
  1816. elif options["mode"] == "raw":
  1817. # Don't need to change the name of the config file
  1818. pass
  1819. else:
  1820. poap_log("Invalid mode selected: %s" % options["mode"])
  1821. poap_log("Mode must be one of the following: %s" % ", ".join(supported_modes))
  1822. abort()
  1823.  
  1824.  
  1825. def setup_logging():
  1826. """
  1827. Configures the log file this script uses
  1828. """
  1829. global log_hdl
  1830.  
  1831. if os.environ.get("POAP_PHASE", None) == "USB":
  1832. poap_script_log = "/bootflash/%s_poap_%s_usb_script.log" % (
  1833. strftime("%Y%m%d%H%M%S", gmtime()),
  1834. os.environ['POAP_PID'])
  1835. else:
  1836. poap_script_log = "/bootflash/%s_poap_%s_script.log" % (strftime("%Y%m%d%H%M%S", gmtime()),
  1837. os.environ['POAP_PID'])
  1838. log_hdl = open(poap_script_log, "w+")
  1839.  
  1840. poap_log("Logfile name: %s" % poap_script_log)
  1841.  
  1842. poap_cleanup_script_logs()
  1843.  
  1844.  
  1845. def check_multilevel_install():
  1846. """
  1847. Checks whether or not the multi-level install procedure is needed. Sets
  1848. multi_step_install to True if it is needed. Also sets single_image to
  1849. True if the target image is a 7x or higher image.
  1850. """
  1851. global options, single_image
  1852.  
  1853. # User wants to override the midway image
  1854. if options["midway_system_image"] != "":
  1855. set_next_upgrade_from_user()
  1856. else:
  1857. set_next_upgrade_from_upgrade_path()
  1858.  
  1859. if re.match("nxos.", options["target_system_image"]) \
  1860. or re.match("n9000", options["target_system_image"]):
  1861. poap_log("Single image is set")
  1862. single_image = True
  1863. else:
  1864. poap_log("Single image is not set")
  1865. single_image = False
  1866.  
  1867.  
  1868. def invoke_personality_restore():
  1869. """
  1870. Does a write erase (so POAP will run again) and invokes the
  1871. POAP Personality restore CLI.
  1872. """
  1873. cli("terminal dont-ask ; write erase")
  1874.  
  1875. try:
  1876. cli("personality restore %s user-name %s password %s hostname %s vrf %s" % (
  1877. options["destination_tarball"], options["username"], options["password"],
  1878. options["hostname"], options["vrf"]))
  1879. except Exception as e:
  1880. # If this fails, personality will have already thrown an error
  1881. abort("Personality has failed! (%s)" % str(e))
  1882.  
  1883.  
  1884. def cleanup_temp_images():
  1885. """
  1886. Cleans up the temporary images if they exist. These are the midway images
  1887. that are downloaded for multi-level install
  1888. """
  1889. if options["destination_kickstart_image"] != options["destination_midway_kickstart_image"]:
  1890. midway_kickstart = os.path.join(options["destination_path"],
  1891. options["destination_midway_kickstart_image"])
  1892. remove_file(midway_kickstart)
  1893. if options["destination_system_image"] != options["destination_midway_system_image"]:
  1894. midway_system = os.path.join(options["destination_path"],
  1895. options["destination_midway_system_image"])
  1896. remove_file(midway_system)
  1897.  
  1898.  
  1899. def main():
  1900. signal.signal(signal.SIGTERM, sigterm_handler)
  1901.  
  1902. # Set all the default parameters and validate the ones provided
  1903. set_defaults_and_validate_options()
  1904.  
  1905. # Configure the logging for the POAP process
  1906. setup_logging()
  1907.  
  1908. # Initialize parameters based on the mode
  1909. setup_mode()
  1910.  
  1911. # Set the prefix for syslogs based on the POAP mode
  1912. set_syslog_prefix()
  1913.  
  1914. # Verify there's enough space (and fail if not)
  1915. verify_freespace()
  1916.  
  1917. # Now that we know we're going to try and copy, let's create
  1918. # the directory structure needed, if any
  1919. create_destination_directories()
  1920.  
  1921. check_multilevel_install()
  1922. # In two step install we just copy the midway image and reboot.
  1923. # Config copy and script download happens in the second step.
  1924. if multi_step_install is False:
  1925. copy_config()
  1926.  
  1927. # Download user scripts and agents
  1928. download_scripts_and_agents()
  1929. # End of multi_step_install is False block
  1930.  
  1931. copy_system()
  1932.  
  1933. if single_image is False:
  1934. copy_kickstart()
  1935.  
  1936. signal.signal(signal.SIGTERM, sig_handler_no_exit)
  1937. # install images
  1938. if single_image is False:
  1939. install_images()
  1940. else:
  1941. install_images_7_x()
  1942.  
  1943.  
  1944. # Cleanup midway images if any
  1945. cleanup_temp_images()
  1946.  
  1947. # Invoke personality restore if personality is enabled
  1948. if options["mode"] == "personality":
  1949. invoke_personality_restore()
  1950. exit(0)
  1951.  
  1952. if empty_first_file is 0:
  1953. cli('copy bootflash:%s scheduled-config' % options["split_config_first"])
  1954. poap_log("Done copying the first scheduled cfg")
  1955. remove_file("/bootflash/%s" % options["split_config_first"])
  1956.  
  1957. cli('copy bootflash:%s scheduled-config' % options["split_config_second"])
  1958. poap_log("Done copying the second scheduled cfg")
  1959. remove_file(os.path.join("/bootflash", options["split_config_second"]))
  1960. log_hdl.close()
  1961. exit(0)
  1962.  
  1963.  
  1964. if __name__ == "__main__":
  1965. try:
  1966. main()
  1967. except Exception:
  1968. exc_type, exc_value, exc_tb = sys.exc_info()
  1969. poap_log("Exception: {0} {1}".format(exc_type, exc_value))
  1970. while exc_tb is not None:
  1971. fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
  1972. poap_log("Stack - File: {0} Line: {1}"
  1973. .format(fname, exc_tb.tb_lineno))
  1974. exc_tb = exc_tb.tb_next
  1975. abort()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement