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