Guest User

Untitled

a guest
Oct 18th, 2016
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.75 KB | None | 0 0
  1. #!/usr/bin/python
  2.  
  3. import sys
  4. import os
  5. import getpass
  6. import base64
  7. import json
  8. import codecs
  9. import urllib
  10. import urllib2
  11. import smtplib
  12. import datetime
  13. import re
  14. import subprocess
  15.  
  16.  
  17.  
  18. ############# INPUT AND DEFAULT VARIABLES #############
  19.  
  20. def printUsage():
  21. print('')
  22. print('Usage:')
  23. print(' ' + os.path.basename(__file__) + ' deployment-init <ENVIRONMENT> <SMTP_HOST> <SMTP_FROM> <SMTP_TO> <BUILD_NUMBER> <PROJECT_VERSION>')
  24. print(' ' + os.path.basename(__file__) + ' deployment-planned <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
  25. print(' ' + os.path.basename(__file__) + ' deployment-planned-nologs <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
  26. print(' ' + os.path.basename(__file__) + ' deployment-planned-nojira <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
  27. print(' ' + os.path.basename(__file__) + ' deployment-started')
  28. print(' ' + os.path.basename(__file__) + ' deployment-status <MANDATORY TEXT MESSAGE>')
  29. print(' ' + os.path.basename(__file__) + ' deployment-complete')
  30. print('')
  31. print(' ' + os.path.basename(__file__) + ' health-check')
  32. print('')
  33. print(' ' + os.path.basename(__file__) + ' restart-planned <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
  34. print(' ' + os.path.basename(__file__) + ' restart-started')
  35. print(' ' + os.path.basename(__file__) + ' restart-status <MANDATORY TEXT MESSAGE>')
  36. print(' ' + os.path.basename(__file__) + ' restart-complete')
  37. print('')
  38. print(' ' + os.path.basename(__file__) + ' shutdown-planned <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
  39. print(' ' + os.path.basename(__file__) + ' shutdown-started')
  40. print(' ' + os.path.basename(__file__) + ' shutdown-status <MANDATORY TEXT MESSAGE>')
  41. print(' ' + os.path.basename(__file__) + ' shutdown-complete')
  42. print('')
  43.  
  44. # Define Action
  45.  
  46. if len(sys.argv) < 2:
  47. printUsage()
  48. sys.exit()
  49.  
  50. commandAction = sys.argv[1]
  51.  
  52. # Define Params
  53.  
  54. commandParam = ""
  55. if (commandAction == "deployment-init"):
  56. if len(sys.argv) < 8:
  57. printUsage()
  58. sys.exit()
  59. commandParam = " ".join(sys.argv[2:])
  60. if ("planned" in commandAction or "status" in commandAction):
  61. if len(sys.argv) < 3:
  62. printUsage()
  63. sys.exit()
  64. commandParam = " ".join(sys.argv[2:])
  65.  
  66. # Get Username
  67.  
  68. userName = ""
  69. if (commandAction != "deployment-init") or (commandAction != "health-check"):
  70. userName = raw_input("Please enter your DB email: ")
  71. if ("@db.com" not in userName):
  72. print('Username should be in format: firstname.lastname@db.com')
  73. sys.exit()
  74.  
  75. # Get Password
  76.  
  77. userPass = ""
  78. if (commandAction == "deployment-planned") :
  79. if os.isatty(0):
  80. userPass = getpass.getpass("Please enter your DB password: ")
  81. else:
  82. # for piping password
  83. userPass = raw_input()
  84. if (userPass == ""):
  85. print('Password should not be empty')
  86. sys.exit()
  87.  
  88. # Define global variables
  89.  
  90. changeLogMaxDays = 14
  91. summaryMaxLength = 50
  92. gerritUrl = "https://www.dbcde.com/gerrit"
  93. gerritProject = "dbtruss.git"
  94. jiraUrl = "https://dbatlas.db.com/jira01"
  95. base64string = base64.encodestring('%s:%s' % (userName, userPass)).replace('\n','')
  96. releaseVersionPrefix = "Gravity"
  97.  
  98. deploymentConfigPath = os.environ['SHARED_HOME_PATH'] + "/grv-email.conf"
  99. changeLogPath = os.environ['SHARED_HOME_PATH'] + "/grv-email-changelog.json"
  100.  
  101. environment = os.popen("grv-getprop environment.name.upper").read().strip()
  102. mailHost = os.popen("grv-getprop db.truss.statistics.mail.host").read().strip()
  103. mailFrom = os.popen("grv-getprop db.truss.alerts.mail.sender").read().strip()
  104. mailTo = os.popen("grv-getprop db.truss.notice.mail.recipient").read().strip()
  105. releaseVersionBuildNumber = os.environ['ENV_BUILD_NUMBER']
  106. releaseVersion = "NOT_SPECIFIED"
  107.  
  108.  
  109. ############# FUNCTIONS #############
  110.  
  111. def updateDynamicVariables(params):
  112. global mailHost
  113. global mailFrom
  114. global mailTo
  115. global releaseVersionBuildNumber
  116. global releaseVersion
  117. params = params.split()
  118. environment = params[0]
  119. mailHost = params[1]
  120. mailFrom = params[2]
  121. mailTo = params[3]
  122. releaseVersionBuildNumber = params[4]
  123. releaseVersion = params[5]
  124. print("Dynamic variables updated:")
  125. print(" environment = " + environment)
  126. print(" mailHost = " + mailHost)
  127. print(" mailFrom = " + mailFrom)
  128. print(" mailTo = " + mailTo)
  129. print(" releaseVersionBuildNumber = " + releaseVersionBuildNumber)
  130. print(" releaseVersion = " + releaseVersion)
  131.  
  132. def getReleaseVersionFullName():
  133. return releaseVersionPrefix + " " + releaseVersionBuildNumber
  134.  
  135. def splitListToChunks(list, chunkSize):
  136. n = max(1, chunkSize)
  137. return [list[i:i + chunkSize] for i in range(0, len(list), chunkSize)]
  138.  
  139.  
  140. def getMessageHeader(subject, content):
  141. return """From: <""" + mailFrom + """>
  142. To: <""" + mailTo + """>
  143. MIME-Version: 1.0
  144. Content-type: text/html
  145. Subject: """ + subject + """ [I]
  146.  
  147. Classification: <strong>For internal use only</strong><br/>
  148. <br/>
  149. <br/>
  150. Dear all,<br/>
  151. <br/>
  152. """ + content + """</br>
  153. <br/>
  154. Responsible engineer: <strong>""" + userName + """</strong></br>
  155. <br/>
  156. Kind Regards</br>
  157. </br></br>
  158. """
  159.  
  160.  
  161. def sendMessage(message):
  162.  
  163. try:
  164.  
  165. print("Sending mail...")
  166.  
  167. #print(message)
  168.  
  169. #file = open("grv-email.out.html", "w")
  170. #file.write(message.encode('cp1252'))
  171. #file.close()
  172.  
  173. smtpObj = smtplib.SMTP(mailHost)
  174. smtpObj.sendmail(mailFrom, mailTo, message.encode('utf-8'))
  175.  
  176. print("Mail sent successfully.")
  177.  
  178. except Exception:
  179. print("*** ERROR: unable to send email\n")
  180. raise
  181.  
  182.  
  183. def getDateTimeFromChangeLogItem_(item):
  184.  
  185. return datetime.datetime(int(item['date'][0:4]), int(item['date'][5:7]), int(item['date'][8:10]))
  186.  
  187.  
  188. def getDateTimeFromString(input): #input format: YYYY-MM-DD
  189.  
  190. return datetime.datetime(int(input[0:4]), int(input[5:7]), int(input[8:10]))
  191.  
  192.  
  193. def getChangeLogData():
  194.  
  195. print("Read change log file: " + changeLogPath)
  196.  
  197. result = []
  198.  
  199. try:
  200. with codecs.open(changeLogPath, 'rU', 'utf-8') as file:
  201. items = json.load(file)
  202.  
  203.  
  204. print("Skip changes older then " + str(changeLogMaxDays) + " days")
  205.  
  206. if len(items) > 0:
  207. latestDate = getDateTimeFromString(items[0]['date'][0:10])
  208. for item in items:
  209. itemDate = getDateTimeFromString(item['date'][0:10])
  210. if ((latestDate-itemDate).days <= changeLogMaxDays):
  211. item['date'] = itemDate.strftime("%a") + " " + item['date'][0:10]
  212. result.append(item)
  213.  
  214. except Exception:
  215. print("*** ERROR: Failed to read change log ***\n")
  216. raise
  217.  
  218. return result
  219.  
  220.  
  221. def getChangeLogJiraKeys(changeLogData):
  222.  
  223. print("Searching for change log jira keys...")
  224.  
  225. result = []
  226. temp = set()
  227.  
  228. for item in changeLogData:
  229. for key in re.findall("DBTRUSS-[0-9]+", item['message']):
  230. if key not in temp:
  231. result.append(key)
  232. temp.add(key)
  233.  
  234.  
  235. print("Found amount: " + str(len(result)))
  236. return result
  237.  
  238.  
  239. def getChangeLogHtmlTable(changeLogData):
  240.  
  241. print("Building change log html table...")
  242.  
  243. messageChangeLog = """
  244. <h3>Change Log</h3>
  245. <style>
  246. table{border-top:1px solid grey;border-left:1px solid grey;}
  247. th,td{border-right:1px solid grey;border-bottom:1px solid grey;white-space:nowrap;}
  248. </style>
  249. <table cellspacing="0" border="1" cellpadding="3">
  250. <tr><th>Date</th><th>Change Summary</th><th>Author</th><th>Commit</th></tr>
  251. """
  252.  
  253. for changeLogRow in changeLogData:
  254. messageChangeLog = messageChangeLog + "<tr>"
  255. messageChangeLog = messageChangeLog + "<td>" + changeLogRow['date'] + "</td>"
  256. messageChangeLog = messageChangeLog + "<td>" + changeLogRow['message'].splitlines()[0][:summaryMaxLength] + ("..." if len(changeLogRow['message'].splitlines()[0]) > summaryMaxLength else "") + "</td>"
  257. messageChangeLog = messageChangeLog + "<td>" + changeLogRow['committerName'] + "</td>"
  258. messageChangeLog = messageChangeLog + '<td><a href="' + gerritUrl + "/gitweb?p=" + gerritProject + ";a=commit;h=" + changeLogRow['id'] + '">' + changeLogRow['id'][0:7] + "</a></td>"
  259. messageChangeLog = messageChangeLog + "</tr>\n"
  260.  
  261. messageChangeLog = messageChangeLog + "</table>"
  262.  
  263. return messageChangeLog
  264.  
  265.  
  266. def getJiraIssues(jiraIssueKeys):
  267.  
  268. chunkSize = 1
  269.  
  270. print("Get jira tickets in chunks by " + str(chunkSize) + " items")
  271.  
  272. results = []
  273.  
  274. try:
  275.  
  276. for i, chunk in enumerate(splitListToChunks(jiraIssueKeys, chunkSize)):
  277.  
  278. paramsObject = {
  279. "jql" : "key in (" + ",".join(chunk) + ")",
  280. "startAt" : 0,
  281. "maxResults" : chunkSize,
  282. "fields" : "priority,issuetype,summary,status,assignee,reporter,created,updated,fixVersions"
  283. }
  284.  
  285. queryString = urllib.urlencode(paramsObject)
  286.  
  287. jiraIssueSearchRestUrl = jiraUrl + "/rest/api/2/search?" + queryString
  288.  
  289. request = urllib2.Request(jiraIssueSearchRestUrl)
  290. request.add_header("Authorization", "Basic %s" % base64string)
  291.  
  292. response = urllib2.urlopen(request)
  293.  
  294. results.extend(json.load(response)['issues'])
  295.  
  296. except urllib2.HTTPError, err:
  297. if err.code == 400:
  298. print("HTTPError 400 was skiped for jira " + ",".join(chunk))
  299. else:
  300. raise
  301. except Exception:
  302. print("*** ERROR: Could not get data from jira (maybe wrong username or password?) ***\n "+str(request))
  303. raise
  304.  
  305. try:
  306. print("Sort results by priority and key")
  307. results = sorted(results, key=lambda item: (item['fields']['priority']['id'], item['id']))
  308. except Exception:
  309. print("*** Exception while sorting ***")
  310. raise
  311.  
  312. return results
  313.  
  314.  
  315. def getJiraStatusesHtmlTable(jiraIssues):
  316.  
  317. print("Building jira statuses html table...")
  318.  
  319. result = """
  320. <h3>JIRA Statuses</h3>
  321. JIRA items referenced in change log summaries (see "Change Log" section).</br>
  322. Ordered by Priority, then by Key.</br></br>
  323. <style>
  324. table{border-top:1px solid grey;border-left:1px solid grey;}
  325. th,td{border-right:1px solid grey;border-bottom:1px solid grey;white-space:nowrap;}
  326. </style>
  327. <table cellspacing="0" border="1" cellpadding="3">
  328. <tr><th>Priority</th><th>Type</th><th>Key</th><th>Summary</th><th>Status</th><th>Fix Version(s)</th><th>Assignee</th><th>Reporter</th><th>Created</th><th>Updated</th></tr>
  329. """
  330.  
  331. for jiraIssue in jiraIssues:
  332.  
  333. jiraKey = jiraIssue['key']
  334. jiraFields = jiraIssue['fields']
  335.  
  336. priorityColor = {
  337. "Blocker" : "tomato",
  338. "Critical" : "tomato",
  339. "Major" : "tomato",
  340. "Medium" : "deepskyblue",
  341. "Minor" : "yellowgreen",
  342. "Trivial" : "yellowgreen"
  343. }.get(jiraFields['priority']['name'], "purple")
  344.  
  345. statusColor = {
  346. "Analysis & Design" : "gold",
  347. "In Development" : "gold",
  348. "In Progress" : "gold",
  349. "On Hold" : "gold",
  350. "Open" : "gold",
  351. "Ready for Development" : "gold",
  352. }.get(jiraFields['status']['name'], "yellowgreen")
  353.  
  354. result = result + "<tr>"
  355. result = result + "<td bgcolor=" + priorityColor + ">" + jiraFields['priority']['name'] + "</td>"
  356. result = result + "<td>" + jiraFields['issuetype']['name'] + "</td>"
  357. result = result + "<td>" + '<a href="' + jiraUrl + "/browse/" + jiraKey + '">' + jiraKey + '</a>' + "</td>"
  358. result = result + "<td>" + jiraFields['summary'][:summaryMaxLength] + ("..." if len(jiraFields['summary']) > summaryMaxLength else "") + "</td>"
  359. result = result + "<td bgcolor=" + statusColor + ">" + jiraFields['status']['name'] + "</td>"
  360. result = result + "<td>" + (jiraFields['fixVersions'][0]['name'] if len(jiraFields['fixVersions']) > 0 else "-") + "</td>"
  361. result = result + "<td>" + (jiraFields['assignee']['displayName'] if jiraFields['assignee'] else "Unassigned") + "</td>"
  362. result = result + "<td>" + (jiraFields['reporter']['displayName'] if jiraFields['reporter'] else "Unassigned") + "</td>"
  363. result = result + "<td>" + jiraFields['created'][:10] + "</td>"
  364. result = result + "<td>" + jiraFields['updated'][:10] + "</td>"
  365. result = result + "</tr>\n"
  366.  
  367. result = result + "</table>"
  368.  
  369. return result
  370.  
  371. #O.M
  372. def checkForReport(path):
  373.  
  374. if Path(path).is_file(): #verify the file exists
  375.  
  376. #verify the file was modified in last 30(delta) minutes
  377. delta = 30
  378. now = dt.datetime.now()
  379.  
  380. st = os.stat(path)
  381. mtime = dt.datetime.fromtimestamp(st.st_mtime)
  382.  
  383. return mtime > (now-dt.timedelta(minutes=delta)):
  384. else
  385. return False
  386.  
  387. def readFromReportFile(filePath):
  388.  
  389. if checkForReport(filePath):
  390. with open(filePath, 'r') as myfile:
  391. return myfile.read().replace('\n', '')
  392. else
  393. return ""
  394.  
  395. ############# MAIN FLOW #############
  396.  
  397. print('Action to perform: ' + commandAction + " " + commandParam)
  398.  
  399.  
  400. # Define action word
  401.  
  402. action = ""
  403. if "deployment" in commandAction:
  404. action = "deployment"
  405. elif "health" in commandAction:
  406. action = "healthcheck"
  407. elif "restart" in commandAction:
  408. action = "restart"
  409. elif "shutdown" in commandAction:
  410. action = "shutdown"
  411. else:
  412. action = "something"
  413.  
  414.  
  415. # Build Message
  416.  
  417. message = ""
  418.  
  419.  
  420. if commandAction == "deployment-init":
  421.  
  422. updateDynamicVariables(commandParam)
  423.  
  424. try:
  425. print("Storing configuration in: " + deploymentConfigPath)
  426. file = open(deploymentConfigPath, "w")
  427. file.write(commandParam)
  428. file.close()
  429. except Exception:
  430. print("*** ERROR: Failed to store configuration ***\n")
  431. raise
  432.  
  433. try:
  434. print("Download change log into: " + changeLogPath)
  435. nexusProxy=os.popen("grv-getprop nexus.proxy").read().strip()
  436. nexusCommand = 'wget ' + nexusProxy
  437. nexusCommandWithParams = nexusCommand + ' "' + releaseVersion+ '" -O ' + changeLogPath
  438. if releaseVersion.endswith('-SNAPSHOT'):
  439. releaseType = 'dbtruss-snapshots'
  440. else:
  441. releaseType = 'dbtruss-releases'
  442. changeLogUrl = 'https://nexus.dbcde.com/service/local/artifact/maven/content?r=' + releaseType + '&g=com.db.truss&a=dbtruss&v=' + releaseVersion+ '&c=changelog&e=json'
  443. nexusCommandWithParams = nexusCommand + ' "' + changeLogUrl+ '" -O ' + changeLogPath
  444. print('Download command: ' + nexusCommandWithParams)
  445. subprocess.call(['/bin/bash', '-i', '-c', nexusCommandWithParams])
  446. except Exception:
  447. print("*** ERROR: Failed to download and store change log ***\n")
  448. raise
  449.  
  450. sys.exit()
  451.  
  452. if ("deployment" in commandAction and commandAction != "deployment-init"):
  453.  
  454. configParams = ""
  455.  
  456. try:
  457. print("Reading configuration from: " + deploymentConfigPath)
  458. file = open(deploymentConfigPath, "r")
  459. configParams = file.readline()
  460. file.close()
  461. except Exception:
  462. print("*** ERROR: Failed to read configuration ***\n")
  463. raise
  464.  
  465. updateDynamicVariables(configParams)
  466.  
  467.  
  468. if "planned" in commandAction:
  469.  
  470. params = commandParam.split()
  471. timeParam = params[0]
  472.  
  473. subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " planned"
  474.  
  475. body = subject + " <strong>in " + timeParam + " minutes </strong>"
  476. if (len(params) > 1):
  477. messageParam = " ".join(params[1:])
  478. body += "<br/><br/> Additional info: <strong>" + messageParam + "</strong>"
  479.  
  480. message = getMessageHeader(subject, body)
  481.  
  482. changeLogHtmlTable = ""
  483. changeLogJiraKeys = []
  484. jiraStatusesHtmlTable = ""
  485.  
  486. if (commandAction == "deployment-planned" or commandAction == "deployment-planned-nojira"):
  487.  
  488. changeLogData = getChangeLogData()
  489.  
  490. changeLogHtmlTable = "Change log is unavailable."
  491. if len(changeLogData) > 0:
  492. changeLogHtmlTable = getChangeLogHtmlTable(changeLogData)
  493. changeLogJiraKeys = getChangeLogJiraKeys(changeLogData)
  494.  
  495. if commandAction == "deployment-planned":
  496.  
  497. if len(changeLogJiraKeys) > 0:
  498. jiraIssues = getJiraIssues(changeLogJiraKeys)
  499. jiraStatusesHtmlTable = "JIRA statuses could not be retrieved."
  500. if len(jiraIssues) > 0:
  501. jiraStatusesHtmlTable = getJiraStatusesHtmlTable(jiraIssues)
  502.  
  503. message = message + jiraStatusesHtmlTable
  504. message = message + changeLogHtmlTable
  505.  
  506.  
  507. elif "started" in commandAction:
  508.  
  509. subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " started"
  510. message = getMessageHeader(subject, subject)
  511.  
  512. elif "status" in commandAction:
  513.  
  514. subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " status"
  515. message = getMessageHeader(subject, subject + ": <strong>" + commandParam + "</strong>")
  516.  
  517. elif "complete" in commandAction:
  518.  
  519. subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " complete"
  520. message = getMessageHeader(subject, subject)
  521.  
  522. elif action=="healthcheck":
  523.  
  524. envName = 'prod'
  525. reportPath = './a/reports/' + envName + '.html'
  526. content = readFromReportFile(reportPath)
  527.  
  528. if len(content) > 0
  529. subject = getReleaseVersionFullName() + " (" + environment + ") " + " PROD Health Check complete"
  530. message = getMessageHeader(subject, content)
  531. else
  532. print("*** WARNING: Report for healthcheck is not generated ***\n")
  533. sys.exit()
  534. else:
  535.  
  536. printUsage()
  537. sys.exit()
  538.  
  539. # Send Message
  540.  
  541. sendMessage(message)
Add Comment
Please, Sign In to add comment