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