| Index: gpu/tools/check_gpu_bots.py
|
| diff --git a/gpu/tools/check_gpu_bots.py b/gpu/tools/check_gpu_bots.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..fb221284f82563b57227ebc8286f8a8d7c2113e3
|
| --- /dev/null
|
| +++ b/gpu/tools/check_gpu_bots.py
|
| @@ -0,0 +1,650 @@
|
| +#!/usr/bin/env python
|
| +
|
| +# Copyright 2014 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +import argparse
|
| +import datetime
|
| +import getpass
|
| +import json
|
| +import os
|
| +import smtplib
|
| +import sys
|
| +import time
|
| +import urllib
|
| +import urllib2
|
| +
|
| +class Emailer:
|
| + DEFAULT_EMAIL_PASSWORD_FILE = '.email_password'
|
| + GMAIL_SMTP_SERVER = 'smtp.gmail.com:587'
|
| + SUBJECT = 'Chrome GPU Bots Notification'
|
| +
|
| + def __init__(self, email_from, email_to, email_password_file):
|
| + self.email_from = email_from
|
| + self.email_to = email_to
|
| + self.email_password = Emailer._getEmailPassword(email_password_file)
|
| +
|
| + @staticmethod
|
| + def format_email_body(time_str, offline_str, failed_str, noteworthy_str):
|
| + return '%s%s%s%s' % (time_str, offline_str, failed_str, noteworthy_str)
|
| +
|
| + def send_email(self, body):
|
| + message = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s' % (self.email_from,
|
| + ','.join(self.email_to), Emailer.SUBJECT, body)
|
| +
|
| + try:
|
| + server = smtplib.SMTP(Emailer.GMAIL_SMTP_SERVER)
|
| + server.starttls()
|
| + server.login(self.email_from, self.email_password)
|
| + server.sendmail(self.email_from, self.email_to, message)
|
| + server.quit()
|
| + except Exception as e:
|
| + print 'Error sending email: %s' % str(e)
|
| +
|
| + def testEmailLogin(self):
|
| + server = smtplib.SMTP(Emailer.GMAIL_SMTP_SERVER)
|
| + server.starttls()
|
| + server.login(self.email_from, self.email_password)
|
| + server.quit()
|
| +
|
| + @staticmethod
|
| + def _getEmailPassword(email_password_file):
|
| + password = ''
|
| +
|
| + password_file = (email_password_file if email_password_file is not None
|
| + else Emailer.DEFAULT_EMAIL_PASSWORD_FILE)
|
| +
|
| + if os.path.isfile(password_file):
|
| + with open(password_file, 'r') as f:
|
| + password = f.read().strip()
|
| + else:
|
| + password = getpass.getpass(
|
| + 'Please enter email password for source email account: ')
|
| +
|
| + return password
|
| +
|
| +class GpuBot:
|
| + def __init__(self, waterfall_name, bot_name, bot_data):
|
| + self.waterfall_name = waterfall_name
|
| + self.bot_name = bot_name
|
| + self.bot_data = bot_data
|
| + self._end_time = None
|
| + self._hours_since_last_run = None
|
| + self.failure_string = None
|
| + self.bot_url = None
|
| + self.build_url = None
|
| +
|
| + def getEndTime(self):
|
| + return self._end_time
|
| +
|
| + def setEndTime(self, end_time):
|
| + self._end_time = end_time
|
| + self._hours_since_last_run = \
|
| + roughTimeDiffInHours(end_time, time.localtime())
|
| +
|
| + def getHoursSinceLastRun(self):
|
| + return self._hours_since_last_run
|
| +
|
| + def toDict(self):
|
| + dict = {'waterfall_name': self.waterfall_name, 'bot_name': self.bot_name}
|
| +
|
| + if self._end_time is not None:
|
| + dict['end_time'] = serialTime(self._end_time)
|
| + dict['hours_since_last_run'] = self._hours_since_last_run
|
| +
|
| + if self.failure_string is not None:
|
| + dict['failure_string'] = self.failure_string
|
| +
|
| + if self.bot_url is not None:
|
| + dict['bot_url'] = self.bot_url
|
| +
|
| + if self.build_url is not None:
|
| + dict['build_url'] = self.build_url
|
| +
|
| + return dict
|
| +
|
| + @staticmethod
|
| + def fromDict(dict):
|
| + gpu_bot = GpuBot(dict['waterfall_name'], dict['bot_name'], None)
|
| +
|
| + if 'end_time' in dict:
|
| + gpu_bot._end_time = unserializeTime(dict['end_time'])
|
| +
|
| + if 'hours_since_last_run' in dict:
|
| + self._hours_since_last_run = dict['hours_since_last_run']
|
| +
|
| + if 'failure_string' in dict:
|
| + self.failure_string = dict['failure_string']
|
| +
|
| + if 'bot_url' in dict:
|
| + self.bot_url = dict['bot_url']
|
| +
|
| + if 'build_url' in dict:
|
| + self.build_url = dict['build_url']
|
| +
|
| + return gpu_bot
|
| +
|
| +def errorNoMostRecentBuild(waterfall_name, bot_name):
|
| + print 'No most recent build available: %s::%s' % (waterfall_name, bot_name)
|
| +
|
| +class Waterfall:
|
| + BASE_URL = 'http://build.chromium.org/p/'
|
| + BASE_BUILD_URL = BASE_URL + '%s/builders/%s'
|
| + SPECIFIC_BUILD_URL = BASE_URL + '%s/builders/%s/builds/%s'
|
| + BASE_JSON_BUILDERS_URL = BASE_URL + '%s/json/builders'
|
| + BASE_JSON_BUILDS_URL = BASE_URL + '%s/json/builders/%s/builds'
|
| + REGULAR_WATERFALLS = ['chromium.gpu',
|
| + 'tryserver.chromium.gpu',
|
| + 'chromium.gpu.fyi']
|
| + WEBKIT_GPU_BOTS = ['GPU Win Builder',
|
| + 'GPU Win Builder (dbg)',
|
| + 'GPU Win7 (NVIDIA)',
|
| + 'GPU Win7 (dbg) (NVIDIA)',
|
| + 'GPU Mac Builder',
|
| + 'GPU Mac Builder (dbg)',
|
| + 'GPU Mac10.7',
|
| + 'GPU Mac10.7 (dbg)',
|
| + 'GPU Linux Builder',
|
| + 'GPU Linux Builder (dbg)',
|
| + 'GPU Linux (NVIDIA)',
|
| + 'GPU Linux (dbg) (NVIDIA)']
|
| + FILTERED_WATERFALLS = [('chromium.webkit', WEBKIT_GPU_BOTS)]
|
| +
|
| + @staticmethod
|
| + def getJsonFromUrl(url):
|
| + conn = urllib2.urlopen(url)
|
| + result = conn.read()
|
| + conn.close()
|
| + return json.loads(result)
|
| +
|
| + @staticmethod
|
| + def getBuildersJsonForWaterfall(waterfall):
|
| + querystring = '?filter'
|
| + return (Waterfall.getJsonFromUrl((Waterfall.BASE_JSON_BUILDERS_URL + '%s')
|
| + % (waterfall, querystring)))
|
| +
|
| + @staticmethod
|
| + def getLastNBuildsForBuilder(n, waterfall, builder):
|
| + if n <= 0:
|
| + return {}
|
| +
|
| + querystring = '?'
|
| +
|
| + for i in range(n):
|
| + querystring += 'select=-%d&' % (i + 1)
|
| +
|
| + querystring += 'filter'
|
| +
|
| + return Waterfall.getJsonFromUrl((Waterfall.BASE_JSON_BUILDS_URL + '%s') %
|
| + (waterfall, urllib.quote(builder), querystring))
|
| +
|
| + @staticmethod
|
| + def getFilteredBuildersJsonForWaterfall(waterfall, filter):
|
| + querystring = '?'
|
| +
|
| + for bot_name in filter:
|
| + querystring += 'select=%s&' % urllib.quote(bot_name)
|
| +
|
| + querystring += 'filter'
|
| +
|
| + return Waterfall.getJsonFromUrl((Waterfall.BASE_JSON_BUILDERS_URL + '%s')
|
| + % (waterfall, querystring))
|
| +
|
| + @staticmethod
|
| + def getAllGpuBots():
|
| + allbots = {k: Waterfall.getBuildersJsonForWaterfall(k)
|
| + for k in Waterfall.REGULAR_WATERFALLS}
|
| +
|
| + filteredbots = {k[0]:
|
| + Waterfall.getFilteredBuildersJsonForWaterfall(k[0], k[1])
|
| + for k in Waterfall.FILTERED_WATERFALLS}
|
| +
|
| + allbots.update(filteredbots)
|
| +
|
| + return allbots
|
| +
|
| + @staticmethod
|
| + def getOfflineBots(bots):
|
| + offline_bots = []
|
| +
|
| + for waterfall_name in bots:
|
| + waterfall = bots[waterfall_name]
|
| +
|
| + for bot_name in waterfall:
|
| + bot = waterfall[bot_name]
|
| +
|
| + if bot['state'] != 'offline':
|
| + continue
|
| +
|
| + gpu_bot = GpuBot(waterfall_name, bot_name, bot)
|
| + gpu_bot.bot_url = Waterfall.BASE_BUILD_URL % (waterfall_name,
|
| + urllib.quote(bot_name))
|
| +
|
| + most_recent_build = Waterfall.getMostRecentlyCompletedBuildForBot(
|
| + gpu_bot)
|
| +
|
| + if (most_recent_build and 'times' in most_recent_build and
|
| + most_recent_build['times']):
|
| + gpu_bot.setEndTime(time.localtime(most_recent_build['times'][1]))
|
| + else:
|
| + errorNoMostRecentBuild(waterfall_name, bot_name)
|
| +
|
| + offline_bots.append(gpu_bot)
|
| +
|
| + return offline_bots
|
| +
|
| + @staticmethod
|
| + def getMostRecentlyCompletedBuildForBot(bot):
|
| + if bot.bot_data is not None and 'most_recent_build' in bot.bot_data:
|
| + return bot.bot_data['most_recent_build']
|
| +
|
| + # Unfortunately, the JSON API doesn't provide a "most recent completed
|
| + # build" call. We just have to get some number of the most recent (including
|
| + # current, in-progress builds) and give up if that's not enough.
|
| + NUM_BUILDS = 10
|
| + builds = Waterfall.getLastNBuildsForBuilder(NUM_BUILDS, bot.waterfall_name,
|
| + bot.bot_name)
|
| +
|
| + for i in range(NUM_BUILDS):
|
| + current_build_name = '-%d' % (i + 1)
|
| + current_build = builds[current_build_name]
|
| +
|
| + if 'results' in current_build and current_build['results'] is not None:
|
| + if bot.bot_data is not None:
|
| + bot.bot_data['most_recent_build'] = current_build
|
| +
|
| + return current_build
|
| +
|
| + return None
|
| +
|
| + @staticmethod
|
| + def getFailedBots(bots):
|
| + failed_bots = []
|
| +
|
| + for waterfall_name in bots:
|
| + waterfall = bots[waterfall_name]
|
| +
|
| + for bot_name in waterfall:
|
| + bot = waterfall[bot_name]
|
| + gpu_bot = GpuBot(waterfall_name, bot_name, bot)
|
| + gpu_bot.bot_url = Waterfall.BASE_BUILD_URL % (waterfall_name,
|
| + urllib.quote(bot_name))
|
| +
|
| + most_recent_build = Waterfall.getMostRecentlyCompletedBuildForBot(
|
| + gpu_bot)
|
| +
|
| + if (most_recent_build and 'text' in most_recent_build and
|
| + 'failed' in most_recent_build['text']):
|
| + gpu_bot.failure_string = ' '.join(most_recent_build['text'])
|
| + gpu_bot.build_url = Waterfall.SPECIFIC_BUILD_URL % (waterfall_name,
|
| + urllib.quote(bot_name), most_recent_build['number'])
|
| + failed_bots.append(gpu_bot)
|
| + elif not most_recent_build:
|
| + errorNoMostRecentBuild(waterfall_name, bot_name)
|
| +
|
| + return failed_bots
|
| +
|
| +def formatTime(t):
|
| + return time.strftime("%a, %d %b %Y %H:%M:%S", t)
|
| +
|
| +def roughTimeDiffInHours(t1, t2):
|
| + datetimes = []
|
| +
|
| + for t in [t1, t2]:
|
| + datetimes.append(datetime.datetime(t.tm_year, t.tm_mon, t.tm_mday,
|
| + t.tm_hour, t.tm_min, t.tm_sec))
|
| +
|
| + datetime_diff = datetimes[0] - datetimes[1]
|
| +
|
| + hours = float(datetime_diff.total_seconds()) / 3600.0
|
| +
|
| + return abs(hours)
|
| +
|
| +def getBotStr(bot):
|
| + s = ' %s::%s\n' % (bot.waterfall_name, bot.bot_name)
|
| +
|
| + if bot.failure_string is not None:
|
| + s += ' failure: %s\n' % bot.failure_string
|
| +
|
| + if bot.getEndTime() is not None:
|
| + s += (' last build end time: %s (roughly %f hours ago)\n' %
|
| + (formatTime(bot.getEndTime()), bot.getHoursSinceLastRun()))
|
| +
|
| + if bot.bot_url is not None:
|
| + s += ' bot url: %s\n' % bot.bot_url
|
| +
|
| + if bot.build_url is not None:
|
| + s += ' build url: %s\n' % bot.build_url
|
| +
|
| + s += '\n'
|
| + return s
|
| +
|
| +def getBotsStr(bots):
|
| + s = ''
|
| +
|
| + for bot in bots:
|
| + s += getBotStr(bot)
|
| +
|
| + s += '\n'
|
| + return s
|
| +
|
| +def getOfflineBotsStr(offline_bots):
|
| + return 'Offline bots:\n%s' % getBotsStr(offline_bots)
|
| +
|
| +def getFailedBotsStr(failed_bots):
|
| + return 'Failed bots:\n%s' % getBotsStr(failed_bots)
|
| +
|
| +def getBotDicts(bots):
|
| + dicts = []
|
| +
|
| + for bot in bots:
|
| + dicts.append(bot.toDict())
|
| +
|
| + return dicts
|
| +
|
| +def unserializeTime(t):
|
| + return time.struct_time((t['year'], t['mon'], t['day'], t['hour'], t['min'],
|
| + t['sec'], 0, 0, 0))
|
| +
|
| +def serialTime(t):
|
| + return {'year': t.tm_year, 'mon': t.tm_mon, 'day': t.tm_mday,
|
| + 'hour': t.tm_hour, 'min': t.tm_min, 'sec': t.tm_sec}
|
| +
|
| +def getSummary(offline_bots, failed_bots):
|
| + offline_bot_dict = getBotDicts(offline_bots)
|
| + failed_bot_dict = getBotDicts(failed_bots)
|
| + return {'offline': offline_bot_dict, 'failed': failed_bot_dict}
|
| +
|
| +def findBot(name, lst):
|
| + for bot in lst:
|
| + if bot.bot_name == name:
|
| + return bot
|
| +
|
| + return None
|
| +
|
| +def getNoteworthyEvents(offline_bots, failed_bots, previous_results):
|
| + CRITICAL_NUM_HOURS = 1.0
|
| +
|
| + previous_offline = (previous_results['offline'] if 'offline'
|
| + in previous_results else [])
|
| +
|
| + previous_failures = (previous_results['failed'] if 'failed'
|
| + in previous_results else [])
|
| +
|
| + noteworthy_offline = []
|
| + for bot in offline_bots:
|
| + if bot.getHoursSinceLastRun() >= CRITICAL_NUM_HOURS:
|
| + previous_bot = findBot(bot.bot_name, previous_offline)
|
| +
|
| + if (previous_bot is None or
|
| + previous_bot.getHoursSinceLastRun() < CRITICAL_NUM_HOURS):
|
| + noteworthy_offline.append(bot)
|
| +
|
| + noteworthy_new_failures = []
|
| + for bot in failed_bots:
|
| + previous_bot = findBot(bot.bot_name, previous_failures)
|
| +
|
| + if previous_bot is None:
|
| + noteworthy_new_failures.append(bot)
|
| +
|
| + noteworthy_new_offline_recoveries = []
|
| + for bot in previous_offline:
|
| + if bot.getHoursSinceLastRun() < CRITICAL_NUM_HOURS:
|
| + continue
|
| +
|
| + current_bot = findBot(bot.bot_name, offline_bots)
|
| + if current_bot is None:
|
| + noteworthy_new_offline_recoveries.append(bot)
|
| +
|
| + noteworthy_new_failure_recoveries = []
|
| + for bot in previous_failures:
|
| + current_bot = findBot(bot.bot_name, failed_bots)
|
| +
|
| + if current_bot is None:
|
| + noteworthy_new_failure_recoveries.append(bot)
|
| +
|
| + return {'offline': noteworthy_offline, 'failed': noteworthy_new_failures,
|
| + 'recovered_failures': noteworthy_new_failure_recoveries,
|
| + 'recovered_offline': noteworthy_new_offline_recoveries}
|
| +
|
| +def getNoteworthyStr(noteworthy_events):
|
| + s = ''
|
| +
|
| + if noteworthy_events['offline']:
|
| + s += 'IMPORTANT bots newly offline for over an hour:\n'
|
| +
|
| + for bot in noteworthy_events['offline']:
|
| + s += getBotStr(bot)
|
| +
|
| + s += '\n'
|
| +
|
| + if noteworthy_events['failed']:
|
| + s += 'IMPORTANT new failing bots:\n'
|
| +
|
| + for bot in noteworthy_events['failed']:
|
| + s += getBotStr(bot)
|
| +
|
| + s += '\n'
|
| +
|
| + if noteworthy_events['recovered_offline']:
|
| + s += 'IMPORTANT newly recovered previously offline bots:\n'
|
| +
|
| + for bot in noteworthy_events['recovered_offline']:
|
| + s += getBotStr(bot)
|
| +
|
| + s += '\n'
|
| +
|
| + if noteworthy_events['recovered_failures']:
|
| + s += 'IMPORTANT newly recovered failing bots:\n'
|
| +
|
| + for bot in noteworthy_events['recovered_failures']:
|
| + s += getBotStr(bot)
|
| +
|
| + s += '\n'
|
| +
|
| + return s
|
| +
|
| +def dictsToBots(bots):
|
| + offline_bots = []
|
| + for bot in bots['offline']:
|
| + offline_bots.append(GpuBot.fromDict(bot))
|
| +
|
| + failed_bots = []
|
| + for bot in bots['failed']:
|
| + failed_bots.append(GpuBot.fromDict(bot))
|
| +
|
| + return {'offline': offline_bots, 'failed': failed_bots}
|
| +
|
| +class GpuBotPoller:
|
| + DEFAULT_PREVIOUS_RESULTS_FILE = '.check_gpu_bots_previous_results'
|
| +
|
| + def __init__(self, emailer, send_email_for_recovered_offline_bots,
|
| + send_email_for_recovered_failing_bots, send_email_on_error,
|
| + previous_results_file):
|
| + self.emailer = emailer
|
| +
|
| + self.send_email_for_recovered_offline_bots = \
|
| + send_email_for_recovered_offline_bots
|
| +
|
| + self.send_email_for_recovered_failing_bots = \
|
| + send_email_for_recovered_failing_bots
|
| +
|
| + self.send_email_on_error = send_email_on_error
|
| + self.previous_results_file = previous_results_file
|
| +
|
| + def shouldEmail(self, noteworthy_events):
|
| + if noteworthy_events['offline'] or noteworthy_events['failed']:
|
| + return True
|
| +
|
| + if (self.send_email_for_recovered_offline_bots and
|
| + noteworthy_events['recovered_offline']):
|
| + return True
|
| +
|
| + if (self.send_email_for_recovered_failing_bots and
|
| + noteworthy_events['recovered_failures']):
|
| + return True
|
| +
|
| + return False
|
| +
|
| + def writeResults(self, summary):
|
| + results_file = (self.previous_results_file
|
| + if self.previous_results_file is not None
|
| + else GpuBotPoller.DEFAULT_PREVIOUS_RESULTS_FILE)
|
| +
|
| + with open(results_file, 'w') as f:
|
| + f.write(json.dumps(summary))
|
| +
|
| + def getPreviousResults(self):
|
| + previous_results_file = (self.previous_results_file
|
| + if self.previous_results_file is not None
|
| + else GpuBotPoller.DEFAULT_PREVIOUS_RESULTS_FILE)
|
| +
|
| + previous_results = {}
|
| + if os.path.isfile(previous_results_file):
|
| + with open(previous_results_file, 'r') as f:
|
| + previous_results = dictsToBots(json.loads(f.read()))
|
| +
|
| + return previous_results
|
| +
|
| + def checkBots(self):
|
| + time_str = 'Current time: %s\n\n' % (formatTime(time.localtime()))
|
| + print time_str
|
| +
|
| + try:
|
| + bots = Waterfall.getAllGpuBots()
|
| +
|
| + offline_bots = Waterfall.getOfflineBots(bots)
|
| + offline_str = getOfflineBotsStr(offline_bots)
|
| + print offline_str
|
| +
|
| + failed_bots = Waterfall.getFailedBots(bots)
|
| + failed_str = getFailedBotsStr(failed_bots)
|
| + print failed_str
|
| +
|
| + previous_results = self.getPreviousResults()
|
| + noteworthy_events = getNoteworthyEvents(offline_bots, failed_bots,
|
| + previous_results)
|
| +
|
| + noteworthy_str = getNoteworthyStr(noteworthy_events)
|
| + print noteworthy_str
|
| +
|
| + summary = getSummary(offline_bots, failed_bots)
|
| + self.writeResults(summary)
|
| +
|
| + if (self.emailer is not None and self.shouldEmail(noteworthy_events)):
|
| + self.emailer.send_email(Emailer.format_email_body(time_str, offline_str,
|
| + failed_str, noteworthy_str))
|
| + except Exception as e:
|
| + error_str = 'Error: %s' % str(e)
|
| + print error_str
|
| +
|
| + if self.send_email_on_error:
|
| + self.emailer.send_email(error_str)
|
| +
|
| +def parseArgs(sys_args):
|
| + parser = argparse.ArgumentParser(prog=sys_args[0],
|
| + description='Query the Chromium GPU Bots Waterfall, output ' +
|
| + 'potential problems, and optionally repeat automatically and/or ' +
|
| + 'email notifications of results.')
|
| +
|
| + parser.add_argument('--repeat-delay', type=int, dest='repeat_delay',
|
| + required=False,
|
| + help='How often to automatically re-run the script, in minutes.')
|
| +
|
| + parser.add_argument('--email-from', type=str, dest='email_from',
|
| + required=False,
|
| + help='Email address to send from. Requires also specifying ' +
|
| + '\'--email-to\'.')
|
| +
|
| + parser.add_argument('--email-to', type=str, dest='email_to', required=False,
|
| + nargs='+',
|
| + help='Email address(es) to send to. Requires also specifying ' +
|
| + '\'--email-from\'')
|
| +
|
| + parser.add_argument('--send-email-for-recovered-offline-bots',
|
| + dest='send_email_for_recovered_offline_bots', action='store_true',
|
| + default=False,
|
| + help='Send an email out when a bot which has been offline for more ' +
|
| + 'than 1 hour goes back online.')
|
| +
|
| + parser.add_argument('--send-email-for-recovered-failing-bots',
|
| + dest='send_email_for_recovered_failing_bots',
|
| + action='store_true', default=False,
|
| + help='Send an email when a failing bot recovers.')
|
| +
|
| + parser.add_argument('--send-email-on-error',
|
| + dest='send_email_on_error',
|
| + action='store_true', default=False,
|
| + help='Send an email when the script has an error. For example, if ' +
|
| + 'the server is unreachable.')
|
| +
|
| + parser.add_argument('--email-password-file',
|
| + dest='email_password_file',
|
| + required=False,
|
| + help=(('File containing the plaintext password of the source email ' +
|
| + 'account. By default, \'%s\' will be tried. If it does not exist, ' +
|
| + 'you will be prompted. If you opt to store your password on disk ' +
|
| + 'in plaintext, use of a dummy account is strongly recommended.')
|
| + % Emailer.DEFAULT_EMAIL_PASSWORD_FILE))
|
| +
|
| + parser.add_argument('--previous-results-file',
|
| + dest='previous_results_file',
|
| + required=False,
|
| + help=(('File to store the results of the previous invocation of ' +
|
| + 'this script. By default, \'%s\' will be used.')
|
| + % GpuBotPoller.DEFAULT_PREVIOUS_RESULTS_FILE))
|
| +
|
| + args = parser.parse_args(sys_args[1:])
|
| +
|
| + if args.email_from is not None and args.email_to is None:
|
| + parser.error('--email-from requires --email-to.')
|
| + elif args.email_to is not None and args.email_from is None:
|
| + parser.error('--email-to requires --email-from.')
|
| + elif args.email_from is None and args.send_email_for_recovered_offline_bots:
|
| + parser.error('--send-email-for-recovered-offline-bots requires ' +
|
| + '--email-to and --email-from.')
|
| + elif (args.email_from is None and args.send_email_for_recovered_failing_bots):
|
| + parser.error('--send-email-for-recovered-failing-bots ' +
|
| + 'requires --email-to and --email-from.')
|
| + elif (args.email_from is None and args.send_email_on_error):
|
| + parser.error('--send-email-on-error ' +
|
| + 'requires --email-to and --email-from.')
|
| + elif (args.email_password_file and
|
| + not os.path.isfile(args.email_password_file)):
|
| + parser.error('File does not exist: %s' % args.email_password_file)
|
| +
|
| + return args
|
| +
|
| +def main(sys_args):
|
| + args = parseArgs(sys_args)
|
| +
|
| + emailer = None
|
| + if args.email_from is not None and args.email_to is not None:
|
| + emailer = Emailer(args.email_from, args.email_to, args.email_password_file)
|
| +
|
| + try:
|
| + emailer.testEmailLogin()
|
| + except Exception as e:
|
| + print 'Error logging into email account: %s' % str(e)
|
| + return 1
|
| +
|
| + poller = GpuBotPoller(emailer,
|
| + args.send_email_for_recovered_offline_bots,
|
| + args.send_email_for_recovered_failing_bots,
|
| + args.send_email_on_error,
|
| + args.previous_results_file)
|
| +
|
| + while True:
|
| + poller.checkBots()
|
| +
|
| + if args.repeat_delay is None:
|
| + break
|
| +
|
| + print 'Will run again in %d minutes...\n' % args.repeat_delay
|
| + time.sleep(args.repeat_delay * 60)
|
| +
|
| + return 0
|
| +
|
| +if __name__ == '__main__':
|
| + sys.exit(main(sys.argv))
|
|
|