aboutsummaryrefslogtreecommitdiff
path: root/deprecated/crb/crb_driver.py
blob: c6403462835632d5e94226ecbb422c614f250e13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#!/usr/bin/python2
#
# Copyright 2010 Google Inc. All Rights Reserved.

import datetime
import optparse
import os
import smtplib
import sys
import time
from email.mime.text import MIMEText

from autotest_gatherer import AutotestGatherer as AutotestGatherer
from autotest_run import AutotestRun as AutotestRun
from machine_manager_singleton import MachineManagerSingleton as MachineManagerSingleton
from cros_utils import logger
from cros_utils.file_utils import FileUtils


def CanonicalizeChromeOSRoot(chromeos_root):
  chromeos_root = os.path.expanduser(chromeos_root)
  if os.path.isfile(os.path.join(chromeos_root, 'src/scripts/enter_chroot.sh')):
    return chromeos_root
  else:
    return None


class Autotest(object):

  def __init__(self, autotest_string):
    self.name = None
    self.iterations = None
    self.args = None
    fields = autotest_string.split(',', 1)
    self.name = fields[0]
    if len(fields) > 1:
      autotest_string = fields[1]
      fields = autotest_string.split(',', 1)
    else:
      return
    self.iterations = int(fields[0])
    if len(fields) > 1:
      self.args = fields[1]
    else:
      return

  def __str__(self):
    return '\n'.join([self.name, self.iterations, self.args])


def CreateAutotestListFromString(autotest_strings, default_iterations=None):
  autotest_list = []
  for autotest_string in autotest_strings.split(':'):
    autotest = Autotest(autotest_string)
    if default_iterations and not autotest.iterations:
      autotest.iterations = default_iterations

    autotest_list.append(autotest)
  return autotest_list


def CreateAutotestRuns(images,
                       autotests,
                       remote,
                       board,
                       exact_remote,
                       rerun,
                       rerun_if_failed,
                       main_chromeos_root=None):
  autotest_runs = []
  for image in images:
    logger.GetLogger().LogOutput('Computing md5sum of: %s' % image)
    image_checksum = FileUtils().Md5File(image)
    logger.GetLogger().LogOutput('md5sum %s: %s' % (image, image_checksum))
    ###    image_checksum = "abcdefghi"

    chromeos_root = main_chromeos_root
    if not main_chromeos_root:
      image_chromeos_root = os.path.join(
          os.path.dirname(image), '../../../../..')
      chromeos_root = CanonicalizeChromeOSRoot(image_chromeos_root)
      assert chromeos_root, 'chromeos_root: %s invalid' % image_chromeos_root
    else:
      chromeos_root = CanonicalizeChromeOSRoot(main_chromeos_root)
      assert chromeos_root, 'chromeos_root: %s invalid' % main_chromeos_root

    # We just need a single ChromeOS root in the MachineManagerSingleton. It is
    # needed because we can save re-image time by checking the image checksum at
    # the beginning and assigning autotests to machines appropriately.
    if not MachineManagerSingleton().chromeos_root:
      MachineManagerSingleton().chromeos_root = chromeos_root

    for autotest in autotests:
      for iteration in range(autotest.iterations):
        autotest_run = AutotestRun(autotest,
                                   chromeos_root=chromeos_root,
                                   chromeos_image=image,
                                   board=board,
                                   remote=remote,
                                   iteration=iteration,
                                   image_checksum=image_checksum,
                                   exact_remote=exact_remote,
                                   rerun=rerun,
                                   rerun_if_failed=rerun_if_failed)
        autotest_runs.append(autotest_run)
  return autotest_runs


def GetNamesAndIterations(autotest_runs):
  strings = []
  for autotest_run in autotest_runs:
    strings.append('%s:%s' % (autotest_run.autotest.name,
                              autotest_run.iteration))
  return ' %s (%s)' % (len(strings), ' '.join(strings))


