aboutsummaryrefslogtreecommitdiff
path: root/deprecated/crb/autotest_run.py
blob: 380c578b9a4f5e61f4900184c5cf1964b293f39f (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
import datetime
import getpass
import glob
import os
import pickle
import re
import threading
import time
import image_chromeos
import machine_manager_singleton
import table_formatter
from cros_utils import command_executer
from cros_utils import logger

SCRATCH_DIR = '/home/%s/cros_scratch' % getpass.getuser()
PICKLE_FILE = 'pickle.txt'
VERSION = '1'


def ConvertToFilename(text):
  ret = text
  ret = re.sub('/', '__', ret)
  ret = re.sub(' ', '_', ret)
  ret = re.sub('=', '', ret)
  ret = re.sub("\"", '', ret)
  return ret


class AutotestRun(threading.Thread):

  def __init__(self,
               autotest,
               chromeos_root='',
               chromeos_image='',
               board='',
               remote='',
               iteration=0,
               image_checksum='',
               exact_remote=False,
               rerun=False,
               rerun_if_failed=False):
    self.autotest = autotest
    self.chromeos_root = chromeos_root
    self.chromeos_image = chromeos_image
    self.board = board
    self.remote = remote
    self.iteration = iteration
    l = logger.GetLogger()
    l.LogFatalIf(not image_checksum, "Checksum shouldn't be None")
    self.image_checksum = image_checksum
    self.results = {}
    threading.Thread.__init__(self)
    self.terminate = False
    self.retval = None
    self.status = 'PENDING'
    self.run_completed = False
    self.exact_remote = exact_remote
    self.rerun = rerun
    self.rerun_if_failed = rerun_if_failed
    self.results_dir = None
    self.full_name = None

  @staticmethod
  def MeanExcludingSlowest(array):
    mean = sum(array) / len(array)
    array2 = []

    for v in array:
      if mean != 0 and abs(v - mean) / mean < 0.2:
        array2.append(v)

    if array2:
      return sum(array2) / len(array2)
    else:
      return mean

  @staticmethod
  def AddComposite(results_dict):
    composite_keys = []
    composite_dict = {}
    for key in results_dict:
      mo = re.match('(.*){\d+}', key)
      if mo:
        composite_keys.append(mo.group(1))
    for key in results_dict:
      for composite_key in composite_keys:
        if (key.count(composite_key) != 0 and
            table_formatter.IsFloat(results_dict[key])):
          if composite_key not in composite_dict:
            composite_dict[composite_key] = []
          composite_dict[composite_key].append(float(results_dict[key]))
          break

    for composite_key in composite_dict:
      v = composite_dict[composite_key]
      results_dict['%s[c]' % composite_key] = sum(v) / len(v)
      mean_excluding_slowest = AutotestRun.MeanExcludingSlowest(v)
      results_dict['%s[ce]' % composite_key] = mean_excluding_slowest

    return results_dict

  def ParseOutput(self):
    p = re.compile('^-+.*?^-+', re.DOTALL | re.MULTILINE)
    matches = p.findall(self.out)
    for i in range(len(matches)):
      results = matches[i]
      results_dict = {}
      for line in results.splitlines()[1:-1]:
        mo = re.match('(.*\S)\s+\[\s+(PASSED|FAILED)\s+\]', line)
        if mo:
          results_dict[mo.group(1)] = mo.group(2)
          continue
        mo = re.match('(.*\S)\s+(.*)', line)
        if mo:
          results_dict[mo.group(1)] = mo.group(2)

      # Add a composite keyval for tests like startup.
      results_dict = AutotestRun.AddComposite(results_dict)

      self.results = results_dict

      # This causes it to not parse the table again
      # Autotest recently added a secondary table
      # That reports errors and screws up the final pretty output.
      break
    mo = re.search('Results placed in (\S+)', self.out)
    if mo:
      self.results_dir = mo.group(1)
      self.full_name = os.path.basename(self.results_dir)

  def GetCacheHashBase(self):
    ret = ('%s %s %s' %
           (self.image_checksum, self.autotest.name, self.iteration))
    if self.autotest.args:
      ret += ' %s' % self.autotest.args
    ret += '-%s' % VERSION
    return ret

  def GetLabel(self):
    ret = '%s %s remote:%s' % (self.chromeos_image, self.autotest.name,
                               self.remote)
    return ret

  def TryToLoadFromCache(self):
    base = self.GetCacheHashBase()
    if self.exact_remote:
      if not self.remote:
        return False
      cache_dir_glob = '%s_%s' % (ConvertToFilename(base), self.remote)
    else:
      cache_dir_glob = '%s*' % ConvertToFilename(base)
    cache_path_glob = os.path.join(SCRATCH_DIR, cache_dir_glob)
    matching_dirs = glob.glob(cache_path_glob)
    if matching_dirs:
      matching_dir = matching_dirs[0]
      cache_file = os.path.join(matching_dir, PICKLE_FILE)
      assert os.path.isfile(cache_file)
      self._logger.LogOutput('Trying to read from cache file: %s' % cache_file)
      return self.ReadFromCache(cache_file)
    self._logger.LogOutput('Cache miss. AM going to run: %s for: %s' %
                           (self.autotest.name, self.chromeos_image))
    return False

  def ReadFromCache(self, cache_file):
    with open(cache_file, 'rb') as f:
      self.retval = pickle.load(f)
      self.out = pickle.load(f)
      self.err = pickle.load(f)
      self._logger.LogOutput(self.out)
      return True
    return False

  def StoreToCache(self):
    base = self.GetCacheHashBase()
    self.cache_dir = os.path.join(SCRATCH_DIR,
                                  '%s_%s' % (ConvertToFilename(base),
                                             self.remote))
    cache_file = os.path.join(self.cache_dir, PICKLE_FILE)
    command = 'mkdir -p %s' % os.path.dirname(cache_file)
    ret = self._ce.RunCommand(command)
    assert ret == 0, "Couldn't create cache dir"
    with open(cache_file, 'wb') as f:
      pickle.dump(self.retval, f)
      pickle.dump(self.out, f)
      pickle.dump(self.err, f)

  def run(self):
    self._logger = logger.Logger(
        os.path.dirname(__file__), '%s.%s' % (os.path.basename(__file__),
                                              self.name), True)
    self._ce = command_executer.GetCommandExecuter(self._logger)
    self.RunCached()

  def RunCached(self):
    self.status = 'WAITING'
    cache_hit = False
    if not self.rerun:
      cache_hit = self.TryToLoadFromCache()
    else:
      self._logger.LogOutput('--rerun passed. Not using cached results.')
    if self.rerun_if_failed and self.retval:
      self._logger.LogOutput('--rerun_if_failed passed and existing test '
                             'failed. Rerunning...')
      cache_hit = False
    if not cache_hit:
      # Get machine
      while True:
        if self.terminate:
          return 1
        self.machine = (machine_manager_singleton.MachineManagerSingleton(
        ).AcquireMachine(self.image_checksum))
        if self.machine:
          self._logger.LogOutput('%s: Machine %s acquired at %s' %
                                 (self.name, self.machine.name,
                                  datetime.datetime.now()))
          break
        else:
          sleep_duration = 10
          time.sleep(sleep_duration)
      try:
        self.remote = self.machine.name

        if self.machine.checksum != self.image_checksum:
          self.retval = self.ImageTo(self.machine.name)
          if self.retval:
            return self.retval
          self.machine.checksum = self.image_checksum
          self.machine.image = self.chromeos_image
        self.status = 'RUNNING: %s' % self.autotest.name
        [self.retval, self.out, self.err] = self.RunTestOn(self.machine.name)
        self.run_completed = True

      finally:
        self._logger.LogOutput('Releasing machine: %s' % self.machine.name)
        machine_manager_singleton.MachineManagerSingleton().ReleaseMachine(
            self.machine)
        self._logger.LogOutput('Released machine: %s' % self.machine.name)

      self.StoreToCache()

    if not self.retval:
      self.status = 'SUCCEEDED'
    else:
      self.status = 'FAILED'

    self.ParseOutput()
    # Copy results directory to the scratch dir
    if (not cache_hit and not self.retval and self.autotest.args and
        '--profile' in self.autotest.args):
      results_dir = os.path.join(self.chromeos_root, 'chroot',
                                 self.results_dir.lstrip('/'))
      tarball = os.path.join(
          self.cache_dir, os.path.basename(os.path.dirname(self.results_dir)))
      command = ('cd %s && tar cjf %s.tbz2 .' % (results_dir, tarball))
      self._ce.RunCommand(command)
      perf_data_file = os.path.join(self.results_dir, self.full_name,
                                    'profiling/iteration.1/perf.data')

      # Attempt to build a perf report and keep it with the results.
      command = ('cd %s/src/scripts &&'
                 ' cros_sdk -- /usr/sbin/perf report --symfs=/build/%s'
                 ' -i %s --stdio' % (self.chromeos_root, self.board,
                                     perf_data_file))
      ret, out, err = self._ce.RunCommandWOutput(command)
      with open(os.path.join(self.cache_dir, 'perf.report'), 'wb') as f:
        f.write(out)
    return self.retval

  def ImageTo(self, machine_name):
    image_args = [image_chromeos.__file__, '--chromeos_root=%s' %
                  self.chromeos_root, '--image=%s' % self.chromeos_image,
                  '--remote=%s' % machine_name]
    if self.board:
      image_args.append('--board=%s' % self.board)

###    devserver_port = 8080
###    mo = re.search("\d+", self.name)
###    if mo:
###      to_add = int(mo.group(0))
###      assert to_add < 100, "Too many threads launched!"
###      devserver_port += to_add

###    # I tried --noupdate_stateful, but that still fails when run in parallel.
###    image_args.append("--image_to_live_args=\"--devserver_port=%s"
###                      " --noupdate_stateful\"" % devserver_port)
###    image_args.append("--image_to_live_args=--devserver_port=%s" %
###                      devserver_port)

# Currently can't image two machines at once.
# So have to serialized on this lock.
    self.status = 'WAITING ON IMAGE_LOCK'
    with machine_manager_singleton.MachineManagerSingleton().image_lock:
      self.status = 'IMAGING'
      retval = self._ce.RunCommand(' '.join(['python'] + image_args))
      machine_manager_singleton.MachineManagerSingleton().num_reimages += 1
      if retval:
        self.status = 'ABORTED DUE TO IMAGE FAILURE'
    return retval

  def DoPowerdHack(self):
    command = 'sudo initctl stop powerd'
    self._ce.CrosRunCommand(command,
                            machine=self.machine.name,
                            chromeos_root=self.chromeos_root)

  def RunTestOn(self, machine_name):
    command = 'cd %s/src/scripts' % self.chromeos_root
    options = ''
    if self.board:
      options += ' --board=%s' % self.board
    if self.autotest.args:
      options += " --args='%s'" % self.autotest.args
    if 'tegra2' in self.board:
      self.DoPowerdHack()
    command += ('&& cros_sdk -- /usr/bin/test_that %s %s %s' %
                (options, machine_name, self.autotest.name))
    return self._ce.RunCommand(command, True)