1# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Check USB device by running linux command on CfM"""
6
7from __future__ import print_function
8
9import logging
10import re
11import time
12from autotest_lib.client.common_lib.cros.manual import get_usb_devices
13from autotest_lib.client.common_lib.cros import power_cycle_usb_util
14
15CORE_DIR_LINES = 3
16ATRUS = '18d1:8001'
17
18def check_chrome_logfile(dut):
19    """
20    Get the latest chrome log file.
21    @param dut: The handle of the device under test.
22    @returns: the latest chrome log file
23    """
24    output = None
25    logging.info('---Get the latest chrome log file')
26    cmd = 'ls -latr /var/log/chrome/chrome'
27    try:
28        output = dut.run(cmd, ignore_status=True).stdout
29    except Exception as e:
30        logging.exception('Fail to run command %s.', cmd)
31        return None
32    logging.info('---cmd: %s', cmd)
33    logging.info('---output: %s', output.lower().strip())
34    return output
35
36def check_last_reboot(dut):
37    """
38    Get the last line of eventlog.txt
39    @param dut: The handle of the device under test.
40    @returns: the last line in eventlog.txt
41    """
42    output = None
43    cmd = 'tail -1 /var/log/eventlog.txt'
44    logging.info('---Get the latest reboot log')
45    try:
46        output = dut.run(cmd, ignore_status=True).stdout
47    except Exception as e:
48        logging.exception('Fail to run command %s.', cmd)
49        return None
50    logging.info('---cmd: %s', cmd)
51    logging.info('---output: %s', output.lower().strip())
52    return output
53
54def check_is_platform(dut, name):
55    """
56    Check whether CfM is expected platform.
57    @param dut: The handle of the device under test.
58    @param name: The name of platform
59    @returns: True, if CfM's platform is same as expected.
60              False, if not.
61    """
62    cmd = ("cat /var/log/platform_info.txt | grep name | "
63           "awk -v N=3 \'{print $N}\'")
64    output = dut.run(cmd, ignore_status=True).stdout.split()[0]
65    logging.info('---cmd: %s', cmd)
66    logging.info('---output: %s', output.lower())
67    return output.lower() == name
68
69
70def get_mgmt_ipv4(dut):
71    """
72    Get mgmt ipv4 address
73    @param dut: The handle of the device under test. Should be initialized in
74                 autotest.
75    @return: ipv4 address for mgmt interface.
76    """
77    cmd = 'ifconfig -a | grep eth0 -A 2 | grep netmask'
78    try:
79        output = dut.run(cmd, ignore_status=True).stdout
80    except Exception as e:
81        logging.exception('Fail to run command %s.', cmd)
82        return None
83    ipv4 = re.findall(r"inet\s*([0-9.]+)\s*netmask.*", output)[0]
84    return ipv4
85
86
87def retrieve_usb_devices(dut):
88    """
89    Populate output of usb-devices on CfM.
90    @param dut: handle of CfM under test
91    @returns dict of all usb devices detected on CfM.
92    """
93    usb_devices = (dut.run('usb-devices', ignore_status=True).
94                   stdout.strip().split('\n\n'))
95    usb_data = get_usb_devices.extract_usb_data(
96               '\nUSB-Device\n'+'\nUSB-Device\n'.join(usb_devices))
97    return usb_data
98
99
100def extract_peripherals_for_cfm(usb_data):
101    """
102    Check CfM has camera, speaker and Mimo connected.
103    @param usb_data: dict extracted from output of "usb-devices"
104    """
105    peripheral_map = {}
106
107    speaker_list = get_usb_devices.get_speakers(usb_data)
108    camera_list = get_usb_devices.get_cameras(usb_data)
109    display_list = get_usb_devices.get_display_mimo(usb_data)
110    controller_list = get_usb_devices.get_controller_mimo(usb_data)
111    for pid_vid, device_count in speaker_list.iteritems():
112        if device_count > 0:
113            peripheral_map[pid_vid] = device_count
114
115    for pid_vid, device_count in camera_list.iteritems():
116        if device_count > 0:
117            peripheral_map[pid_vid] = device_count
118
119    for pid_vid, device_count in controller_list.iteritems():
120        if device_count > 0:
121            peripheral_map[pid_vid] = device_count
122
123    for pid_vid, device_count in display_list.iteritems():
124        if device_count > 0:
125            peripheral_map[pid_vid] = device_count
126
127    for pid_vid, device_count in peripheral_map.iteritems():
128        logging.info('---device: %s (%s), count: %d',
129                     pid_vid, get_usb_devices.get_device_prod(pid_vid),
130                     device_count)
131
132    return peripheral_map
133
134
135def check_peripherals_for_cfm(peripheral_map):
136    """
137    Check CfM has one and only one camera,
138    one and only one speaker,
139    or one and only one mimo.
140    @param peripheral_map: dict for connected camera, speaker, or mimo.
141    @returns: True if check passes,
142              False if check fails.
143    """
144    peripherals = peripheral_map.keys()
145
146    type_camera = set(peripherals).intersection(get_usb_devices.CAMERA_LIST)
147    type_speaker = set(peripherals).intersection(get_usb_devices.SPEAKER_LIST)
148    type_controller = set(peripherals).intersection(\
149                      get_usb_devices.TOUCH_CONTROLLER_LIST)
150    type_panel = set(peripherals).intersection(\
151                 get_usb_devices.TOUCH_DISPLAY_LIST)
152
153    # check CfM have one, and only one type camera, huddly and mimo
154    if len(type_camera) == 0:
155        logging.info('No camera is found on CfM.')
156        return False
157
158    if not len(type_camera) == 1:
159        logging.info('More than one type of cameras are found on CfM.')
160        return False
161
162    if len(type_speaker) == 0:
163        logging.info('No speaker is found on CfM.')
164        return False
165
166    if not len(type_speaker) == 1:
167        logging.info('More than one type of speakers are found on CfM.')
168        return False
169
170    if len(type_controller) == 0:
171       logging.info('No controller is found on CfM.')
172       return False
173
174
175    if not len(type_controller) == 1:
176        logging.info('More than one type of controller are found on CfM.')
177        return False
178
179    if len(type_panel) == 0:
180        logging.info('No Display is found on CfM.')
181        return False
182
183    if not len(type_panel) == 1:
184        logging.info('More than one type of displays are found on CfM.')
185        return False
186
187    # check CfM have only one camera, huddly and mimo
188    for pid_vid, device_count in peripheral_map.iteritems():
189        if device_count > 1:
190            logging.info('Number of device %s connected to CfM : %d',
191                         get_usb_devices.get_device_prod(pid_vid),
192                         device_count)
193            return False
194
195    return True
196
197
198def check_usb_enumeration(dut, puts):
199    """
200    Check USB enumeration for devices
201    @param dut: the handle of CfM under test
202    @param puts: the list of peripherals under test
203    @returns True, none if test passes
204             False, errMsg if test test fails
205    """
206    usb_data = retrieve_usb_devices(dut)
207    if not usb_data:
208        logging.warning('No usb devices found on DUT')
209        return False, 'No usb device found on DUT.'
210    else:
211        usb_device_list = extract_peripherals_for_cfm(usb_data)
212        logging.info('---usb device = %s', usb_device_list)
213        if not set(puts).issubset(set(usb_device_list.keys())):
214            logging.info('Detect device fails for usb enumeration')
215            logging.info('Expect enumerated devices: %s', puts)
216            logging.info('Actual enumerated devices: %s',
217                         usb_device_list.keys())
218            return False, 'Some usb devices are not found.'
219        return True, None
220
221
222def check_usb_interface_initializion(dut, puts):
223    """
224    Check CfM shows valid interface for all peripherals connected.
225    @param dut: the handle of CfM under test
226    @param puts: the list of peripherals under test
227    @returns True, none if test passes
228             False, errMsg if test test fails
229    """
230    usb_data = retrieve_usb_devices(dut)
231    for put in puts:
232        number, health = get_usb_devices.is_usb_device_ok(usb_data, put)
233        logging.info('---device interface = %d, %s for %s',
234                     number, health,  get_usb_devices.get_device_prod(put))
235        if '0' in health:
236            logging.warning('Device %s has invalid interface', put)
237            return False, 'Device {} has invalid interface.'.format(put)
238    return True, None
239
240
241def clear_core_file(dut):
242    """clear core files"""
243    cmd = "rm -rf /var/spool/crash/*.*"
244    try:
245        dut.run_output(cmd)
246    except Exception as e:
247        logging.exception('Fail to clean core files under '
248                     '/var/spool/crash')
249        logging.exception('Fail to execute %s :', cmd)
250
251
252def check_process_crash(dut, cdlines):
253    """Check whether there is core file."""
254    cmd = 'ls -latr /var/spool/crash'
255    try:
256        core_files_output = dut.run_output(cmd).splitlines()
257    except Exception as e:
258        logging.exception('Can not find file under /var/spool/crash.')
259        logging.exception('Fail to execute %s:', cmd)
260        return True,  CORE_DIR_LINES
261    logging.info('---%s\n---%s', cmd, core_files_output)
262    if len(core_files_output) - cdlines <= 0:
263        logging.info('---length of files: %d', len(core_files_output))
264        return True, len(core_files_output)
265    else:
266        return False, len(core_files_output)
267
268
269def gpio_usb_test(dut, gpio_list, device_list, pause, board):
270    """
271    Run GPIO test to powercycle usb port.
272    @parama dut: handler of CfM,
273    @param gpio_list: the list of gpio ports,
274    @param device_list: the list of usb devices,
275    @param pause: time needs to wait before restoring power to usb port,
276                  in seconds
277    @param board: board name for CfM
278    @returns True
279    """
280    if not gpio_list:
281       gpio_list = []
282       for device in device_list:
283           vid, pid  = device.split(':')
284           logging.info('---check gpio for device %s:%s', vid, pid)
285           try:
286               ports = power_cycle_usb_util.get_target_all_gpio(dut, board,
287                                                            vid , pid)
288           except Exception as e:
289               errmsg = 'Fail to get gpio port.'
290               logging.exception('%s.', errmsg)
291               return False, errmsg
292           [gpio_list.append(_port) for _port in ports]
293
294    for port in gpio_list:
295        if not port:
296            continue
297        logging.info('+++powercycle gpio port %s', port)
298        try:
299            power_cycle_usb_util.power_cycle_usb_gpio(dut, port, pause)
300        except Exception as e:
301            errmsg = 'Fail to powercycle gpio port.'
302            logging.exception('%s.', errmsg)
303            return False, errmsg
304    return True, None
305
306
307def reboot_test(dut, pause):
308    """
309    Reboot CfM.
310    @parama dut: handler of CfM,
311    @param pause: time needs to wait after issuing reboot command, in seconds,
312
313    """
314    try:
315        dut.reboot()
316    except Exception as e:
317        logging.exception('Fail to reboot CfM.')
318        return False
319    logging.info('---reboot done')
320    time.sleep(pause)
321    return True
322
323
324
325def find_last_log(dut, speaker):
326    """
327    Get the lastlast_lines line for log files.
328    @param dut: handler of CfM
329    @param speaker: vidpid if speaker.
330    @returns: the list of string of the last line of logs.
331    """
332    last_lines = {
333              'messages':[],
334              'chrome':[],
335              'ui': [],
336              'atrus': []
337                  }
338    logging.debug('Get the last line of log file, speaker %s', speaker)
339    try:
340        cmd = "tail -1 /var/log/messages | awk -v N=1 '{print $N}'"
341        last_lines['messages'] = dut.run_output(cmd).strip().split()[0]
342        cmd = "tail -1 /var/log/chrome/chrome | awk -v N=1 '{print $N}'"
343        last_lines['chrome'] = dut.run_output(cmd).strip().split()[0]
344        cmd = "tail -1 /var/log/ui/ui.LATEST | awk -v N=1 '{print $N}'"
345        last_lines['ui']= dut.run_output(cmd)
346        if speaker == ATRUS and check_is_platform(dut, 'guado'):
347            logging.info('---atrus speaker %s connected to CfM', speaker)
348            cmd = 'tail -1 /var/log/atrus.log | awk -v N=1 "{print $N}"'
349            last_lines['atrus'] = dut.run_output(cmd).strip().split()[0]
350    except Exception as e:
351        logging.exception('Fail to get the last line from log files.')
352    for item, timestamp in last_lines.iteritems():
353        logging.debug('---%s: %s', item, timestamp)
354    return last_lines
355
356
357def collect_log_since_last_check(dut, lastlines, logfile):
358    """Collect log file since last check."""
359    output = None
360    if logfile == "messages":
361        cmd ='awk \'/{}/,0\' /var/log/messages'.format(lastlines[logfile])
362    if logfile == "chrome":
363        cmd ='awk \'/{}/,0\' /var/log/chrome/chrome'.format(lastlines[logfile])
364    if logfile == "ui":
365        cmd ='awk \'/{}/,0\' /var/log/ui/ui.LATEST'.format(lastlines[logfile])
366    if logfile == 'atrus':
367         cmd ='awk \'/{}/,0\' /var/log/atrus.log'.format(lastlines[logfile])
368    logging.info('---cmd = %s', cmd)
369    try:
370        output =  dut.run_output(cmd).split('\n')
371    except Exception as e:
372        logging.exception('Fail to get output from log files.')
373    logging.info('---length of log: %d', len(output))
374    if not output:
375        logging.info('--fail to find match log, check the latest log.')
376
377    if not output:
378        if logfile == "messages":
379            cmd ='cat /var/log/messages'
380        if logfile == "chrome":
381            cmd ='cat /var/log/chrome/chrome'
382        if logfile == "ui":
383            cmd ='cat /var/log/ui/ui.LATEST'
384        if logfile == 'atrus':
385            cmd ='cat /var/log/atrus.log'
386        output =  dut.run_output(cmd).split('\n')
387        logging.info('---length of log: %d', len(output))
388    return output
389
390def check_log(dut, timestamp, error_list, checkitem, logfile):
391    """
392    Check logfile does not contain any element in error_list[checkitem].
393    """
394    error_log_list = []
395    logging.info('---now check log %s in file %s', checkitem, logfile)
396    output = collect_log_since_last_check(dut, timestamp, logfile)
397    for _error in error_list[checkitem]:
398         error_log_list.extend([s for s in output if _error in str(s)])
399    if not error_log_list:
400        return True, None
401    else:
402        tempmsg = '\n'.join(error_log_list)
403        errmsg = 'Error_Found:in_log_file:{}:{}.'.format(logfile, tempmsg)
404        logging.info('---%s', errmsg)
405        return False, errmsg
406