def GetStatusString(autotest_runs):
  status_bins = {}
  for autotest_run in autotest_runs:
    if autotest_run.status not in status_bins:
      status_bins[autotest_run.status] = []
    status_bins[autotest_run.status].append(autotest_run)

  status_strings = []
  for key, val in status_bins.items():
    status_strings.append('%s: %s' % (key, GetNamesAndIterations(val)))
  return 'Thread Status:\n%s' % '\n'.join(status_strings)


def GetProgressBar(num_done, num_total):
  ret = 'Done: %s%%' % int(100.0 * num_done / num_total)
  bar_length = 50
  done_char = '>'
  undone_char = ' '
  num_done_chars = bar_length * num_done / num_total
  num_undone_chars = bar_length - num_done_chars
  ret += ' [%s%s]' % (num_done_chars * done_char,
                      num_undone_chars * undone_char)
  return ret


def GetProgressString(start_time, num_remain, num_total):
  current_time = time.time()
  elapsed_time = current_time - start_time
  try:
    eta_seconds = float(num_remain) * elapsed_time / (num_total - num_remain)
    eta_seconds = int(eta_seconds)
    eta = datetime.timedelta(seconds=eta_seconds)
  except ZeroDivisionError:
    eta = 'Unknown'
  strings = []
  strings.append('Current time: %s Elapsed: %s ETA: %s' %
                 (datetime.datetime.now(),
                  datetime.timedelta(seconds=int(elapsed_time)), eta))
  strings.append(GetProgressBar(num_total - num_remain, num_total))
  return '\n'.join(strings)


def RunAutotestRunsInParallel(autotest_runs):
  start_time = time.time()
  active_threads = []
  for autotest_run in autotest_runs:
    # Set threads to daemon so program exits when ctrl-c is pressed.
    autotest_run.daemon = True
    autotest_run.start()
    active_threads.append(autotest_run)

  print_interval = 30
  last_printed_time = time.time()
  while active_threads:
    try:
      active_threads = [t for t in active_threads
                        if t is not None and t.isAlive()]
      for t in active_threads:
        t.join(1)
      if time.time() - last_printed_time > print_interval:
        border = '=============================='
        logger.GetLogger().LogOutput(border)
        logger.GetLogger().LogOutput(GetProgressString(start_time, len(
            [t for t in autotest_runs if t.status not in ['SUCCEEDED', 'FAILED']
            ]), len(autotest_runs)))
        logger.GetLogger().LogOutput(GetStatusString(autotest_runs))
        logger.GetLogger().LogOutput('%s\n' %
                                     MachineManagerSingleton().AsString())
        logger.GetLogger().LogOutput(border)
        last_printed_time = time.time()
    except KeyboardInterrupt:
      print 'C-c received... cleaning up threads.'
      for t in active_threads:
        t.terminate = True
      return 1
  return 0


def RunAutotestRunsSerially(autotest_runs):
  for autotest_run in autotest_runs:
    retval = autotest_run.Run()
    if retval:
      return retval


def ProduceTables(autotest_runs, full_table, fit_string):
  l = logger.GetLogger()
  ags_dict = {}
  for autotest_run in autotest_runs:
    name = autotest_run.full_name
    if name not in ags_dict:
      ags_dict[name] = AutotestGatherer()
    ags_dict[name].runs.append(autotest_run)
    output = ''
  for b, ag in ags_dict.items():
    output += 'Benchmark: %s\n' % b
    output += ag.GetFormattedMainTable(percents_only=not full_table,
                                       fit_string=fit_string)
    output += '\n'

  summary = ''
  for b, ag in ags_dict.items():
    summary += 'Benchmark Summary Table: %s\n' % b
    summary += ag.GetFormattedSummaryTable(percents_only=not full_table,
                                           fit_string=fit_string)
    summary += '\n'

  output += summary
  output += ('Number of re-images performed: %s' %
             MachineManagerSingleton().num_reimages)
  l.LogOutput(output)

  if autotest_runs:
    board = autotest_runs[0].board
  else:
    board = ''

  subject = '%s: %s' % (board, ', '.join(ags_dict.keys()))

  if any(autotest_run.run_completed for autotest_run in autotest_runs):
    SendEmailToUser(subject, summary)


