tm_sw_autoconf.tcl 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. ::cisco::eem::description "This policy bootstraps a device using the serial number and PID parameters"
  2. ::cisco::eem::event_register_appl tag timer sub_system 798 type 1 maxrun 7200
  3. ::cisco::eem::event_register_none tag none
  4. ::cisco::eem::trigger {
  5. ::cisco::eem::correlate event timer or event none
  6. }
  7. namespace import ::cisco::eem::*
  8. namespace import ::cisco::lib::*
  9. namespace import ::http::*
  10. # CHANGE ME!!!
  11. set ZTP_IP {ZTP_SERVER_IP}
  12. set URL "http://$ZTP_IP/swreg/swreg.php"
  13. set VERIFY_URL "http://$ZTP_IP/swreg/verify.php"
  14. set TFTP_URL "tftp://$ZTP_IP"
  15. proc init {} {
  16. variable map
  17. variable alphanumeric a-zA-Z0-9
  18. for {set i 0} {$i <= 256} {incr i} {
  19. set c [format %c $i]
  20. if { ! [string match \[$alphanumeric\] $c] } {
  21. set map($c) %[format %.2x $i]
  22. }
  23. }
  24. array set map { " " + \n %0d%0a }
  25. }
  26. init
  27. proc url_encode {str} {
  28. variable map
  29. variable alphanumeric
  30. regsub -all \[^$alphanumeric\] $str {$map(&)} str
  31. regsub -all {[][{})\\]\)} $str {\\&} str
  32. return [subst -nocommand $str]
  33. }
  34. if { [catch {cli_open} result] } {
  35. error $result $errorInfo
  36. }
  37. array set cli $result
  38. if { [catch {cli_exec $cli(fd) "enable"} result] } {
  39. error $result $errorInfo
  40. }
  41. if { [catch {cli_exec $cli(fd) "show inventory"} result] } {
  42. error $result $errorInfo
  43. }
  44. if { ! [regexp {PID: (\S+)} $result -> pid] } {
  45. puts "ERROR: Failed to find PID in '$result'"
  46. exit 1
  47. }
  48. if { ! [regexp {SN: (\S+)} $result -> sn] } {
  49. puts "ERROR: Failed to find SN in '$result'"
  50. exit 1
  51. }
  52. if { [catch {cli_exec $cli(fd) "show version"} result] } {
  53. error $result $errorInfo
  54. }
  55. if { ! [regexp {Version ([^,\s]+)[,\s]} $result -> vers] } {
  56. puts "ERROR: Failed to find version in '$result'"
  57. exit 1
  58. }
  59. if { ! [regexp {System image file is "([^:]+:[^"]+)"} $result -> imagepath] } { ;#"
  60. puts "ERROR: Failed to find system image file in '$result'"
  61. exit 1
  62. }
  63. set fstype {flash:}
  64. set rawimagef [file tail $imagepath]
  65. set imaged [string trimright [file dirname $imagepath] "/"]
  66. regexp {([^:]+:)} $imagepath -> fstype
  67. if { [catch {cli_exec $cli(fd) "show ip int brie | include Ethernet"} result] } {
  68. error $result $errorInfo
  69. }
  70. set intfs 0
  71. foreach line [split $result "\n"] {
  72. if { [regexp {Ethernet} $line] } {
  73. incr intfs
  74. }
  75. }
  76. set vers [url_encode $vers]
  77. set imagef [url_encode $rawimagef]
  78. ::http::config -useragent "tm_sw_autoconf.tcl/1.0"
  79. set tok [::http::geturl "$URL?pid=$pid&sn=$sn&version=$vers&num_ports=$intfs&imagef=$imagef"]
  80. if { [::http::error $tok] != "" } {
  81. puts "ERROR: Failed to retrieve device info: '[::http::error $tok]'"
  82. exit 1
  83. }
  84. set image {}
  85. set config {}
  86. set sdm {}
  87. set pnp 0
  88. foreach line [split [::http::data $tok] "\n"] {
  89. if { [regexp {^Image: (\S+)} $line -> res] } {
  90. set image $res
  91. }
  92. if { [regexp {^Config: (\S+)} $line -> res] } {
  93. set config $res
  94. }
  95. if { [regexp {^SDM: (\S+)} $line -> res] } {
  96. set sdm $res
  97. }
  98. if { [regexp {^PNP} $line] } {
  99. set pnp 1
  100. }
  101. }
  102. if { $image == {} && $config == {} && $pnp == 0 } {
  103. puts "Switch not registered; rebooting..."
  104. after 60000
  105. action_reload
  106. }
  107. if { $image != {} } {
  108. if { [catch {cli_exec $cli(fd) "del /force $imagepath"} result] } {
  109. error $result $errorInfo
  110. }
  111. if { $imaged != $fstype } {
  112. if { [catch {cli_exec $cli(fd) "del /force /recursive $imaged"} result] } {
  113. error $result $errorInfo
  114. }
  115. }
  116. if { [catch {cli_exec $cli(fd) "del /force $fstype$rawimagef"} result] } {
  117. error $result $errorInfo
  118. }
  119. if { [catch {cli_exec $cli(fd) "config t"} result] } {
  120. error $result $errorInfo
  121. }
  122. if { [catch {cli_exec $cli(fd) "ip tftp blocksize 8192"} result] } {
  123. error $result $errorInfo
  124. }
  125. if { [catch {cli_exec $cli(fd) "end"} result] } {
  126. error $result $errorInfo
  127. }
  128. if { [catch {cli_exec $cli(fd) "copy $TFTP_URL/$image $fstype"} result] } {
  129. error $result $errorInfo
  130. }
  131. # Log result in case this fails
  132. action_syslog msg "Output of copy command: '$result'"
  133. if { [catch {cli_exec $cli(fd) "config t"} result] } {
  134. error $result $errorInfo
  135. }
  136. # Workaround busted boot variable setting on 9300 switches.
  137. set fd [open "${fstype}boot_hack.txt" w]
  138. puts $fd "\nboot system $fstype$image\nend"
  139. close $fd
  140. if { [catch {cli_exec $cli(fd) "end"} result] } {
  141. error $result $errorInfo
  142. }
  143. if { [catch {cli_exec $cli(fd) "copy ${fstype}boot_hack.txt start"} result] } {
  144. error $result $errorInfo
  145. }
  146. if { [catch {cli_exec $cli(fd) "config t"} result] } {
  147. error $result $errorInfo
  148. }
  149. if { [catch {cli_exec $cli(fd) "boot system $fstype$image"} result] } {
  150. error $result $errorInfo
  151. }
  152. if { [catch {cli_exec $cli(fd) "no ip tftp blocksize 8192"} result] } {
  153. error $result $errorInfo
  154. }
  155. if { [catch {cli_exec $cli(fd) "end"} result] } {
  156. error $result $errorInfo
  157. }
  158. if { [catch {cli_exec $cli(fd) "write mem"} result] } {
  159. error $result $errorInfo
  160. }
  161. file delete -force ${fstype}boot_hack.txt
  162. if { [regexp {packages.conf} $imagepath] } {
  163. if { [catch {cli_exec $cli(fd) "del /force ${fstype}*.pkg"} result] } {
  164. error $result $errorInfo
  165. }
  166. if { [catch {cli_exec $cli(fd) "del /force ${fstype}packages.conf"} result] } {
  167. error $result $errorInfo
  168. }
  169. }
  170. if { [catch {cli_exec $cli(fd) "show boot | inc $image"} result] } {
  171. error $result $errorInfo
  172. }
  173. if { ! [regexp "$image" $result] } {
  174. action_syslog msg "FAILED BOOT: Failed to set boot variable: '$result'"
  175. exit 1
  176. }
  177. }
  178. if { $config != {} } {
  179. if { [catch {cli_exec $cli(fd) "copy $TFTP_URL/device-configs/$config start"} result] } {
  180. error $result $errorInfo
  181. }
  182. if { [catch {cli_exec $cli(fd) "show switch | inc ^\\*"} result] } {
  183. error $result $errorInfo
  184. }
  185. if { [regexp {\*([2-9])} $result -> swn] } {
  186. if { [catch {cli_exec $cli(fd) "config t"} result] } {
  187. error $result $errorInfo
  188. }
  189. if { [catch {cli_write $cli(fd) "switch $swn renumber 1"} result] } {
  190. error $result $errorInfo
  191. }
  192. if { [catch {cli_read_pattern $cli(fd) "(confirm|continue)"} result] } {
  193. error $result $errorInfo
  194. }
  195. if { [catch {cli_exec $cli(fd) "\r"} result] } {
  196. error $result $errorInfo
  197. }
  198. if { [catch {cli_exec $cli(fd) "end"} result] } {
  199. error $result $errorInfo
  200. }
  201. }
  202. if { [catch {cli_exec $cli(fd) "license right-to-use activate ipservices acceptEULA"} result] } {
  203. puts "WARNING: Failed to change license: '$result'"
  204. }
  205. if { [regexp {Invalid input} $result] } {
  206. if { [catch {cli_exec $cli(fd) "license right-to-use activate ipservices all acceptEULA"} result] } {
  207. puts "WARNING: Failed to change license: '$result'"
  208. }
  209. }
  210. }
  211. if { $sdm != {} } {
  212. if { [catch {cli_exec $cli(fd) "config t"} result] } {
  213. error $result $errorInfo
  214. }
  215. if { [catch {cli_exec $cli(fd) "sdm prefer $sdm"} result] } {
  216. error $result $errorInfo
  217. }
  218. if { [catch {cli_exec $cli(fd) "end"} result] } {
  219. error $result $errorInfo
  220. }
  221. }
  222. set md5 {}
  223. set verify_image {}
  224. if { $image != {} } {
  225. if { [catch {cli_exec $cli(fd) "verify /md5 $fstype$image"} result] } {
  226. error $result $errorInfo
  227. }
  228. if { ! [regexp {= ([A-Fa-f0-9]+)} $result -> md5] } {
  229. action_syslog msg "MD5 VALIDATION FAILED: '$result'"
  230. exit 1
  231. }
  232. set verify_image [url_encode $image]
  233. }
  234. if { $config != {} || $image != {} } {
  235. if { $config != {} } {
  236. if { [catch {cli_exec $cli(fd) "copy start ${TFTP_URL}/device-tmp/$config"} result] } {
  237. error $result $errorInfo
  238. }
  239. # Wait a max of five seconds for the tftp daemon to flush its write buffer.
  240. after 5000
  241. }
  242. ::http::config -useragent "tm_sw_autoconf.tcl/1.0"
  243. set tok [::http::geturl "$VERIFY_URL?config=$config&sn=$sn&md5=$md5&image=$verify_image"]
  244. if { [::http::error $tok] != "" } {
  245. puts "ERROR: Failed to verify device config: '[::http::error $tok]'"
  246. exit 1
  247. }
  248. foreach line [split [::http::data $tok] "\n"] {
  249. if { [regexp {ERROR:} $line] } {
  250. action_syslog msg "AUTOCONF FAILED: Bootstrap verification failed: '$line'"
  251. exit 1
  252. }
  253. }
  254. if { $config != {} } {
  255. # XXX: This seems weird, but it's required to make sure the VLAN database
  256. # is properly updated upon reboot. By loading the startup config into
  257. # running before rebooting, this reliable ensures the VLAN database is
  258. # consistent.
  259. if { [catch {cli_exec $cli(fd) "config mem"} result] } {
  260. error $result $errorInfo
  261. }
  262. }
  263. }
  264. if { $pnp == 1 } {
  265. if { [catch {cli_exec $cli(fd) "test pnpa discovery process"} result] } {
  266. error $result $errorInfo
  267. }
  268. # At this point, PnP takes over.
  269. # TODO Need to confirm that the PnP discovery is successful
  270. }
  271. catch {cli_close $cli(fd) $cli(tty_id)}
  272. action_syslog msg "AUTOCONF COMPLETE: Switch is ready."
  273. action_reload