machine_manager_unittest.py revision f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbe
1#!/usr/bin/python
2
3# Copyright 2012 Google Inc. All Rights Reserved.
4"""Unittest for machine_manager."""
5import os.path
6import time
7import hashlib
8
9import mock
10import unittest
11
12import image_chromeos
13import label
14import file_lock_machine
15import machine_manager
16import image_checksummer
17import test_flag
18
19from benchmark import Benchmark
20from benchmark_run import MockBenchmarkRun
21from cros_utils import command_executer
22from cros_utils import logger
23
24
25class MyMachineManager(machine_manager.MachineManager):
26
27  def __init__(self, chromeos_root):
28    super(MyMachineManager, self).__init__(chromeos_root, 0, 'average',
29                                           file_lock_machine.Machine.LOCKS_DIR)
30
31  def _TryToLockMachine(self, cros_machine):
32    self._machines.append(cros_machine)
33    cros_machine.checksum = ''
34
35  def AddMachine(self, machine_name):
36    with self._lock:
37      for m in self._all_machines:
38        assert m.name != machine_name, 'Tried to double-add %s' % machine_name
39      cm = machine_manager.MockCrosMachine(machine_name, self.chromeos_root,
40                                           'average')
41      assert cm.machine_checksum, ('Could not find checksum for machine %s' %
42                                   machine_name)
43      self._all_machines.append(cm)
44
45
46CHROMEOS_ROOT = '/tmp/chromeos-root'
47MACHINE_NAMES = ['lumpy1', 'lumpy2', 'lumpy3', 'daisy1', 'daisy2']
48LABEL_LUMPY = label.MockLabel('lumpy', 'lumpy_chromeos_image', CHROMEOS_ROOT,
49                              'lumpy', ['lumpy1', 'lumpy2', 'lumpy3', 'lumpy4'],
50                              '', '', False, 'average,'
51                              'gcc', None)
52LABEL_MIX = label.MockLabel('mix', 'chromeos_image', CHROMEOS_ROOT, 'mix',
53                            ['daisy1', 'daisy2', 'lumpy3', 'lumpy4'], '', '',
54                            False, 'average', 'gcc', None)
55
56
57class MachineManagerTest(unittest.TestCase):
58
59  mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
60
61  mock_logger = mock.Mock(spec=logger.Logger)
62
63  mock_lumpy1 = mock.Mock(spec=machine_manager.CrosMachine)
64  mock_lumpy2 = mock.Mock(spec=machine_manager.CrosMachine)
65  mock_lumpy3 = mock.Mock(spec=machine_manager.CrosMachine)
66  mock_lumpy4 = mock.Mock(spec=machine_manager.CrosMachine)
67  mock_daisy1 = mock.Mock(spec=machine_manager.CrosMachine)
68  mock_daisy2 = mock.Mock(spec=machine_manager.CrosMachine)
69
70  @mock.patch.object(os.path, 'isdir')
71  def setUp(self, mock_isdir):
72
73    mock_isdir.return_value = True
74    self.mm = machine_manager.MachineManager(
75        '/usr/local/chromeos', 0, 'average',
76        file_lock_machine.Machine.LOCKS_DIR, self.mock_cmd_exec,
77        self.mock_logger)
78
79    self.mock_lumpy1.name = 'lumpy1'
80    self.mock_lumpy2.name = 'lumpy2'
81    self.mock_lumpy3.name = 'lumpy3'
82    self.mock_lumpy4.name = 'lumpy4'
83    self.mock_daisy1.name = 'daisy1'
84    self.mock_daisy2.name = 'daisy2'
85    self.mock_lumpy1.machine_checksum = 'lumpy123'
86    self.mock_lumpy2.machine_checksum = 'lumpy123'
87    self.mock_lumpy3.machine_checksum = 'lumpy123'
88    self.mock_lumpy4.machine_checksum = 'lumpy123'
89    self.mock_daisy1.machine_checksum = 'daisy12'
90    self.mock_daisy2.machine_checksum = 'daisy12'
91    self.mock_lumpy1.checksum_string = 'lumpy_checksum_str'
92    self.mock_lumpy2.checksum_string = 'lumpy_checksum_str'
93    self.mock_lumpy3.checksum_string = 'lumpy_checksum_str'
94    self.mock_lumpy4.checksum_string = 'lumpy_checksum_str'
95    self.mock_daisy1.checksum_string = 'daisy_checksum_str'
96    self.mock_daisy2.checksum_string = 'daisy_checksum_str'
97    self.mock_lumpy1.cpuinfo = 'lumpy_cpu_info'
98    self.mock_lumpy2.cpuinfo = 'lumpy_cpu_info'
99    self.mock_lumpy3.cpuinfo = 'lumpy_cpu_info'
100    self.mock_lumpy4.cpuinfo = 'lumpy_cpu_info'
101    self.mock_daisy1.cpuinfo = 'daisy_cpu_info'
102    self.mock_daisy2.cpuinfo = 'daisy_cpu_info'
103    self.mm._all_machines.append(self.mock_daisy1)
104    self.mm._all_machines.append(self.mock_daisy2)
105    self.mm._all_machines.append(self.mock_lumpy1)
106    self.mm._all_machines.append(self.mock_lumpy2)
107    self.mm._all_machines.append(self.mock_lumpy3)
108
109  def testGetMachines(self):
110    manager = MyMachineManager(CHROMEOS_ROOT)
111    for m in MACHINE_NAMES:
112      manager.AddMachine(m)
113    names = [m.name for m in manager.GetMachines(LABEL_LUMPY)]
114    self.assertEqual(names, ['lumpy1', 'lumpy2', 'lumpy3'])
115
116  def testGetAvailableMachines(self):
117    manager = MyMachineManager(CHROMEOS_ROOT)
118    for m in MACHINE_NAMES:
119      manager.AddMachine(m)
120    for m in manager._all_machines:
121      if int(m.name[-1]) % 2:
122        manager._TryToLockMachine(m)
123    names = [m.name for m in manager.GetAvailableMachines(LABEL_LUMPY)]
124    self.assertEqual(names, ['lumpy1', 'lumpy3'])
125
126  @mock.patch.object(time, 'sleep')
127  @mock.patch.object(command_executer.CommandExecuter, 'RunCommand')
128  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommand')
129  @mock.patch.object(image_checksummer.ImageChecksummer, 'Checksum')
130  def test_image_machine(self, mock_checksummer, mock_run_croscmd, mock_run_cmd,
131                         mock_sleep):
132
133    def FakeMD5Checksum(input_str):
134      return 'machine_fake_md5_checksum'
135
136    self.fake_logger_count = 0
137    self.fake_logger_msgs = []
138
139    def FakeLogOutput(msg):
140      self.fake_logger_count += 1
141      self.fake_logger_msgs.append(msg)
142
143    def ResetValues():
144      self.fake_logger_count = 0
145      self.fake_logger_msgs = []
146      mock_run_cmd.reset_mock()
147      mock_run_croscmd.reset_mock()
148      mock_checksummer.reset_mock()
149      mock_sleep.reset_mock()
150      machine.checksum = 'fake_md5_checksum'
151      self.mm.checksum = None
152      self.mm.num_reimages = 0
153
154    self.mock_cmd_exec.CrosRunCommand = mock_run_croscmd
155    self.mock_cmd_exec.RunCommand = mock_run_cmd
156
157    self.mm.logger.LogOutput = FakeLogOutput
158    machine = self.mock_lumpy1
159    machine._GetMD5Checksum = FakeMD5Checksum
160    machine.checksum = 'fake_md5_checksum'
161    mock_checksummer.return_value = 'fake_md5_checksum'
162    self.mock_cmd_exec.log_level = 'verbose'
163
164    test_flag.SetTestMode(True)
165    # Test 1: label.image_type == "local"
166    LABEL_LUMPY.image_type = 'local'
167    self.mm.ImageMachine(machine, LABEL_LUMPY)
168    self.assertEqual(mock_run_cmd.call_count, 0)
169    self.assertEqual(mock_run_croscmd.call_count, 0)
170
171    #Test 2: label.image_type == "trybot"
172    ResetValues()
173    LABEL_LUMPY.image_type = 'trybot'
174    mock_run_cmd.return_value = 0
175    self.mm.ImageMachine(machine, LABEL_LUMPY)
176    self.assertEqual(mock_run_croscmd.call_count, 0)
177    self.assertEqual(mock_checksummer.call_count, 0)
178
179    # Test 3: label.image_type is neither local nor trybot; retval from
180    # RunCommand is 1, i.e. image_chromeos fails...
181    ResetValues()
182    LABEL_LUMPY.image_type = 'other'
183    mock_run_cmd.return_value = 1
184    try:
185      self.mm.ImageMachine(machine, LABEL_LUMPY)
186    except:
187      self.assertEqual(mock_checksummer.call_count, 0)
188      self.assertEqual(mock_run_cmd.call_count, 2)
189      self.assertEqual(mock_run_croscmd.call_count, 1)
190      self.assertEqual(mock_sleep.call_count, 1)
191      image_call_args_str = mock_run_cmd.call_args[0][0]
192      image_call_args = image_call_args_str.split(' ')
193      self.assertEqual(image_call_args[0], 'python')
194      self.assertEqual(image_call_args[1].split('/')[-1], 'image_chromeos.pyc')
195      image_call_args = image_call_args[2:]
196      self.assertEqual(image_call_args,
197                       ['--chromeos_root=/tmp/chromeos-root',
198                        '--image=lumpy_chromeos_image', '--image_args=',
199                        '--remote=lumpy1', '--logging_level=average',
200                        '--board=lumpy'])
201      self.assertEqual(mock_run_croscmd.call_args[0][0], 'reboot && exit')
202
203    # Test 4: Everything works properly. Trybot image type.
204    ResetValues()
205    LABEL_LUMPY.image_type = 'trybot'
206    mock_run_cmd.return_value = 0
207    self.mm.ImageMachine(machine, LABEL_LUMPY)
208    self.assertEqual(mock_checksummer.call_count, 0)
209    self.assertEqual(mock_run_croscmd.call_count, 0)
210    self.assertEqual(mock_sleep.call_count, 0)
211
212  def test_compute_common_checksum(self):
213
214    self.mm.machine_checksum = {}
215    self.mm.ComputeCommonCheckSum(LABEL_LUMPY)
216    self.assertEqual(self.mm.machine_checksum['lumpy'], 'lumpy123')
217    self.assertEqual(len(self.mm.machine_checksum), 1)
218
219    self.mm.machine_checksum = {}
220    self.assertRaises(machine_manager.BadChecksum,
221                      self.mm.ComputeCommonCheckSum, LABEL_MIX)
222
223  def test_compute_common_checksum_string(self):
224    self.mm.machine_checksum_string = {}
225    self.mm.ComputeCommonCheckSumString(LABEL_LUMPY)
226    self.assertEqual(len(self.mm.machine_checksum_string), 1)
227    self.assertEqual(self.mm.machine_checksum_string['lumpy'],
228                     'lumpy_checksum_str')
229
230    self.mm.machine_checksum_string = {}
231    self.mm.ComputeCommonCheckSumString(LABEL_MIX)
232    self.assertEqual(len(self.mm.machine_checksum_string), 1)
233    self.assertEqual(self.mm.machine_checksum_string['mix'],
234                     'daisy_checksum_str')
235
236  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
237  def test_try_to_lock_machine(self, mock_cros_runcmd):
238
239    self.assertRaises(self.mm._TryToLockMachine, None)
240
241    mock_cros_runcmd.return_value = [0, 'false_lock_checksum', '']
242    self.mock_cmd_exec.CrosRunCommand = mock_cros_runcmd
243    self.mm._machines = []
244    self.mm._TryToLockMachine(self.mock_lumpy1)
245    self.assertEqual(len(self.mm._machines), 1)
246    self.assertEqual(self.mm._machines[0], self.mock_lumpy1)
247    self.assertEqual(self.mock_lumpy1.checksum, 'false_lock_checksum')
248    self.assertEqual(mock_cros_runcmd.call_count, 1)
249    cmd_str = mock_cros_runcmd.call_args[0][0]
250    self.assertEqual(cmd_str, 'cat /usr/local/osimage_checksum_file')
251    args_dict = mock_cros_runcmd.call_args[1]
252    self.assertEqual(len(args_dict), 2)
253    self.assertEqual(args_dict['machine'], self.mock_lumpy1.name)
254    self.assertEqual(args_dict['chromeos_root'], '/usr/local/chromeos')
255
256  @mock.patch.object(machine_manager, 'CrosMachine')
257  def test_add_machine(self, mock_machine):
258
259    mock_machine.machine_checksum = 'daisy123'
260    self.assertEqual(len(self.mm._all_machines), 5)
261    self.mm.AddMachine('daisy3')
262    self.assertEqual(len(self.mm._all_machines), 6)
263
264    self.assertRaises(Exception, self.mm.AddMachine, 'lumpy1')
265
266  def test_remove_machine(self):
267    self.mm._machines = self.mm._all_machines
268    self.assertTrue(self.mock_lumpy2 in self.mm._machines)
269    self.mm.RemoveMachine(self.mock_lumpy2.name)
270    self.assertFalse(self.mock_lumpy2 in self.mm._machines)
271
272  def test_force_same_image_to_all_machines(self):
273    self.image_log = []
274
275    def FakeImageMachine(machine, label_arg):
276      image = label_arg.chromeos_image
277      self.image_log.append('Pushed %s onto %s' % (image, machine.name))
278
279    def FakeSetUpChecksumInfo():
280      pass
281
282    self.mm.ImageMachine = FakeImageMachine
283    self.mock_lumpy1.SetUpChecksumInfo = FakeSetUpChecksumInfo
284    self.mock_lumpy2.SetUpChecksumInfo = FakeSetUpChecksumInfo
285    self.mock_lumpy3.SetUpChecksumInfo = FakeSetUpChecksumInfo
286
287    self.mm.ForceSameImageToAllMachines(LABEL_LUMPY)
288    self.assertEqual(len(self.image_log), 3)
289    self.assertEqual(self.image_log[0],
290                     'Pushed lumpy_chromeos_image onto lumpy1')
291    self.assertEqual(self.image_log[1],
292                     'Pushed lumpy_chromeos_image onto lumpy2')
293    self.assertEqual(self.image_log[2],
294                     'Pushed lumpy_chromeos_image onto lumpy3')
295
296  @mock.patch.object(image_checksummer.ImageChecksummer, 'Checksum')
297  @mock.patch.object(hashlib, 'md5')
298  def test_acquire_machine(self, mock_md5, mock_checksum):
299
300    self.msgs = []
301    self.log_fatal_msgs = []
302
303    def FakeLock(machine):
304      self.msgs.append('Tried to lock %s' % machine.name)
305
306    def FakeLogFatal(msg):
307      self.log_fatal_msgs.append(msg)
308
309    self.mm._TryToLockMachine = FakeLock
310    self.mm.logger.LogFatal = FakeLogFatal
311
312    mock_md5.return_value = '123456'
313    mock_checksum.return_value = 'fake_md5_checksum'
314
315    self.mm._machines = self.mm._all_machines
316    self.mock_lumpy1.locked = True
317    self.mock_lumpy2.locked = True
318    self.mock_lumpy3.locked = False
319    self.mock_lumpy3.checksum = 'fake_md5_checksum'
320    self.mock_daisy1.locked = True
321    self.mock_daisy2.locked = False
322    self.mock_daisy2.checksum = 'fake_md5_checksum'
323
324    self.mock_lumpy1.released_time = time.time()
325    self.mock_lumpy2.released_time = time.time()
326    self.mock_lumpy3.released_time = time.time()
327    self.mock_daisy1.released_time = time.time()
328    self.mock_daisy2.released_time = time.time()
329
330    # Test 1. Basic test. Acquire lumpy3.
331    self.mm.AcquireMachine(LABEL_LUMPY)
332    m = self.mock_lumpy1
333    self.assertEqual(m, self.mock_lumpy1)
334    self.assertTrue(self.mock_lumpy1.locked)
335    self.assertEqual(mock_md5.call_count, 0)
336    self.assertEqual(self.msgs, ['Tried to lock lumpy1', 'Tried to lock lumpy2',
337                                 'Tried to lock lumpy3'])
338
339    # Test the second return statment (machine is unlocked, has no checksum)
340    save_locked = self.mock_lumpy1.locked
341    self.mock_lumpy1.locked = False
342    self.mock_lumpy1.checksum = None
343    m = self.mm.AcquireMachine(LABEL_LUMPY)
344    self.assertEqual(m, self.mock_lumpy1)
345    self.assertTrue(self.mock_lumpy1.locked)
346
347    # Test the third return statement:
348    #   - machine is unlocked
349    #   - checksums don't match
350    #   - current time minus release time is > 20.
351    self.mock_lumpy1.locked = False
352    self.mock_lumpy1.checksum = '123'
353    self.mock_lumpy1.released_time = time.time() - 8
354    m = self.mm.AcquireMachine(LABEL_LUMPY)
355    self.assertEqual(m, self.mock_lumpy1)
356    self.assertTrue(self.mock_lumpy1.locked)
357
358    # Test all machines are already locked.
359    m = self.mm.AcquireMachine(LABEL_LUMPY)
360    self.assertIsNone(m)
361
362    # Restore values of mock_lumpy1, so other tests succeed.
363    self.mock_lumpy1.locked = save_locked
364    self.mock_lumpy1.checksum = '123'
365
366  def test_get_available_machines(self):
367    self.mm._machines = self.mm._all_machines
368
369    machine_list = self.mm.GetAvailableMachines()
370    self.assertEqual(machine_list, self.mm._all_machines)
371
372    machine_list = self.mm.GetAvailableMachines(LABEL_MIX)
373    self.assertEqual(machine_list, [self.mock_daisy1, self.mock_daisy2,
374                                    self.mock_lumpy3])
375
376    machine_list = self.mm.GetAvailableMachines(LABEL_LUMPY)
377    self.assertEqual(machine_list, [self.mock_lumpy1, self.mock_lumpy2,
378                                    self.mock_lumpy3])
379
380  def test_get_machines(self):
381    machine_list = self.mm.GetMachines()
382    self.assertEqual(machine_list, self.mm._all_machines)
383
384    machine_list = self.mm.GetMachines(LABEL_MIX)
385    self.assertEqual(machine_list, [self.mock_daisy1, self.mock_daisy2,
386                                    self.mock_lumpy3])
387
388    machine_list = self.mm.GetMachines(LABEL_LUMPY)
389    self.assertEqual(machine_list, [self.mock_lumpy1, self.mock_lumpy2,
390                                    self.mock_lumpy3])
391
392  def test_release_machines(self):
393
394    self.mm._machines = [self.mock_lumpy1, self.mock_daisy2]
395
396    self.mock_lumpy1.locked = True
397    self.mock_daisy2.locked = True
398
399    self.assertTrue(self.mock_lumpy1.locked)
400    self.mm.ReleaseMachine(self.mock_lumpy1)
401    self.assertFalse(self.mock_lumpy1.locked)
402    self.assertEqual(self.mock_lumpy1.status, 'Available')
403
404    self.assertTrue(self.mock_daisy2.locked)
405    self.mm.ReleaseMachine(self.mock_daisy2)
406    self.assertFalse(self.mock_daisy2.locked)
407    self.assertEqual(self.mock_daisy2.status, 'Available')
408
409    # Test double-relase...
410    self.assertRaises(AssertionError, self.mm.ReleaseMachine, self.mock_lumpy1)
411
412  def test_cleanup(self):
413    self.mock_logger.reset_mock()
414    self.mm.Cleanup()
415    self.assertEqual(self.mock_logger.call_count, 0)
416
417  OUTPUT_STR = ('Machine Status:\nMachine                        Thread     '
418                'Lock Status                    Checksum'
419                '                        \nlumpy1                         test '
420                'run   True PENDING                   123'
421                '                             \nlumpy2                         '
422                'test run   False PENDING                   123'
423                '                             \nlumpy3                         '
424                'test run   False PENDING                   123'
425                '                             \ndaisy1                         '
426                'test run   False PENDING                   678'
427                '                             \ndaisy2                         '
428                'test run   True PENDING                   678'
429                '                             ')
430
431  def test_as_string(self):
432
433    mock_logger = mock.Mock(spec=logger.Logger)
434
435    bench = Benchmark('page_cycler.netsim.top_10',    # name
436                      'page_cycler.netsim.top_10',    # test_name
437                      '',             # test_args
438                      1,              # iteratins
439                      False,          # rm_chroot_tmp
440                      '',             # perf_args
441                      suite='telemetry_Crosperf')     # suite
442
443    test_run = MockBenchmarkRun('test run', bench, LABEL_LUMPY, 1, [], self.mm,
444                                mock_logger, 'verbose', '')
445
446    self.mm._machines = [self.mock_lumpy1, self.mock_lumpy2, self.mock_lumpy3,
447                         self.mock_daisy1, self.mock_daisy2]
448
449    self.mock_lumpy1.test_run = test_run
450    self.mock_lumpy2.test_run = test_run
451    self.mock_lumpy3.test_run = test_run
452    self.mock_daisy1.test_run = test_run
453    self.mock_daisy2.test_run = test_run
454
455    self.mock_lumpy1.locked = True
456    self.mock_lumpy2.locked = False
457    self.mock_lumpy3.locked = False
458    self.mock_daisy1.locked = False
459    self.mock_daisy2.locked = True
460
461    self.mock_lumpy1.checksum = '123'
462    self.mock_lumpy2.checksum = '123'
463    self.mock_lumpy3.checksum = '123'
464    self.mock_daisy1.checksum = '678'
465    self.mock_daisy2.checksum = '678'
466
467    output = self.mm.AsString()
468    self.assertEqual(output, self.OUTPUT_STR)
469
470  def test_get_all_cpu_info(self):
471    info = self.mm.GetAllCPUInfo([LABEL_LUMPY, LABEL_MIX])
472    self.assertEqual(info,
473                     'lumpy\n-------------------\nlumpy_cpu_info\n\n\nmix\n-'
474                     '------------------\ndaisy_cpu_info\n\n\n')
475
476
477MEMINFO_STRING = """MemTotal:        3990332 kB
478MemFree:         2608396 kB
479Buffers:          147168 kB
480Cached:           811560 kB
481SwapCached:            0 kB
482Active:           503480 kB
483Inactive:         628572 kB
484Active(anon):     174532 kB
485Inactive(anon):    88576 kB
486Active(file):     328948 kB
487Inactive(file):   539996 kB
488Unevictable:           0 kB
489Mlocked:               0 kB
490SwapTotal:       5845212 kB
491SwapFree:        5845212 kB
492Dirty:              9384 kB
493Writeback:             0 kB
494AnonPages:        173408 kB
495Mapped:           146268 kB
496Shmem:             89676 kB
497Slab:             188260 kB
498SReclaimable:     169208 kB
499SUnreclaim:        19052 kB
500KernelStack:        2032 kB
501PageTables:         7120 kB
502NFS_Unstable:          0 kB
503Bounce:                0 kB
504WritebackTmp:          0 kB
505CommitLimit:     7840376 kB
506Committed_AS:    1082032 kB
507VmallocTotal:   34359738367 kB
508VmallocUsed:      364980 kB
509VmallocChunk:   34359369407 kB
510DirectMap4k:       45824 kB
511DirectMap2M:     4096000 kB
512"""
513
514CPUINFO_STRING = """processor: 0
515vendor_id: GenuineIntel
516cpu family: 6
517model: 42
518model name: Intel(R) Celeron(R) CPU 867 @ 1.30GHz
519stepping: 7
520microcode: 0x25
521cpu MHz: 1300.000
522cache size: 2048 KB
523physical id: 0
524siblings: 2
525core id: 0
526cpu cores: 2
527apicid: 0
528initial apicid: 0
529fpu: yes
530fpu_exception: yes
531cpuid level: 13
532wp: yes
533flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer xsave lahf_lm arat epb xsaveopt pln pts dts tpr_shadow vnmi flexpriority ept vpid
534bogomips: 2594.17
535clflush size: 64
536cache_alignment: 64
537address sizes: 36 bits physical, 48 bits virtual
538power management:
539
540processor: 1
541vendor_id: GenuineIntel
542cpu family: 6
543model: 42
544model name: Intel(R) Celeron(R) CPU 867 @ 1.30GHz
545stepping: 7
546microcode: 0x25
547cpu MHz: 1300.000
548cache size: 2048 KB
549physical id: 0
550siblings: 2
551core id: 1
552cpu cores: 2
553apicid: 2
554initial apicid: 2
555fpu: yes
556fpu_exception: yes
557cpuid level: 13
558wp: yes
559flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer xsave lahf_lm arat epb xsaveopt pln pts dts tpr_shadow vnmi flexpriority ept vpid
560bogomips: 2594.17
561clflush size: 64
562cache_alignment: 64
563address sizes: 36 bits physical, 48 bits virtual
564power management:
565"""
566
567CHECKSUM_STRING = ('processor: 0vendor_id: GenuineIntelcpu family: 6model: '
568                   '42model name: Intel(R) Celeron(R) CPU 867 @ '
569                   '1.30GHzstepping: 7microcode: 0x25cache size: 2048 '
570                   'KBphysical id: 0siblings: 2core id: 0cpu cores: 2apicid: '
571                   '0initial apicid: 0fpu: yesfpu_exception: yescpuid level: '
572                   '13wp: yesflags: fpu vme de pse tsc msr pae mce cx8 apic sep'
573                   ' mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse '
574                   'sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc '
575                   'arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc '
576                   'aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 '
577                   'ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt '
578                   'tsc_deadline_timer xsave lahf_lm arat epb xsaveopt pln pts '
579                   'dts tpr_shadow vnmi flexpriority ept vpidclflush size: '
580                   '64cache_alignment: 64address sizes: 36 bits physical, 48 '
581                   'bits virtualpower management:processor: 1vendor_id: '
582                   'GenuineIntelcpu family: 6model: 42model name: Intel(R) '
583                   'Celeron(R) CPU 867 @ 1.30GHzstepping: 7microcode: 0x25cache'
584                   ' size: 2048 KBphysical id: 0siblings: 2core id: 1cpu cores:'
585                   ' 2apicid: 2initial apicid: 2fpu: yesfpu_exception: yescpuid'
586                   ' level: 13wp: yesflags: fpu vme de pse tsc msr pae mce cx8 '
587                   'apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx '
588                   'fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm '
589                   'constant_tsc arch_perfmon pebs bts rep_good nopl xtopology '
590                   'nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl '
591                   'vmx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic '
592                   'popcnt tsc_deadline_timer xsave lahf_lm arat epb xsaveopt '
593                   'pln pts dts tpr_shadow vnmi flexpriority ept vpidclflush '
594                   'size: 64cache_alignment: 64address sizes: 36 bits physical,'
595                   ' 48 bits virtualpower management: 4194304')
596
597DUMP_VPD_STRING = """
598"PBA_SN"="Pba.txt"
599"Product_S/N"="HT4L91SC300208"
600"serial_number"="HT4L91SC300208Z"
601"System_UUID"="12153006-1755-4f66-b410-c43758a71127"
602"shipping_country"="US"
603"initial_locale"="en-US"
604"keyboard_layout"="xkb:us::eng"
605"initial_timezone"="America/Los_Angeles"
606"MACAddress"=""
607"System_UUID"="29dd9c61-7fa1-4c83-b89a-502e7eb08afe"
608"ubind_attribute"="0c433ce7585f486730b682bb05626a12ce2d896e9b57665387f8ce2ccfdcc56d2e2f1483"
609"gbind_attribute"="7e9a851324088e269319347c6abb8d1572ec31022fa07e28998229afe8acb45c35a89b9d"
610"ActivateDate"="2013-38"
611"""
612
613IFCONFIG_STRING = """
614eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
615        inet 172.17.129.247  netmask 255.255.254.0  broadcast 172.17.129.255
616        inet6 2620:0:1000:3002:143:fed4:3ff6:279d  prefixlen 64  scopeid 0x0<global>
617        inet6 2620:0:1000:3002:4459:1399:1f02:9e4c  prefixlen 64  scopeid 0x0<global>
618        inet6 2620:0:1000:3002:d9e4:87b:d4ec:9a0e  prefixlen 64  scopeid 0x0<global>
619        inet6 2620:0:1000:3002:7d45:23f1:ea8a:9604  prefixlen 64  scopeid 0x0<global>
620        inet6 2620:0:1000:3002:250:b6ff:fe63:db65  prefixlen 64  scopeid 0x0<global>
621        inet6 fe80::250:b6ff:fe63:db65  prefixlen 64  scopeid 0x20<link>
622        ether 00:50:b6:63:db:65  txqueuelen 1000  (Ethernet)
623        RX packets 9817166  bytes 10865181708 (10.1 GiB)
624        RX errors 194  dropped 0  overruns 0  frame 194
625        TX packets 0  bytes 2265811903 (2.1 GiB)
626        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
627
628eth1: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
629        ether e8:03:9a:9c:50:3d  txqueuelen 1000  (Ethernet)
630        RX packets 0  bytes 0 (0.0 B)
631        RX errors 0  dropped 0  overruns 0  frame 0
632        TX packets 0  bytes 0 (0.0 B)
633        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
634
635lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 16436
636        inet 127.0.0.1  netmask 255.0.0.0
637        inet6 ::1  prefixlen 128  scopeid 0x10<host>
638        loop  txqueuelen 0  (Local Loopback)
639        RX packets 981004  bytes 1127468524 (1.0 GiB)
640        RX errors 0  dropped 0  overruns 0  frame 0
641        TX packets 981004  bytes 1127468524 (1.0 GiB)
642        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
643
644wlan0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
645        ether 44:6d:57:20:4a:c5  txqueuelen 1000  (Ethernet)
646        RX packets 0  bytes 0 (0.0 B)
647        RX errors 0  dropped 0  overruns 0  frame 0
648        TX packets 0  bytes 0 (0.0 B)
649        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
650"""
651
652
653class CrosMachineTest(unittest.TestCase):
654
655  mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
656
657  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
658  def test_init(self, mock_setup):
659
660    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
661                                     'average', self.mock_cmd_exec)
662    self.assertEqual(mock_setup.call_count, 1)
663    self.assertEqual(cm.chromeos_root, '/usr/local/chromeos')
664    self.assertEqual(cm.log_level, 'average')
665
666  @mock.patch.object(machine_manager.CrosMachine, 'IsReachable')
667  @mock.patch.object(machine_manager.CrosMachine, '_GetMemoryInfo')
668  @mock.patch.object(machine_manager.CrosMachine, '_GetCPUInfo')
669  @mock.patch.object(machine_manager.CrosMachine,
670                     '_ComputeMachineChecksumString')
671  @mock.patch.object(machine_manager.CrosMachine, '_GetMachineID')
672  @mock.patch.object(machine_manager.CrosMachine, '_GetMD5Checksum')
673  def test_setup_checksum_info(self, mock_md5sum, mock_machineid,
674                               mock_checkstring, mock_cpuinfo, mock_meminfo,
675                               mock_isreachable):
676
677    # Test 1. Machine is not reachable; SetUpChecksumInfo is called via
678    # __init__.
679    mock_isreachable.return_value = False
680    mock_md5sum.return_value = 'md5_checksum'
681    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
682                                     'average', self.mock_cmd_exec)
683    cm.checksum_string = 'This is a checksum string.'
684    cm.machine_id = 'machine_id1'
685    self.assertEqual(mock_isreachable.call_count, 1)
686    self.assertIsNone(cm.machine_checksum)
687    self.assertEqual(mock_meminfo.call_count, 0)
688
689    # Test 2. Machine is reachable. Call explicitly.
690    mock_isreachable.return_value = True
691    cm.checksum_string = 'This is a checksum string.'
692    cm.machine_id = 'machine_id1'
693    cm.SetUpChecksumInfo()
694    self.assertEqual(mock_isreachable.call_count, 2)
695    self.assertEqual(mock_meminfo.call_count, 1)
696    self.assertEqual(mock_cpuinfo.call_count, 1)
697    self.assertEqual(mock_checkstring.call_count, 1)
698    self.assertEqual(mock_machineid.call_count, 1)
699    self.assertEqual(mock_md5sum.call_count, 2)
700    self.assertEqual(cm.machine_checksum, 'md5_checksum')
701    self.assertEqual(cm.machine_id_checksum, 'md5_checksum')
702    self.assertEqual(mock_md5sum.call_args_list[0][0][0],
703                     'This is a checksum string.')
704    self.assertEqual(mock_md5sum.call_args_list[1][0][0], 'machine_id1')
705
706  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommand')
707  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
708  def test_is_reachable(self, mock_setup, mock_run_cmd):
709
710    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
711                                     'average', self.mock_cmd_exec)
712    self.mock_cmd_exec.CrosRunCommand = mock_run_cmd
713
714    # Test 1. CrosRunCommand returns 1 (fail)
715    mock_run_cmd.return_value = 1
716    result = cm.IsReachable()
717    self.assertFalse(result)
718    self.assertEqual(mock_setup.call_count, 1)
719    self.assertEqual(mock_run_cmd.call_count, 1)
720
721    # Test 2. CrosRunCommand returns 0 (success)
722    mock_run_cmd.return_value = 0
723    result = cm.IsReachable()
724    self.assertTrue(result)
725    self.assertEqual(mock_run_cmd.call_count, 2)
726    first_args = mock_run_cmd.call_args_list[0]
727    second_args = mock_run_cmd.call_args_list[1]
728    self.assertEqual(first_args[0], second_args[0])
729    self.assertEqual(first_args[1], second_args[1])
730    self.assertEqual(len(first_args[0]), 1)
731    self.assertEqual(len(first_args[1]), 2)
732    self.assertEqual(first_args[0][0], 'ls')
733    args_dict = first_args[1]
734    self.assertEqual(args_dict['machine'], 'daisy.cros')
735    self.assertEqual(args_dict['chromeos_root'], '/usr/local/chromeos')
736
737  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
738  def test_parse_memory_info(self, mock_setup):
739    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
740                                     'average', self.mock_cmd_exec)
741    cm.meminfo = MEMINFO_STRING
742    cm._ParseMemoryInfo()
743    self.assertEqual(cm.phys_kbytes, 4194304)
744
745  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
746  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
747  def test_get_memory_info(self, mock_setup, mock_run_cmd):
748    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
749                                     'average', self.mock_cmd_exec)
750    self.mock_cmd_exec.CrosRunCommand = mock_run_cmd
751    mock_run_cmd.return_value = [0, MEMINFO_STRING, '']
752    cm._GetMemoryInfo()
753    self.assertEqual(mock_run_cmd.call_count, 1)
754    call_args = mock_run_cmd.call_args_list[0]
755    self.assertEqual(call_args[0][0], 'cat /proc/meminfo')
756    args_dict = call_args[1]
757    self.assertEqual(args_dict['machine'], 'daisy.cros')
758    self.assertEqual(args_dict['chromeos_root'], '/usr/local/chromeos')
759    self.assertEqual(cm.meminfo, MEMINFO_STRING)
760    self.assertEqual(cm.phys_kbytes, 4194304)
761
762    mock_run_cmd.return_value = [1, MEMINFO_STRING, '']
763    self.assertRaises(cm._GetMemoryInfo)
764
765  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
766  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
767  def test_get_cpu_info(self, mock_setup, mock_run_cmd):
768    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
769                                     'average', self.mock_cmd_exec)
770    self.mock_cmd_exec.CrosRunCommand = mock_run_cmd
771    mock_run_cmd.return_value = [0, CPUINFO_STRING, '']
772    cm._GetCPUInfo()
773    self.assertEqual(mock_run_cmd.call_count, 1)
774    call_args = mock_run_cmd.call_args_list[0]
775    self.assertEqual(call_args[0][0], 'cat /proc/cpuinfo')
776    args_dict = call_args[1]
777    self.assertEqual(args_dict['machine'], 'daisy.cros')
778    self.assertEqual(args_dict['chromeos_root'], '/usr/local/chromeos')
779    self.assertEqual(cm.cpuinfo, CPUINFO_STRING)
780
781  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
782  def test_compute_machine_checksum_string(self, mock_setup):
783    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
784                                     'average', self.mock_cmd_exec)
785    cm.cpuinfo = CPUINFO_STRING
786    cm.meminfo = MEMINFO_STRING
787    cm._ParseMemoryInfo()
788    cm._ComputeMachineChecksumString()
789    self.assertEqual(cm.checksum_string, CHECKSUM_STRING)
790
791  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
792  def test_get_md5_checksum(self, mock_setup):
793    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
794                                     'average', self.mock_cmd_exec)
795    temp_str = 'abcde'
796    checksum_str = cm._GetMD5Checksum(temp_str)
797    self.assertEqual(checksum_str, 'ab56b4d92b40713acc5af89985d4b786')
798
799    temp_str = ''
800    checksum_str = cm._GetMD5Checksum(temp_str)
801    self.assertEqual(checksum_str, '')
802
803  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommand')
804  @mock.patch.object(machine_manager.CrosMachine, 'SetUpChecksumInfo')
805  def test_get_machine_id(self, mock_setup, mock_run_cmd):
806    cm = machine_manager.CrosMachine('daisy.cros', '/usr/local/chromeos',
807                                     'average', self.mock_cmd_exec)
808    self.mock_cmd_exec.CrosRunCommand = mock_run_cmd
809    mock_run_cmd.return_value = [0, DUMP_VPD_STRING, '']
810
811    cm._GetMachineID()
812    self.assertEqual(cm.machine_id, '"Product_S/N"="HT4L91SC300208"')
813
814    mock_run_cmd.return_value = [0, IFCONFIG_STRING, '']
815    cm._GetMachineID()
816    self.assertEqual(
817        cm.machine_id,
818        '        ether 00:50:b6:63:db:65  txqueuelen 1000  (Ethernet)_        '
819        'ether e8:03:9a:9c:50:3d  txqueuelen 1000  (Ethernet)_        ether '
820        '44:6d:57:20:4a:c5  txqueuelen 1000  (Ethernet)')
821
822    mock_run_cmd.return_value = [0, 'invalid hardware config', '']
823    self.assertRaises(cm._GetMachineID)
824
825
826if __name__ == '__main__':
827  unittest.main()
828