def SendEmailToUser(subject, text_to_send):
  # Email summary to the current user.
  msg = MIMEText(text_to_send)

  # me == the sender's email address
  # you == the recipient's email address
  me = os.path.basename(__file__)
  you = os.getlogin()
  msg['Subject'] = '[%s] %s' % (os.path.basename(__file__), subject)
  msg['From'] = me
  msg['To'] = you

  # Send the message via our own SMTP server, but don't include the
  # envelope header.
  s = smtplib.SMTP('localhost')
  s.sendmail(me, [you], msg.as_string())
  s.quit()


def Main(argv):
  """The main function."""
  # Common initializations
  ###  command_executer.InitCommandExecuter(True)
  l = logger.GetLogger()

  parser = optparse.OptionParser()
  parser.add_option('-t',
                    '--tests',
                    dest='tests',
                    help=('Tests to compare.'
                          'Optionally specify per-test iterations by:'
                          '<test>,<iter>:<args>'))
  parser.add_option('-c',
                    '--chromeos_root',
                    dest='chromeos_root',
                    help='A *single* chromeos_root where scripts can be found.')
  parser.add_option('-n',
                    '--iterations',
                    dest='iterations',
                    help='Iterations to run per benchmark.',
                    default=1)
  parser.add_option('-r',
                    '--remote',
                    dest='remote',
                    help='The remote chromeos machine.')
  parser.add_option('-b', '--board', dest='board', help='The remote board.')
  parser.add_option('--full_table',
                    dest='full_table',
                    help='Print full tables.',
                    action='store_true',
                    default=True)
  parser.add_option('--exact_remote',
                    dest='exact_remote',
                    help='Run tests on the exact remote.',
                    action='store_true',
                    default=False)
  parser.add_option('--fit_string',
                    dest='fit_string',
                    help='Fit strings to fixed sizes.',
                    action='store_true',
                    default=False)
  parser.add_option('--rerun',
                    dest='rerun',
                    help='Re-run regardless of cache hit.',
                    action='store_true',
                    default=False)
  parser.add_option('--rerun_if_failed',
                    dest='rerun_if_failed',
                    help='Re-run if previous run was a failure.',
                    action='store_true',
                    default=False)
  parser.add_option('--no_lock',
                    dest='no_lock',
                    help='Do not lock the machine before running the tests.',
                    action='store_true',
                    default=False)
  l.LogOutput(' '.join(argv))
  [options, args] = parser.parse_args(argv)

  if options.remote is None:
    l.LogError('No remote machine specified.')
    parser.print_help()
    return 1

  if not options.board:
    l.LogError('No board specified.')
    parser.print_help()
    return 1

  remote = options.remote
  tests = options.tests
  board = options.board
  exact_remote = options.exact_remote
  iterations = int(options.iterations)

  autotests = CreateAutotestListFromString(tests, iterations)

  main_chromeos_root = options.chromeos_root
  images = args[1:]
  fit_string = options.fit_string
  full_table = options.full_table
  rerun = options.rerun
  rerun_if_failed = options.rerun_if_failed

  MachineManagerSingleton().no_lock = options.no_lock

  # Now try creating all the Autotests
  autotest_runs = CreateAutotestRuns(images, autotests, remote, board,
                                     exact_remote, rerun, rerun_if_failed,
                                     main_chromeos_root)

  try:
    # At this point we have all the autotest runs.
    for machine in remote.split(','):
      MachineManagerSingleton().AddMachine(machine)

    retval = RunAutotestRunsInParallel(autotest_runs)
    if retval:
      return retval

    # Now print tables
    ProduceTables(autotest_runs, full_table, fit_string)
  finally:
    # not sure why this isn't called at the end normally...
    MachineManagerSingleton().__del__()

  return 0


if __name__ == '__main__':
  sys.exit(Main(sys.argv))