• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 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"""Autotest for Logitech PTZPro firmware updater functionality and udev rule."""
5
6from __future__ import print_function
7import logging
8import os
9import re
10import time
11
12from autotest_lib.client.common_lib.cros import power_cycle_usb_util
13
14from autotest_lib.client.common_lib import error
15from autotest_lib.server import test
16
17POWER_CYCLE_WAIT_TIME = 1  # seconds
18UPDATER_WAIT_TIME = 100  # seconds
19
20
21class enterprise_CFM_LogitechPtzUpdater(test.test):
22    """Logitech Ptz Pro firmware updater functionality test in Chrome Box.
23
24    The procedure of the test is:
25    1. setup old firmware as dufault firmware on DUT
26    2. flash old version FW to device,
27    3. setup new firmware as default firmware on DUT
28    4. power cycle usb port to simulate unplug and replug of device, which
29       should be able to trigger udev rule and run the updater,
30    5. wait for the updater to finish,
31    6. run fw updater again and verify that the FW in device is consistent
32       with latest FW within system by checking the output.
33    """
34
35    version = 1
36
37    _LOG_FILE_PATH = '/tmp/logitech-updater.log'
38    _FW_PATH_BASE = '/lib/firmware/logitech'
39    _FW_PKG_ORIGIN = 'ptzpro2'
40    _FW_PKG_BACKUP = 'ptzpro2_backup'
41    _FW_PKG_TEST = 'ptzpro2_154'
42    _FW_PATH_ORIGIN = os.path.join(_FW_PATH_BASE, _FW_PKG_ORIGIN)
43    _FW_PATH_BACKUP = os.path.join(_FW_PATH_BASE, _FW_PKG_BACKUP)
44    _FW_PATH_TEST = os.path.join(_FW_PATH_BASE, _FW_PKG_TEST)
45    _DUT_BOARD = 'guado'
46    _SIS_VID = '046d'
47    _SIS_PID = '085f'
48
49    def initialize(self, host):
50        self.host = host
51        self.log_file = self._LOG_FILE_PATH
52        self.board = self._DUT_BOARD
53        self.vid = self._SIS_VID
54        self.pid = self._SIS_PID
55        # Open log file object.
56        self.log_file_obj = open(self.log_file, 'w')
57
58    def cleanup(self):
59        self.log_file_obj.close()
60        test.test.cleanup(self)
61
62        # Delete test firmware package.
63        cmd = 'rm -rf {}'.format(self._FW_PATH_TEST)
64        self._run_cmd(cmd)
65
66        # Delete the symlink created.
67        cmd = 'rm {}'.format(self._FW_PATH_ORIGIN)
68        self._run_cmd(cmd)
69
70        # Move the backup package back.
71        cmd = 'mv {} {}'.format(self._FW_PATH_BACKUP, self._FW_PATH_ORIGIN)
72        self._run_cmd(cmd)
73
74    def _run_cmd(self, command, str_compare='', print_output=False):
75        """Run command line on DUT.
76
77        Run commands on DUT. Wait for command to complete, then check
78        the output for expected string.
79
80        @param command: command line to run in dut.
81        @param str_compare: a piece of string we want to see in the
82                output of running the command.
83        @param print_output: if true, print command output in log.
84
85        @returns the command output and a bool value. If str_compare is
86               in command output, return true. Otherwise return false.
87
88        """
89
90        logging.info('Execute: %s', command)
91        result = self.host.run(command, ignore_status=True)
92        if result.stderr:
93            output = result.stderr
94        else:
95            output = result.stdout
96        if print_output:
97            logging.info('Output: %s', output.split('\n'))
98        if str_compare and str_compare not in ''.join(output):
99            return output, False
100        else:
101            return output, True
102
103    def convert_rootfs_writable(self):
104        """Remove rootfs verification on DUT, reboot,
105        and remount the filesystem read-writable"""
106
107        logging.info('Disabling rootfs verification...')
108        self.remove_rootfs_verification()
109
110        logging.info('Rebooting...')
111        self.reboot()
112
113        logging.info('Remounting..')
114        cmd = 'mount -o remount,rw /'
115        self._run_cmd(cmd)
116
117    def remove_rootfs_verification(self):
118        """Remove rootfs verification."""
119
120        # 2 & 4 are default partitions, and the system boots from one of them.
121        # Code from chromite/scripts/deploy_chrome.py
122        KERNEL_A_PARTITION = 2
123        KERNEL_B_PARTITION = 4
124
125        cmd_template = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
126                        '--remove_rootfs_verification --force')
127        for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
128            cmd = cmd_template % partition
129            self._run_cmd(cmd)
130
131    def reboot(self):
132        """Reboots the DUT."""
133
134        self.host.reboot()
135
136    def is_filesystem_readwrite(self):
137        """Check if the root file system is read-writable.
138
139        Query the DUT's filesystem /dev/root, often manifested as
140        /dev/dm-0 or is mounted as read-only or not.
141
142        @returns True if the /dev/root is read-writable. False otherwise.
143        """
144
145        cmd = 'cat /proc/mounts | grep "/dev/root"'
146        result, _ = self._run_cmd(cmd)
147        fields = re.split(' |,', result)
148        return True if fields.__len__() >= 4 and fields[3] == 'rw' else False
149
150    def copy_firmware(self):
151        """Copy test firmware from server to DUT."""
152
153        current_dir = os.path.dirname(os.path.realpath(__file__))
154        src_firmware_path = os.path.join(current_dir, self._FW_PKG_TEST)
155        dst_firmware_path = self._FW_PATH_BASE
156        logging.info('Copy firmware from {} to {}.'.format(src_firmware_path,
157                                                           dst_firmware_path))
158        self.host.send_file(src_firmware_path, dst_firmware_path, delete_dest=True)
159
160    def triger_updater(self):
161        """Triger udev rule to run fw updater by power cycling the usb."""
162
163        try:
164            power_cycle_usb_util.power_cycle_usb_vidpid(self.host, self.board,
165                                                        self.vid, self.pid)
166        except KeyError:
167            raise error.TestFail('Counld\'t find target device: '
168                                 'vid:pid {}:{}'.format(self.vid, self.pid))
169
170    def setup_fw(self, firmware_package):
171        """Setup firmware package that is going to be used for updating."""
172
173        firmware_path = os.path.join(self._FW_PATH_BASE, firmware_package)
174        cmd = 'ln -sfn {} {}'.format(firmware_path, self._FW_PATH_ORIGIN)
175        self._run_cmd(cmd)
176
177    def flash_fw(self, str_compare='', print_output=False, force=False):
178        """Flash certain firmware to device.
179
180        Run logitech firmware updater on DUT to flash the firmware setuped
181        to target device (PTZ Pro 2).
182
183        @param force: run with force update, will bypass fw version check.
184        @param str_compare, print_output: the same as function _run_cmd.
185
186        """
187
188        if force:
189            cmd_run_updater = ('/usr/sbin/logitech-updater'
190                               ' --log_to=stdout --update --force')
191        else:
192            cmd_run_updater = ('/usr/sbin/logitech-updater --log_to=stdout --update')
193        output, succeed = self._run_cmd(
194            cmd_run_updater, str_compare=str_compare, print_output=print_output)
195        return output, succeed
196
197    def run_once(self):
198        """Main test procedure."""
199
200        # Make the DUT filesystem writable.
201        if not self.is_filesystem_readwrite():
202            logging.info('DUT root file system is not read-writable. '
203                         'Converting it read-writable...')
204            self.convert_rootfs_writable()
205        else:
206            logging.info('DUT is read-writable.')
207
208        # Copy old FW to device.
209        cmd = 'mv {} {}'.format(self._FW_PATH_ORIGIN, self._FW_PATH_BACKUP)
210        self._run_cmd(cmd)
211        self.copy_firmware()
212
213        # Flash old FW to device.
214        self.setup_fw(self._FW_PKG_TEST)
215        expect_output = 'Done. Updated firmwares successfully.'
216        output, succeed = self.flash_fw(str_compare=expect_output, force=True)
217        self.log_file_obj.write('{}Log info for writing '
218                                'old firmware{}\n'.format('-' * 8, '-' * 8))
219        self.log_file_obj.write(output)
220        if not succeed:
221            raise error.TestFail('Expect \'{}\' in output, '
222                                 'but didn\'t find it.'.format(expect_output))
223
224        # Triger udev to run FW updater.
225        self.setup_fw(self._FW_PKG_BACKUP)
226        self.triger_updater()
227
228        # Wait for fw updater to finish.
229        time.sleep(UPDATER_WAIT_TIME)
230
231        # Try flash the new firmware, should detect same fw version.
232        expect_output = 'Firmware is up to date.'
233        output, succeed = self.flash_fw(str_compare=expect_output)
234        self.log_file_obj.write('{}Log info for writing '
235                                'new firmware{}\n'.format('-' * 8, '-' * 8))
236        self.log_file_obj.write(output)
237        if not succeed:
238            raise error.TestFail('Expect {} in output '
239                                 'but didn\'t find it.'.format(expect_output))
240