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