Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- import sys
- import os
- import getpass
- import base64
- import json
- import codecs
- import urllib
- import urllib2
- import smtplib
- import datetime
- import re
- import subprocess
- ############# INPUT AND DEFAULT VARIABLES #############
- def printUsage():
- print('')
- print('Usage:')
- print(' ' + os.path.basename(__file__) + ' deployment-init <ENVIRONMENT> <SMTP_HOST> <SMTP_FROM> <SMTP_TO> <BUILD_NUMBER> <PROJECT_VERSION>')
- print(' ' + os.path.basename(__file__) + ' deployment-planned <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' deployment-planned-nologs <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' deployment-planned-nojira <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' deployment-started')
- print(' ' + os.path.basename(__file__) + ' deployment-status <MANDATORY TEXT MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' deployment-complete')
- print('')
- print(' ' + os.path.basename(__file__) + ' health-check')
- print('')
- print(' ' + os.path.basename(__file__) + ' restart-planned <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' restart-started')
- print(' ' + os.path.basename(__file__) + ' restart-status <MANDATORY TEXT MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' restart-complete')
- print('')
- print(' ' + os.path.basename(__file__) + ' shutdown-planned <MANDATORY_MINUTES_AMOUNT> <NOT_MANDATORY_TEXT_MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' shutdown-started')
- print(' ' + os.path.basename(__file__) + ' shutdown-status <MANDATORY TEXT MESSAGE>')
- print(' ' + os.path.basename(__file__) + ' shutdown-complete')
- print('')
- # Define Action
- if len(sys.argv) < 2:
- printUsage()
- sys.exit()
- commandAction = sys.argv[1]
- # Define Params
- commandParam = ""
- if (commandAction == "deployment-init"):
- if len(sys.argv) < 8:
- printUsage()
- sys.exit()
- commandParam = " ".join(sys.argv[2:])
- if ("planned" in commandAction or "status" in commandAction):
- if len(sys.argv) < 3:
- printUsage()
- sys.exit()
- commandParam = " ".join(sys.argv[2:])
- # Get Username
- userName = ""
- if (commandAction != "deployment-init") or (commandAction != "health-check"):
- userName = raw_input("Please enter your DB email: ")
- if ("@db.com" not in userName):
- print('Username should be in format: firstname.lastname@db.com')
- sys.exit()
- # Get Password
- userPass = ""
- if (commandAction == "deployment-planned") :
- if os.isatty(0):
- userPass = getpass.getpass("Please enter your DB password: ")
- else:
- # for piping password
- userPass = raw_input()
- if (userPass == ""):
- print('Password should not be empty')
- sys.exit()
- # Define global variables
- changeLogMaxDays = 14
- summaryMaxLength = 50
- gerritUrl = "https://www.dbcde.com/gerrit"
- gerritProject = "dbtruss.git"
- jiraUrl = "https://dbatlas.db.com/jira01"
- base64string = base64.encodestring('%s:%s' % (userName, userPass)).replace('\n','')
- releaseVersionPrefix = "Gravity"
- deploymentConfigPath = os.environ['SHARED_HOME_PATH'] + "/grv-email.conf"
- changeLogPath = os.environ['SHARED_HOME_PATH'] + "/grv-email-changelog.json"
- environment = os.popen("grv-getprop environment.name.upper").read().strip()
- mailHost = os.popen("grv-getprop db.truss.statistics.mail.host").read().strip()
- mailFrom = os.popen("grv-getprop db.truss.alerts.mail.sender").read().strip()
- mailTo = os.popen("grv-getprop db.truss.notice.mail.recipient").read().strip()
- releaseVersionBuildNumber = os.environ['ENV_BUILD_NUMBER']
- releaseVersion = "NOT_SPECIFIED"
- ############# FUNCTIONS #############
- def updateDynamicVariables(params):
- global mailHost
- global mailFrom
- global mailTo
- global releaseVersionBuildNumber
- global releaseVersion
- params = params.split()
- environment = params[0]
- mailHost = params[1]
- mailFrom = params[2]
- mailTo = params[3]
- releaseVersionBuildNumber = params[4]
- releaseVersion = params[5]
- print("Dynamic variables updated:")
- print(" environment = " + environment)
- print(" mailHost = " + mailHost)
- print(" mailFrom = " + mailFrom)
- print(" mailTo = " + mailTo)
- print(" releaseVersionBuildNumber = " + releaseVersionBuildNumber)
- print(" releaseVersion = " + releaseVersion)
- def getReleaseVersionFullName():
- return releaseVersionPrefix + " " + releaseVersionBuildNumber
- def splitListToChunks(list, chunkSize):
- n = max(1, chunkSize)
- return [list[i:i + chunkSize] for i in range(0, len(list), chunkSize)]
- def getMessageHeader(subject, content):
- return """From: <""" + mailFrom + """>
- To: <""" + mailTo + """>
- MIME-Version: 1.0
- Content-type: text/html
- Subject: """ + subject + """ [I]
- Classification: <strong>For internal use only</strong><br/>
- <br/>
- <br/>
- Dear all,<br/>
- <br/>
- """ + content + """</br>
- <br/>
- Responsible engineer: <strong>""" + userName + """</strong></br>
- <br/>
- Kind Regards</br>
- </br></br>
- """
- def sendMessage(message):
- try:
- print("Sending mail...")
- #print(message)
- #file = open("grv-email.out.html", "w")
- #file.write(message.encode('cp1252'))
- #file.close()
- smtpObj = smtplib.SMTP(mailHost)
- smtpObj.sendmail(mailFrom, mailTo, message.encode('utf-8'))
- print("Mail sent successfully.")
- except Exception:
- print("*** ERROR: unable to send email\n")
- raise
- def getDateTimeFromChangeLogItem_(item):
- return datetime.datetime(int(item['date'][0:4]), int(item['date'][5:7]), int(item['date'][8:10]))
- def getDateTimeFromString(input): #input format: YYYY-MM-DD
- return datetime.datetime(int(input[0:4]), int(input[5:7]), int(input[8:10]))
- def getChangeLogData():
- print("Read change log file: " + changeLogPath)
- result = []
- try:
- with codecs.open(changeLogPath, 'rU', 'utf-8') as file:
- items = json.load(file)
- print("Skip changes older then " + str(changeLogMaxDays) + " days")
- if len(items) > 0:
- latestDate = getDateTimeFromString(items[0]['date'][0:10])
- for item in items:
- itemDate = getDateTimeFromString(item['date'][0:10])
- if ((latestDate-itemDate).days <= changeLogMaxDays):
- item['date'] = itemDate.strftime("%a") + " " + item['date'][0:10]
- result.append(item)
- except Exception:
- print("*** ERROR: Failed to read change log ***\n")
- raise
- return result
- def getChangeLogJiraKeys(changeLogData):
- print("Searching for change log jira keys...")
- result = []
- temp = set()
- for item in changeLogData:
- for key in re.findall("DBTRUSS-[0-9]+", item['message']):
- if key not in temp:
- result.append(key)
- temp.add(key)
- print("Found amount: " + str(len(result)))
- return result
- def getChangeLogHtmlTable(changeLogData):
- print("Building change log html table...")
- messageChangeLog = """
- <h3>Change Log</h3>
- <style>
- table{border-top:1px solid grey;border-left:1px solid grey;}
- th,td{border-right:1px solid grey;border-bottom:1px solid grey;white-space:nowrap;}
- </style>
- <table cellspacing="0" border="1" cellpadding="3">
- <tr><th>Date</th><th>Change Summary</th><th>Author</th><th>Commit</th></tr>
- """
- for changeLogRow in changeLogData:
- messageChangeLog = messageChangeLog + "<tr>"
- messageChangeLog = messageChangeLog + "<td>" + changeLogRow['date'] + "</td>"
- messageChangeLog = messageChangeLog + "<td>" + changeLogRow['message'].splitlines()[0][:summaryMaxLength] + ("..." if len(changeLogRow['message'].splitlines()[0]) > summaryMaxLength else "") + "</td>"
- messageChangeLog = messageChangeLog + "<td>" + changeLogRow['committerName'] + "</td>"
- messageChangeLog = messageChangeLog + '<td><a href="' + gerritUrl + "/gitweb?p=" + gerritProject + ";a=commit;h=" + changeLogRow['id'] + '">' + changeLogRow['id'][0:7] + "</a></td>"
- messageChangeLog = messageChangeLog + "</tr>\n"
- messageChangeLog = messageChangeLog + "</table>"
- return messageChangeLog
- def getJiraIssues(jiraIssueKeys):
- chunkSize = 1
- print("Get jira tickets in chunks by " + str(chunkSize) + " items")
- results = []
- try:
- for i, chunk in enumerate(splitListToChunks(jiraIssueKeys, chunkSize)):
- paramsObject = {
- "jql" : "key in (" + ",".join(chunk) + ")",
- "startAt" : 0,
- "maxResults" : chunkSize,
- "fields" : "priority,issuetype,summary,status,assignee,reporter,created,updated,fixVersions"
- }
- queryString = urllib.urlencode(paramsObject)
- jiraIssueSearchRestUrl = jiraUrl + "/rest/api/2/search?" + queryString
- request = urllib2.Request(jiraIssueSearchRestUrl)
- request.add_header("Authorization", "Basic %s" % base64string)
- response = urllib2.urlopen(request)
- results.extend(json.load(response)['issues'])
- except urllib2.HTTPError, err:
- if err.code == 400:
- print("HTTPError 400 was skiped for jira " + ",".join(chunk))
- else:
- raise
- except Exception:
- print("*** ERROR: Could not get data from jira (maybe wrong username or password?) ***\n "+str(request))
- raise
- try:
- print("Sort results by priority and key")
- results = sorted(results, key=lambda item: (item['fields']['priority']['id'], item['id']))
- except Exception:
- print("*** Exception while sorting ***")
- raise
- return results
- def getJiraStatusesHtmlTable(jiraIssues):
- print("Building jira statuses html table...")
- result = """
- <h3>JIRA Statuses</h3>
- JIRA items referenced in change log summaries (see "Change Log" section).</br>
- Ordered by Priority, then by Key.</br></br>
- <style>
- table{border-top:1px solid grey;border-left:1px solid grey;}
- th,td{border-right:1px solid grey;border-bottom:1px solid grey;white-space:nowrap;}
- </style>
- <table cellspacing="0" border="1" cellpadding="3">
- <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>
- """
- for jiraIssue in jiraIssues:
- jiraKey = jiraIssue['key']
- jiraFields = jiraIssue['fields']
- priorityColor = {
- "Blocker" : "tomato",
- "Critical" : "tomato",
- "Major" : "tomato",
- "Medium" : "deepskyblue",
- "Minor" : "yellowgreen",
- "Trivial" : "yellowgreen"
- }.get(jiraFields['priority']['name'], "purple")
- statusColor = {
- "Analysis & Design" : "gold",
- "In Development" : "gold",
- "In Progress" : "gold",
- "On Hold" : "gold",
- "Open" : "gold",
- "Ready for Development" : "gold",
- }.get(jiraFields['status']['name'], "yellowgreen")
- result = result + "<tr>"
- result = result + "<td bgcolor=" + priorityColor + ">" + jiraFields['priority']['name'] + "</td>"
- result = result + "<td>" + jiraFields['issuetype']['name'] + "</td>"
- result = result + "<td>" + '<a href="' + jiraUrl + "/browse/" + jiraKey + '">' + jiraKey + '</a>' + "</td>"
- result = result + "<td>" + jiraFields['summary'][:summaryMaxLength] + ("..." if len(jiraFields['summary']) > summaryMaxLength else "") + "</td>"
- result = result + "<td bgcolor=" + statusColor + ">" + jiraFields['status']['name'] + "</td>"
- result = result + "<td>" + (jiraFields['fixVersions'][0]['name'] if len(jiraFields['fixVersions']) > 0 else "-") + "</td>"
- result = result + "<td>" + (jiraFields['assignee']['displayName'] if jiraFields['assignee'] else "Unassigned") + "</td>"
- result = result + "<td>" + (jiraFields['reporter']['displayName'] if jiraFields['reporter'] else "Unassigned") + "</td>"
- result = result + "<td>" + jiraFields['created'][:10] + "</td>"
- result = result + "<td>" + jiraFields['updated'][:10] + "</td>"
- result = result + "</tr>\n"
- result = result + "</table>"
- return result
- #O.M
- def checkForReport(path):
- if Path(path).is_file(): #verify the file exists
- #verify the file was modified in last 30(delta) minutes
- delta = 30
- now = dt.datetime.now()
- st = os.stat(path)
- mtime = dt.datetime.fromtimestamp(st.st_mtime)
- return mtime > (now-dt.timedelta(minutes=delta)):
- else
- return False
- def readFromReportFile(filePath):
- if checkForReport(filePath):
- with open(filePath, 'r') as myfile:
- return myfile.read().replace('\n', '')
- else
- return ""
- ############# MAIN FLOW #############
- print('Action to perform: ' + commandAction + " " + commandParam)
- # Define action word
- action = ""
- if "deployment" in commandAction:
- action = "deployment"
- elif "health" in commandAction:
- action = "healthcheck"
- elif "restart" in commandAction:
- action = "restart"
- elif "shutdown" in commandAction:
- action = "shutdown"
- else:
- action = "something"
- # Build Message
- message = ""
- if commandAction == "deployment-init":
- updateDynamicVariables(commandParam)
- try:
- print("Storing configuration in: " + deploymentConfigPath)
- file = open(deploymentConfigPath, "w")
- file.write(commandParam)
- file.close()
- except Exception:
- print("*** ERROR: Failed to store configuration ***\n")
- raise
- try:
- print("Download change log into: " + changeLogPath)
- nexusProxy=os.popen("grv-getprop nexus.proxy").read().strip()
- nexusCommand = 'wget ' + nexusProxy
- nexusCommandWithParams = nexusCommand + ' "' + releaseVersion+ '" -O ' + changeLogPath
- if releaseVersion.endswith('-SNAPSHOT'):
- releaseType = 'dbtruss-snapshots'
- else:
- releaseType = 'dbtruss-releases'
- changeLogUrl = 'https://nexus.dbcde.com/service/local/artifact/maven/content?r=' + releaseType + '&g=com.db.truss&a=dbtruss&v=' + releaseVersion+ '&c=changelog&e=json'
- nexusCommandWithParams = nexusCommand + ' "' + changeLogUrl+ '" -O ' + changeLogPath
- print('Download command: ' + nexusCommandWithParams)
- subprocess.call(['/bin/bash', '-i', '-c', nexusCommandWithParams])
- except Exception:
- print("*** ERROR: Failed to download and store change log ***\n")
- raise
- sys.exit()
- if ("deployment" in commandAction and commandAction != "deployment-init"):
- configParams = ""
- try:
- print("Reading configuration from: " + deploymentConfigPath)
- file = open(deploymentConfigPath, "r")
- configParams = file.readline()
- file.close()
- except Exception:
- print("*** ERROR: Failed to read configuration ***\n")
- raise
- updateDynamicVariables(configParams)
- if "planned" in commandAction:
- params = commandParam.split()
- timeParam = params[0]
- subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " planned"
- body = subject + " <strong>in " + timeParam + " minutes </strong>"
- if (len(params) > 1):
- messageParam = " ".join(params[1:])
- body += "<br/><br/> Additional info: <strong>" + messageParam + "</strong>"
- message = getMessageHeader(subject, body)
- changeLogHtmlTable = ""
- changeLogJiraKeys = []
- jiraStatusesHtmlTable = ""
- if (commandAction == "deployment-planned" or commandAction == "deployment-planned-nojira"):
- changeLogData = getChangeLogData()
- changeLogHtmlTable = "Change log is unavailable."
- if len(changeLogData) > 0:
- changeLogHtmlTable = getChangeLogHtmlTable(changeLogData)
- changeLogJiraKeys = getChangeLogJiraKeys(changeLogData)
- if commandAction == "deployment-planned":
- if len(changeLogJiraKeys) > 0:
- jiraIssues = getJiraIssues(changeLogJiraKeys)
- jiraStatusesHtmlTable = "JIRA statuses could not be retrieved."
- if len(jiraIssues) > 0:
- jiraStatusesHtmlTable = getJiraStatusesHtmlTable(jiraIssues)
- message = message + jiraStatusesHtmlTable
- message = message + changeLogHtmlTable
- elif "started" in commandAction:
- subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " started"
- message = getMessageHeader(subject, subject)
- elif "status" in commandAction:
- subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " status"
- message = getMessageHeader(subject, subject + ": <strong>" + commandParam + "</strong>")
- elif "complete" in commandAction:
- subject = getReleaseVersionFullName() + " (" + environment + ") " + action + " complete"
- message = getMessageHeader(subject, subject)
- elif action=="healthcheck":
- envName = 'prod'
- reportPath = './a/reports/' + envName + '.html'
- content = readFromReportFile(reportPath)
- if len(content) > 0
- subject = getReleaseVersionFullName() + " (" + environment + ") " + " PROD Health Check complete"
- message = getMessageHeader(subject, content)
- else
- print("*** WARNING: Report for healthcheck is not generated ***\n")
- sys.exit()
- else:
- printUsage()
- sys.exit()
- # Send Message
- sendMessage(message)
Add Comment
Please, Sign In to add comment