Advertisement
aramideo

Untitled

Jun 29th, 2018
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.30 KB | None | 0 0
  1. #!/usr/bin/env python2.7
  2. import sys
  3. import argparse
  4. import os, re
  5. from csv import DictReader,reader
  6. import shutil
  7. import commands
  8. import fileinput
  9. import getpass
  10. import subprocess
  11. '''
  12.  
  13. CURRENT VERSION is 3.0
  14.  
  15. This script is to improve the way we currently use to create AddLinksToLag CMs.
  16. It relies on a cutsheets format file with the information of Routers, Ports and
  17. LAGs to be implemented. Based on that it will do the following:
  18.  
  19. 1- Check for unstaged changes on GB & CM repo
  20. 2- Updating GB using arepa.py
  21. 3- Call gen.sh and generate config files locally on GB out folder.
  22. 4- Generate var file using cutsheet.py and create the CM folder to hold it on the ~/NetengIS_CM2015 repo.
  23. 5- Create all the push files from GB out folder to the CM folder.
  24. 6- Generate diffs within the CM folder.
  25. 7- Commit all the changes.
  26. 8- Update commit ID on VAR file, push and lock the CM.
  27.  
  28. '''
  29. #######
  30.  
  31. ##### Getting Vars from Arguments ######
  32.  
  33. parser = argparse.ArgumentParser(description= 'Automatic way to create an AddLinksToLag CM within Border space.'
  34. ,epilog="Example: ./addlinkstolag_cm_prep_v3.py -t ~/cutsheet_test_all.csv -v --mcm MCM-13091682")
  35. parser.add_argument("-c","--mcm", help="MCM number",required=True)
  36. parser.add_argument("-t","--cutsheet", help="Name of the cutsheet file",required=True)
  37. parser.add_argument("-p","--cutsheetsdotpy", help="Full path to cutsheets.py. By default is ~/NetengISRepo/scripts/cutsheet.py ",required=False)
  38. parser.add_argument("-v","--verbose", help="Enable verbose output",action="store_true",default=False)
  39. parser.add_argument("-l","--lock", help="Lock the CM after VAR, Config and diffs has been created",action="store_true",default=False)
  40. parser.add_argument("-g","--skip-gb-update", help="If GB has been updated manually, skip this step during CM creation.",action="store_true",default=False)
  41. parser.add_argument("-d","--snow-forted-region", help="Adds the no-cred extra attr to gen.sh the config. Type password manually for diff creation.",action="store_true",default=False)
  42. parser.add_argument("-x","--dx-peer", help="Enables this script to AddLinksToLag on border side only towards VC-CAR.",action="store_true",default=False)
  43.  
  44.  
  45. args = parser.parse_args()
  46.  
  47. # Display function.
  48. def say(msg, level=1,f_verbose = 1):
  49. if verbose:
  50. f_verbose = 2
  51. if (level <= f_verbose):
  52. print msg
  53.  
  54. # Getting username
  55. def get_username():
  56. user = getpass.getuser()
  57. return user
  58.  
  59. def get_password():
  60. password = getpass.getpass()
  61. return password
  62.  
  63. def load_csv(cfile):
  64. lines = []
  65. cparts = []
  66. skipped = 0
  67.  
  68. columns = ['a_hostname', 'a_lag', 'a_interface', 'z_hostname', 'z_lag', 'z_interface','action']
  69. say("\033[93mLoading cutsheet from %s.\033[0m" % (cfile),2)
  70. try:
  71. CSHEET = open(cfile, "rb")
  72. try:
  73. headers = reader(CSHEET).next()
  74. rdr = DictReader(CSHEET, headers)
  75. for col in columns:
  76. if (not col in rdr.fieldnames):
  77. CSHEET.close()
  78. say("Could not locate field %s in cutsheet.\033[0m" % (col))
  79. sys.exit()
  80. for row in rdr:
  81. parts = [ row['a_hostname'], row['a_lag'], row['a_interface'], row['z_interface'], row['z_lag'], row['z_hostname'], row['action'] ]
  82. parts = [p.strip() for p in parts]
  83.  
  84. if ("" in parts):
  85. say("\033[93mSkipping line: %s\033[0m" % (",".join(parts)),2)
  86. skipped += 1
  87. continue
  88. lines.append(parts)
  89. finally:
  90. CSHEET.close()
  91. except IOError, e:
  92. say("Cannot open %s.\n%s\033[0m" % (cfile, e))
  93. say("\033[93mCutsheet loaded: %d rows - %d rows skipped\033[0m" % (len(lines), skipped),2)
  94. return lines
  95.  
  96. # Getting a uniq list of routers based on cutsheets
  97. def router_list_uniq_function(rows):
  98. router_list_uniq = []
  99. for row in rows:
  100. if row[0] not in router_list_uniq and re.search("-(br|en)-",row[0]):
  101. router_list_uniq.append(row[0])
  102. if row[5] not in router_list_uniq and re.search("-(br|en)-",row[5]):
  103. router_list_uniq.append(row[5])
  104. return sorted(router_list_uniq)
  105.  
  106. # Getting a Router:Lag dict from cutsheet file
  107. def router_lag_dict_list_function(rows):
  108. router_lag_dict_list = []
  109. for row in rows:
  110. router_lag_l_dict = {}
  111. router_lag_r_dict = {}
  112. r=row[0]
  113. l=row[1]
  114. rr=row[5]
  115. rl=row[4]
  116. router_lag_l_dict[r]=l
  117. router_lag_r_dict[rr]=rl
  118. if router_lag_l_dict not in router_lag_dict_list and re.search("-(br|en)-", router_lag_l_dict.keys()[0]):
  119. router_lag_dict_list.append(router_lag_l_dict)
  120. if router_lag_r_dict not in router_lag_dict_list and re.search("-(br|en)-", router_lag_r_dict.keys()[0]):
  121. router_lag_dict_list.append(router_lag_r_dict)
  122. return sorted(router_lag_dict_list)
  123.  
  124. # Merge created branch with mailine and push changes
  125. def merge_and_push(repo,push=False):
  126. try:
  127. msj = "Cannot merge CM branch with mainline on {0} repo.".format(repo)
  128. os.chdir("/home/{0}/{1}/".format(alias,repo))
  129. say("\033[93mMerging branches\033[0m",2)
  130. subprocess.check_call(['git','checkout','mainline'])
  131. subprocess.check_call(['git','pull','--rebase'])
  132. msj = "Changes has been merged with mainline on {0}".format(repo)
  133. subprocess.check_call(['git','merge','{0}_{1}'.format(alias,args.mcm)])
  134. if push:
  135. msj = "Changes has been pushed on {0}".format(repo)
  136. subprocess.check_call(['git','push'])
  137. #print "\033[92mGIT PUSH\033[0m"
  138. say("\033[92m[PASS]\033[0m {0}".format(msj),2)
  139. except:
  140. say("\033[91m[FAIL]\033[0m {0}".format(msj))
  141. sys.exit()
  142.  
  143. #Create and delete branches on any repo where is needed
  144. def git_branch(repo,action="none",msj="none"):
  145. try:
  146. os.chdir("/home/{0}/{1}/".format(alias,repo))
  147. say("\033[93mDeleting branch {0}_{1} if exist!\033[0m".format(alias,args.mcm),2)
  148. subprocess.check_call(['git','checkout','mainline'])
  149. subprocess.check_call(['git','branch','-D','{0}_{1}'.format(alias,args.mcm)])
  150. except:
  151. say("\033[93mSkipping step since branch {0}_{1} was not found.\033[0m".format(alias,args.mcm),2)
  152.  
  153. if action == "set":
  154. try:
  155. subprocess.check_call(['git','pull','--rebase'])
  156. subprocess.check_call(['git','checkout','-b','{0}_{1}'.format(alias,args.mcm)])
  157. subprocess.check_call(['git','branch'])
  158. say("\033[93mNew branch {0}_{1} has been created in {2}\033[0m".format(alias,args.mcm,repo),2)
  159. except:
  160. msj = "Cannot create branch in {0}".format(repo)
  161. say("\033[91m[FAIL]\033[0m {0}".format(msj))
  162. sys.exit()
  163.  
  164. #Check if a directory exist and delete it
  165. def file_exist(path):
  166. if os.path.exists(path):
  167. shutil.rmtree(path)
  168.  
  169.  
  170. #Check if a directory exist and trigger an error if it doesnt
  171. def check_file_exist(file_path):
  172. try:
  173. with open(file_path) as f:
  174. f.read()
  175. except:
  176. say("\033[91m[FAIL]\033[0m This file doesn't exist --> {0}".format(file_path))
  177. sys.exit()
  178.  
  179. # Validate path to cutshets.py
  180. def check_cutsheetsdotpy_path(cdotpy_path=None):
  181. if cdotpy_path:
  182. try:
  183. check_file_exist(cdotpy_path)
  184. return cdotpy_path
  185. except:
  186. say("\033[91m[FAIL]\033[0m Seems like {0} doesn't exist.Please check the path to cutsheet.py".format(cdotpy_path))
  187. else:
  188. try:
  189. cdotpy_path = "/home/{0}/NetengISRepo/scripts/cutsheet.py".format(alias)
  190. check_file_exist(cdotpy_path)
  191. return cdotpy_path
  192. except:
  193. say("\033[91m[FAIL]\033[0m Seems like {0} doesn't exist.Please check the path to cutsheet.py".format(cdotpy_path))
  194.  
  195. # Find config in the out folder when ALTL to a vc-car
  196. def get_out_cfg_file(text_to_find,br_router):
  197. gb_out_path = "/home/{0}/GenevaBuilder/out/{1}/dxpeer_substreams/".format(get_username(),br_router)
  198. try:
  199. for attr in os.listdir(gb_out_path):
  200. with open('{0}/{1}'.format(gb_out_path,attr), 'r') as file :
  201. attrdata = file.read()
  202. if text_to_find in attrdata:
  203. out_cfg_file = '{0}/{1}'.format(gb_out_path,attr)
  204. return out_cfg_file
  205. except:
  206. sys.exit("Fail while attempting to find config in GB out folder!")
  207.  
  208. #1.- Checking it there is any unstaged change on GB or NetengIS_CM2015 repo
  209. def check_for_unstaged_changes():
  210. try:
  211. cmpath = os.path.isdir("/home/{0}/{1}/cm/{0}".format(alias,"NetengIS_CM2015"))
  212. gbpath = os.path.isdir("/home/{0}/{1}".format(alias,"GenevaBuilder"))
  213. if cmpath == False:
  214. errmsg = "Your alias folder doesn't exist as /home/{0}/{1}/cm/{0}".format(alias,"NetengIS_CM2015")
  215. sys.exit()
  216. elif gbpath == False:
  217. errmsg = "GenevaBuilder repo does't not exist as /home/{0}/{1}".format(alias,"GenevaBuilder")
  218. sys.exit()
  219.  
  220. errmsg = "You have unstaged changes. Please clear out your {0} repo.".format("NetengIS_CM2015")
  221. os.chdir("/home/{0}/{1}/".format(alias,"NetengIS_CM2015"))
  222. subprocess.check_call(['git','checkout','mainline'])
  223. subprocess.check_call(['git','pull','--rebase'])
  224.  
  225. errmsg = "You have unstaged changes. Please clear out your {0} repo.".format("GenevaBuilder")
  226. os.chdir("/home/{0}/{1}/".format(alias,"GenevaBuilder"))
  227. subprocess.check_call(['git','checkout','mainline'])
  228. subprocess.check_call(['git','pull','--rebase'])
  229. say("\033[92m[PASS]\033[0m..",2)
  230.  
  231. except:
  232. say("\033[91m[FAIL]\033[0m {0}".format(errmsg))
  233. sys.exit()
  234.  
  235. #2.- Udating GB
  236. def generate_lag_attr(cutsheet,pwd,skip_gb=False):
  237. if skip_gb:
  238. say("\033[92m[PASS]\033[0m Skipping this step since '--skip-gb-update' flag was included.")
  239. else:
  240. try:
  241. #fail_msj = "Cannot create a branch of GenevaBuilder repo"
  242. #os.chdir("/home/{}/{}/".format(alias,"GenevaBuilder"))
  243. #git_branch("GenevaBuilder","set",fail_msj)
  244. os.chdir(pwd)
  245. subprocess.check_call(['/home/{0}/NetengISRepo/people/chezlo/arepa.py'.format(alias),'-c','{0}'.format(cutsheet)])
  246. os.chdir("/home/{0}/GenevaBuilder/".format(alias))
  247. #subprocess.check_call(['git','add','-A'])
  248. commit_msg="\"Updating GB for {0}\"".format(args.mcm)
  249. #say("\033[96mCommiting changes\033[0m",2)
  250. #subprocess.check_call(['git','commit','-a','-m',commit_msg])
  251. #merge_and_push("GenevaBuilder")
  252. #git_branch("GenevaBuilder")
  253. say("\033[92m[PASS]\033[0m..",2)
  254. except:
  255. say("\033[91m[FAIL]\033[0m Something went wrong while updating GB ATTR! Check above log!")
  256. git_branch("GenevaBuilder")
  257. sys.exit()
  258.  
  259.  
  260. #3.- Generating config files
  261. def config_gen_gb(device_list,snow_forted_region=False):
  262. try:
  263. say("\033[96mGenerating configuration for:\033[0m")
  264. for i in range(0,len(device_list)):
  265. file_exist("/home/{0}/GenevaBuilder/out/{1}".format(alias,device_list[i]))
  266. say("\033[96m - {0}\033[0m".format(device_list[i]))
  267. os.chdir("/home/{0}/GenevaBuilder/".format(alias))
  268. if snow_forted_region:
  269. os.system("\n./gen.sh {0} --max-workers 16 --load-only {1}.* --extra-attrs extra-attrs/nocreds.attr".format(" ".join(device_list),device_list[0][:3]))
  270. else:
  271. os.system("\n./gen.sh {0} --max-workers 16 --load-only {1}.*".format(" ".join(device_list),device_list[0][:3]))
  272. for i in range(0,len(device_list)):
  273. check_file_exist("/home/{0}/GenevaBuilder/out/{1}/lagmembers".format(alias,device_list[i]))
  274. except:
  275. say("\033[91m[FAIL]\033[0m Couldn't generate config files for {0}".format(" ".join(device_list)))
  276. sys.exit()
  277.  
  278.  
  279. #4.- Creating VAR file on CM folder. Your CM directory will be created on this step.
  280. def create_varfile(cutsheet_path,cdotpy_path,pwd,fail_msj="none"):
  281. source_file="/home/{0}/NetengIS_CM2015/cm/{0}/{1}/{1}".format(alias,args.mcm)
  282. dest_file="/home/{0}/NetengIS_CM2015/cm/{0}/{1}/{1}.var".format(alias,args.mcm)
  283. rm_file="/home/{0}/NetengIS_CM2015/cm/{0}/{1}/{1}.ds".format(alias,args.mcm)
  284. try:
  285. fail_msj = "Cannot create CM folder in ~/NetengIS_CM2015/cm/{0}/{1}".format(alias,args.mcm)
  286. git_branch("NetengIS_CM2015","set",fail_msj)
  287. file_exist("/home/{0}/NetengIS_CM2015/cm/{0}/{1}".format(alias,args.mcm))
  288. say("\033[93mCreating new {0} folder on \"NetengIS_CM2015\" repository.....\033[0m".format(args.mcm),2)
  289. os.makedirs("/home/{0}/NetengIS_CM2015/cm/{0}/{1}".format(alias,args.mcm))
  290.  
  291. fail_msj = "Something when wrong while creating your VAR file. Please check above log!!"
  292. os.chdir(pwd)
  293. os.system("{0} -c --cutsheet={1} --out=~/NetengIS_CM2015/cm/{2}/{3} --cm={3}".format(cdotpy_path,cutsheet_path,alias,args.mcm))
  294. #TODO subprocess.check_call(['{}'.format(args.cutsheetsdotpy),'-c','--cutsheet','{}'format(cutsheets),'--out','~/NetengIS_CM2015/cm/{}/{}'.format(alias,args.mcm),'--cm','{}'.format(args.mcm)])
  295. os.rename(source_file,dest_file)
  296. os.remove(rm_file)
  297. say("\033[92m[PASS]\033[0m Your VAR file has been generating and compiled successfully..",2)
  298. except:
  299. say("\033[91m[FAIL]\033[0m {0}".format(fail_msj))
  300. git_branch("NetengIS_CM2015")
  301. sys.exit()
  302.  
  303.  
  304. #5.- Copying config files from GB out folder to the CM directory.
  305. def copy_push_files(router_lag_dict_list,dxpeer=False):
  306. for i in router_lag_dict_list:
  307. try:
  308. rou=i.keys()[0]
  309. ae=i.values()[0]
  310. if dxpeer:
  311. source_file=get_out_cfg_file(ae,rou)
  312. else:
  313. source_file="/home/{0}/GenevaBuilder/out/{1}/lagmembers_substreams/{2}".format(alias,rou,ae)
  314. dest_file="/home/{0}/NetengIS_CM2015/cm/{1}/{2}/{3}_{4}.push".format(alias,alias,args.mcm,rou,ae)
  315. say("\033[93mCopying config files from GB out folder to the CM directory!!\033[0m",2)
  316. check_file_exist(source_file)
  317. shutil.copyfile(source_file,dest_file)
  318. say("Copying \"{0}\" to \"{1}\"".format(source_file,dest_file),2)
  319. except:
  320. say("\033[91m[FAIL]\033[0m Cannot copy {0} to {1}".format(source_file,dest_file))
  321. sys.exit()
  322.  
  323. say("\033[92m[PASS]\033[0m..",2)
  324.  
  325. #6.- Generating Diffs
  326. def create_diff(router_lag_dict_list,password,snow_forted_region=False):
  327. for i in router_lag_dict_list:
  328. try:
  329. rou=i.keys()[0]
  330. ae=i.values()[0]
  331. os.chdir("/home/{0}/NetengIS_CM2015/cm/{0}/{1}".format(alias,args.mcm))
  332. if snow_forted_region == True:
  333. subprocess.check_call(['jdop','-l','{0}'.format(alias),'-p','{0}'.format(password),'-d','{0}_{1}.diff'.format(rou,ae),'diff','{0}'.format(rou),'{0}_{1}.push'.format(rou,ae)])
  334. else:
  335. credentials = "com.amazon.networking.managed.prod.tacacs-user.neteng-auto-rw"
  336. subprocess.check_call(['jdop','-x',credentials,'-d','{0}_{1}.diff'.format(rou,ae),'diff','{0}'.format(rou),'{0}_{1}.push'.format(rou,ae)])
  337.  
  338. check_file_exist("{0}_{1}.diff".format(rou,ae))
  339. say("\033[93m{0}_{1}.diff has been created successfully\n\033[0m".format(rou,ae),2)
  340. except:
  341. say("\033[91m[FAIL]\033[0m This diff was not created => {0}_{1}.diff".format(rou,ae))
  342. sys.exit()
  343.  
  344. say("\033[92m[PASS]\033[0m..",2)
  345.  
  346. #7.- Commiting changes to NetengIS_CM2015 repo.
  347. def commit_changes_CM_repo(commit_msg="\"adding VAR, PUSH and DIFFs for {0}\"".format(args.mcm)):
  348. try:
  349. say("\033[93mGit adding changes to commit!!\033[0m",2)
  350. os.chdir("/home/{0}/NetengIS_CM2015/".format(alias))
  351. subprocess.check_call(['git','add','-A'])
  352.  
  353. say("\033[93mCommiting changes\033[0m",2)
  354. subprocess.check_call(['git','commit','-a','-m',commit_msg])
  355. #git_branch("NetengIS_CM2015")
  356. say("\033[92m[PASS]\033[0m..",2)
  357.  
  358. except:
  359. say("\033[91m[FAIL]\033[0m Couldn't commit the changes. Check above log")
  360. sys.exit()
  361.  
  362.  
  363. #8.- Updating commit id, merge braches and lock the CM
  364. def update_cid_lockcm(lock_cm):
  365. try:
  366. error_msg = "Cannot update commit ID for {0}".format(args.mcm)
  367. os.chdir("/home/{0}/NetengIS_CM2015/".format(alias))
  368. output = commands.getoutput("git rev-parse HEAD")
  369. updated_wdir = "set_working_dir(\"brazil://NetengIS_CM2015/cm/{0}/{1}\",\"{2}\")".format(alias,args.mcm,output)
  370. sed_path = "/home/{1}/NetengIS_CM2015/cm/{1}/{0}/{0}.var".format(args.mcm,alias)
  371. sed_find = '/set_working_dir/c\{0}'.format(updated_wdir)
  372. subprocess.check_call(['sed','-i',sed_find,sed_path])
  373. commit_changes_CM_repo("\"Updating Commit ID for {0}\"".format(args.mcm))
  374. say("\033[93mCommit ID on {0}.var has been updated with: {1}\033[0m".format(args.mcm,output),2)
  375. error_msg = "Cannot lock MCM {0}. Check above log".format(args.mcm)
  376. merge_and_push("NetengIS_CM2015",True)
  377. if lock_cm:
  378. subprocess.check_call(['/apollo/env/Daryl/bin/darylscriptc','--lock','--cm','{0}'.format(args.mcm)])
  379. say("\033[92m[PASS]\033[0m CM has been locked.. https://mcm.amazon.com/{0}\n".format(args.mcm))
  380. else:
  381. say("\033[92m[PASS]\033[0m Skipping lock step since '--lock' flag was not included.")
  382.  
  383. except:
  384. say("\033[91m[FAIL]\033[0m {0}".format(error_msg))
  385. sys.exit()
  386.  
  387.  
  388.  
  389. def main():
  390. global verbose,alias
  391. verbose = args.verbose
  392. alias = get_username()
  393. lock_cm = args.lock
  394. skip_gb = args.skip_gb_update
  395. snow_forted_region = args.snow_forted_region
  396. dxpeer = args.dx_peer
  397. if snow_forted_region:
  398. password = get_password()
  399. else:
  400. password = "None"
  401. pwd = os.getcwd()
  402. cutsheets = load_csv(args.cutsheet)
  403. device_list = router_list_uniq_function(cutsheets)
  404. router_lag_dict_list = router_lag_dict_list_function(cutsheets)
  405. cdotpy_path = check_cutsheetsdotpy_path(args.cutsheetsdotpy)
  406.  
  407. say("\033[96m[STEP 1 of 8]\033[0m Checking for unstaged changes on CM & GB repo..")
  408. check_for_unstaged_changes()
  409. say("\033[96m[STEP 2 of 8]\033[0m Updating GenevaBuilder.")
  410. generate_lag_attr(args.cutsheet,pwd,skip_gb)
  411. say("\033[96m[STEP 3 of 8]\033[0m Generating config on GB out folder.")
  412. config_gen_gb(device_list,snow_forted_region)
  413. say("\033[96m[STEP 4 of 8]\033[0m Creating variable file")
  414. create_varfile(args.cutsheet,cdotpy_path,pwd)
  415. say("\033[96m[STEP 5 of 8]\033[0m Creating PUSH files on the CM folder")
  416. copy_push_files(router_lag_dict_list,dxpeer)
  417. say("\033[96m[STEP 6 of 8]\033[0m Generating DIFFs")
  418. create_diff(router_lag_dict_list,password,snow_forted_region)
  419. say("\033[96m[STEP 7 of 8]\033[0m Commiting all changes")
  420. commit_changes_CM_repo()
  421. say("\033[96m[STEP 8 of 8]\033[0m Updating commit ID and locking the CM")
  422. update_cid_lockcm(lock_cm)
  423. say("\033[96m[ALL STEPS EXECUTED] Don't forget to push GB changes.\033[0m")
  424.  
  425. if __name__ == "__main__":
  426. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement