xref: /aosp_15_r20/external/autotest/server/cros/debugd_dev_tools.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2014 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport logging
6*9c5db199SXin Liimport os
7*9c5db199SXin Li
8*9c5db199SXin Liimport common
9*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
10*9c5db199SXin Li
11*9c5db199SXin Li"""
12*9c5db199SXin LiFunctions to query and control debugd dev tools.
13*9c5db199SXin Li
14*9c5db199SXin LiThis file provides a set of functions to check the general state of the
15*9c5db199SXin Lidebugd dev tools, and a set of classes to interface to the individual
16*9c5db199SXin Litools.
17*9c5db199SXin Li
18*9c5db199SXin LiCurrent tool classes are:
19*9c5db199SXin Li    RootfsVerificationTool
20*9c5db199SXin Li    BootFromUsbTool
21*9c5db199SXin Li    SshServerTool
22*9c5db199SXin Li    SystemPasswordTool
23*9c5db199SXin LiThese classes have functions to check the state and enable/disable the
24*9c5db199SXin Litool. Some tools may not be able to disable themselves, in which case
25*9c5db199SXin Lian exception will be thrown (for example, RootfsVerificationTool cannot
26*9c5db199SXin Libe disabled).
27*9c5db199SXin Li
28*9c5db199SXin LiGeneral usage will look something like this:
29*9c5db199SXin Li
30*9c5db199SXin Li# Make sure tools are accessible on the system.
31*9c5db199SXin Liif debugd_dev_tools.are_dev_tools_available(host):
32*9c5db199SXin Li    # Create the tool(s) you want to interact with.
33*9c5db199SXin Li    tools = [debugd_dev_tools.SshServerTool(), ...]
34*9c5db199SXin Li    for tool in tools:
35*9c5db199SXin Li        # Initialize tools and save current state.
36*9c5db199SXin Li        tool.initialize(host, save_initial_state=True)
37*9c5db199SXin Li        # Perform required action with tools.
38*9c5db199SXin Li        tool.enable()
39*9c5db199SXin Li        # Restore initial tool state.
40*9c5db199SXin Li        tool.restore_state()
41*9c5db199SXin Li    # Clean up temporary files.
42*9c5db199SXin Li    debugd_dev_tools.remove_temp_files()
43*9c5db199SXin Li"""
44*9c5db199SXin Li
45*9c5db199SXin Li
46*9c5db199SXin Li# Defined in system_api/dbus/service_constants.h.
47*9c5db199SXin LiDEV_FEATURES_DISABLED = 1 << 0
48*9c5db199SXin LiDEV_FEATURE_ROOTFS_VERIFICATION_REMOVED = 1 << 1
49*9c5db199SXin LiDEV_FEATURE_BOOT_FROM_USB_ENABLED = 1 << 2
50*9c5db199SXin LiDEV_FEATURE_SSH_SERVER_CONFIGURED = 1 << 3
51*9c5db199SXin LiDEV_FEATURE_DEV_MODE_ROOT_PASSWORD_SET = 1 << 4
52*9c5db199SXin LiDEV_FEATURE_SYSTEM_ROOT_PASSWORD_SET = 1 << 5
53*9c5db199SXin Li
54*9c5db199SXin Li
55*9c5db199SXin Li# Location to save temporary files to store and load state. This folder should
56*9c5db199SXin Li# be persistent through a power cycle so we can't use /tmp.
57*9c5db199SXin Li_TEMP_DIR = '/usr/local/autotest/tmp/debugd_dev_tools'
58*9c5db199SXin Li
59*9c5db199SXin Li
60*9c5db199SXin Liclass AccessError(error.CmdError):
61*9c5db199SXin Li    """Raised when debugd D-Bus access fails."""
62*9c5db199SXin Li    pass
63*9c5db199SXin Li
64*9c5db199SXin Li
65*9c5db199SXin Liclass FeatureUnavailableError(error.TestNAError):
66*9c5db199SXin Li    """Raised when a feature cannot be enabled or disabled."""
67*9c5db199SXin Li    pass
68*9c5db199SXin Li
69*9c5db199SXin Li
70*9c5db199SXin Lidef query_dev_tools_state(host):
71*9c5db199SXin Li    """
72*9c5db199SXin Li    Queries debugd for the current dev features state.
73*9c5db199SXin Li
74*9c5db199SXin Li    @param host: Host device.
75*9c5db199SXin Li
76*9c5db199SXin Li    @return: Integer debugd query return value.
77*9c5db199SXin Li
78*9c5db199SXin Li    @raise AccessError: Can't talk to debugd on the host.
79*9c5db199SXin Li    """
80*9c5db199SXin Li    result = _send_debugd_command(host, 'QueryDevFeatures')
81*9c5db199SXin Li    state = int(result.stdout)
82*9c5db199SXin Li    logging.debug('query_dev_tools_state = %d (0x%04X)', state, state)
83*9c5db199SXin Li    return state
84*9c5db199SXin Li
85*9c5db199SXin Li
86*9c5db199SXin Lidef are_dev_tools_available(host):
87*9c5db199SXin Li    """
88*9c5db199SXin Li    Check if dev tools are available on the host.
89*9c5db199SXin Li
90*9c5db199SXin Li    @param host: Host device.
91*9c5db199SXin Li
92*9c5db199SXin Li    @return: True if tools are available, False otherwise.
93*9c5db199SXin Li    """
94*9c5db199SXin Li    try:
95*9c5db199SXin Li        return query_dev_tools_state(host) != DEV_FEATURES_DISABLED
96*9c5db199SXin Li    except AccessError:
97*9c5db199SXin Li        return False
98*9c5db199SXin Li
99*9c5db199SXin Li
100*9c5db199SXin Lidef remove_temp_files(host):
101*9c5db199SXin Li    """
102*9c5db199SXin Li    Removes all DevTools temporary files and directories.
103*9c5db199SXin Li
104*9c5db199SXin Li    Any test using dev tools should try to call this just before
105*9c5db199SXin Li    exiting to erase any temporary files that may have been saved.
106*9c5db199SXin Li
107*9c5db199SXin Li    @param host: Host device.
108*9c5db199SXin Li    """
109*9c5db199SXin Li    host.run('rm -rf "%s"' % _TEMP_DIR)
110*9c5db199SXin Li
111*9c5db199SXin Li
112*9c5db199SXin Lidef expect_access_failure(host, tools):
113*9c5db199SXin Li    """
114*9c5db199SXin Li    Verifies that access is denied to all provided tools.
115*9c5db199SXin Li
116*9c5db199SXin Li    Will check are_dev_tools_available() first to try to avoid changing
117*9c5db199SXin Li    device state in case access is allowed. Otherwise, the function
118*9c5db199SXin Li    will try to enable each tool in the list and throw an exception if
119*9c5db199SXin Li    any succeeds.
120*9c5db199SXin Li
121*9c5db199SXin Li    @param host: Host device.
122*9c5db199SXin Li    @param tools: List of tools to checks.
123*9c5db199SXin Li
124*9c5db199SXin Li    @raise TestFail: are_dev_tools_available() returned True or
125*9c5db199SXin Li                     a tool successfully enabled.
126*9c5db199SXin Li    """
127*9c5db199SXin Li    if are_dev_tools_available(host):
128*9c5db199SXin Li        raise error.TestFail('Unexpected dev tool access success')
129*9c5db199SXin Li    for tool in tools:
130*9c5db199SXin Li        try:
131*9c5db199SXin Li            tool.enable()
132*9c5db199SXin Li        except AccessError:
133*9c5db199SXin Li            # We want an exception, otherwise the tool succeeded.
134*9c5db199SXin Li            pass
135*9c5db199SXin Li        else:
136*9c5db199SXin Li            raise error.TestFail('Unexpected %s enable success.' % tool)
137*9c5db199SXin Li
138*9c5db199SXin Li
139*9c5db199SXin Lidef _send_debugd_command(host, name, args=()):
140*9c5db199SXin Li    """
141*9c5db199SXin Li    Sends a debugd command.
142*9c5db199SXin Li
143*9c5db199SXin Li    @param host: Host to run the command on.
144*9c5db199SXin Li    @param name: String debugd D-Bus function name.
145*9c5db199SXin Li    @param args: List of string arguments to pass to dbus-send.
146*9c5db199SXin Li
147*9c5db199SXin Li    @return: The dbus-send CmdResult object.
148*9c5db199SXin Li
149*9c5db199SXin Li    @raise AccessError: debugd call returned an error.
150*9c5db199SXin Li    """
151*9c5db199SXin Li    command = ('dbus-send --system --fixed --print-reply '
152*9c5db199SXin Li               '--dest=org.chromium.debugd /org/chromium/debugd '
153*9c5db199SXin Li               '"org.chromium.debugd.%s"' % name)
154*9c5db199SXin Li    for arg in args:
155*9c5db199SXin Li        command += ' %s' % arg
156*9c5db199SXin Li    try:
157*9c5db199SXin Li        return host.run(command)
158*9c5db199SXin Li    except error.CmdError as e:
159*9c5db199SXin Li        raise AccessError(e.command, e.result_obj, e.additional_text)
160*9c5db199SXin Li
161*9c5db199SXin Li
162*9c5db199SXin Liclass DevTool(object):
163*9c5db199SXin Li    """
164*9c5db199SXin Li    Parent tool class.
165*9c5db199SXin Li
166*9c5db199SXin Li    Each dev tool has its own child class that handles the details
167*9c5db199SXin Li    of disabling, enabling, and querying the functionality. This class
168*9c5db199SXin Li    provides some common functionality needed by multiple tools.
169*9c5db199SXin Li
170*9c5db199SXin Li    Child classes should implement the following:
171*9c5db199SXin Li      - is_enabled(): use debugd to query whether the tool is enabled.
172*9c5db199SXin Li      - enable(): use debugd to enable the tool.
173*9c5db199SXin Li      - disable(): manually disable the tool.
174*9c5db199SXin Li      - save_state(): record the current tool state on the host.
175*9c5db199SXin Li      - restore_state(): restore the saved tool state.
176*9c5db199SXin Li
177*9c5db199SXin Li    If a child class cannot perform the required action (for
178*9c5db199SXin Li    example the rootfs tool can't currently restore its initial
179*9c5db199SXin Li    state), leave the function unimplemented so it will throw an
180*9c5db199SXin Li    exception if a test attempts to use it.
181*9c5db199SXin Li    """
182*9c5db199SXin Li
183*9c5db199SXin Li
184*9c5db199SXin Li    def initialize(self, host, save_initial_state=False):
185*9c5db199SXin Li        """
186*9c5db199SXin Li        Sets up the initial tool state. This must be called on
187*9c5db199SXin Li        every tool before use.
188*9c5db199SXin Li
189*9c5db199SXin Li        @param host: Device host the test is running on.
190*9c5db199SXin Li        @param save_initial_state: True to save the device state.
191*9c5db199SXin Li        """
192*9c5db199SXin Li        self._host = host
193*9c5db199SXin Li        if save_initial_state:
194*9c5db199SXin Li            self.save_state()
195*9c5db199SXin Li
196*9c5db199SXin Li
197*9c5db199SXin Li    def is_enabled(self):
198*9c5db199SXin Li        """
199*9c5db199SXin Li        Each tool should override this to query itself using debugd.
200*9c5db199SXin Li        Normally this can be done by using the provided
201*9c5db199SXin Li        _check_enabled() function.
202*9c5db199SXin Li        """
203*9c5db199SXin Li        self._unimplemented_function_error('is_enabled')
204*9c5db199SXin Li
205*9c5db199SXin Li
206*9c5db199SXin Li    def enable(self):
207*9c5db199SXin Li        """
208*9c5db199SXin Li        Each tool should override this to enable itself using debugd.
209*9c5db199SXin Li        """
210*9c5db199SXin Li        self._unimplemented_function_error('enable')
211*9c5db199SXin Li
212*9c5db199SXin Li
213*9c5db199SXin Li    def disable(self):
214*9c5db199SXin Li        """
215*9c5db199SXin Li        Each tool should override this to disable itself.
216*9c5db199SXin Li        """
217*9c5db199SXin Li        self._unimplemented_function_error('disable')
218*9c5db199SXin Li
219*9c5db199SXin Li
220*9c5db199SXin Li    def save_state(self):
221*9c5db199SXin Li        """
222*9c5db199SXin Li        Save the initial tool state. Should be overridden by child
223*9c5db199SXin Li        tool classes.
224*9c5db199SXin Li        """
225*9c5db199SXin Li        self._unimplemented_function_error('_save_state')
226*9c5db199SXin Li
227*9c5db199SXin Li
228*9c5db199SXin Li    def restore_state(self):
229*9c5db199SXin Li        """
230*9c5db199SXin Li        Restore the initial tool state. Should be overridden by child
231*9c5db199SXin Li        tool classes.
232*9c5db199SXin Li        """
233*9c5db199SXin Li        self._unimplemented_function_error('_restore_state')
234*9c5db199SXin Li
235*9c5db199SXin Li
236*9c5db199SXin Li    def _check_enabled(self, bits):
237*9c5db199SXin Li        """
238*9c5db199SXin Li        Checks if the given feature is currently enabled according to
239*9c5db199SXin Li        the debugd status query function.
240*9c5db199SXin Li
241*9c5db199SXin Li        @param bits: Integer status bits corresponding to the features.
242*9c5db199SXin Li
243*9c5db199SXin Li        @return: True if the status query is enabled and the
244*9c5db199SXin Li                 indicated bits are all set, False otherwise.
245*9c5db199SXin Li        """
246*9c5db199SXin Li        state = query_dev_tools_state(self._host)
247*9c5db199SXin Li        enabled = bool((state != DEV_FEATURES_DISABLED) and
248*9c5db199SXin Li                       (state & bits == bits))
249*9c5db199SXin Li        logging.debug('%s _check_enabled = %s (0x%04X / 0x%04X)',
250*9c5db199SXin Li                      self, enabled, state, bits)
251*9c5db199SXin Li        return enabled
252*9c5db199SXin Li
253*9c5db199SXin Li
254*9c5db199SXin Li    def _get_temp_path(self, source_path):
255*9c5db199SXin Li        """
256*9c5db199SXin Li        Get temporary storage path for a file or directory.
257*9c5db199SXin Li
258*9c5db199SXin Li        Temporary path is based on the tool class name and the
259*9c5db199SXin Li        source directory to keep tool files isolated and prevent
260*9c5db199SXin Li        name conflicts within tools.
261*9c5db199SXin Li
262*9c5db199SXin Li        The function returns a full temporary path corresponding to
263*9c5db199SXin Li        |source_path|.
264*9c5db199SXin Li
265*9c5db199SXin Li        For example, _get_temp_path('/foo/bar.txt') would return
266*9c5db199SXin Li        '/path/to/temp/folder/debugd_dev_tools/FooTool/foo/bar.txt'.
267*9c5db199SXin Li
268*9c5db199SXin Li        @param source_path: String path to the file or directory.
269*9c5db199SXin Li
270*9c5db199SXin Li        @return: Temp path string.
271*9c5db199SXin Li        """
272*9c5db199SXin Li        return '%s/%s/%s' % (_TEMP_DIR, self, source_path)
273*9c5db199SXin Li
274*9c5db199SXin Li
275*9c5db199SXin Li    def _save_files(self, paths):
276*9c5db199SXin Li        """
277*9c5db199SXin Li        Saves a set of files to a temporary location.
278*9c5db199SXin Li
279*9c5db199SXin Li        This can be used to save specific files so that a tool can
280*9c5db199SXin Li        save its current state before starting a test.
281*9c5db199SXin Li
282*9c5db199SXin Li        See _restore_files() for restoring the saved files.
283*9c5db199SXin Li
284*9c5db199SXin Li        @param paths: List of string paths to save.
285*9c5db199SXin Li        """
286*9c5db199SXin Li        for path in paths:
287*9c5db199SXin Li            temp_path = self._get_temp_path(path)
288*9c5db199SXin Li            self._host.run('mkdir -p "%s"' % os.path.dirname(temp_path))
289*9c5db199SXin Li            self._host.run('cp -r "%s" "%s"' % (path, temp_path),
290*9c5db199SXin Li                           ignore_status=True)
291*9c5db199SXin Li
292*9c5db199SXin Li
293*9c5db199SXin Li    def _restore_files(self, paths):
294*9c5db199SXin Li        """
295*9c5db199SXin Li        Restores saved files to their original location.
296*9c5db199SXin Li
297*9c5db199SXin Li        Used to restore files that have previously been saved by
298*9c5db199SXin Li        _save_files(), usually to return the device to its initial
299*9c5db199SXin Li        state.
300*9c5db199SXin Li
301*9c5db199SXin Li        This function does not erase the saved files, so it can
302*9c5db199SXin Li        be used multiple times if needed.
303*9c5db199SXin Li
304*9c5db199SXin Li        @param paths: List of string paths to restore.
305*9c5db199SXin Li        """
306*9c5db199SXin Li        for path in paths:
307*9c5db199SXin Li            self._host.run('rm -rf "%s"' % path)
308*9c5db199SXin Li            self._host.run('cp -r "%s" "%s"' % (self._get_temp_path(path),
309*9c5db199SXin Li                                                path),
310*9c5db199SXin Li                           ignore_status=True)
311*9c5db199SXin Li
312*9c5db199SXin Li
313*9c5db199SXin Li    def _unimplemented_function_error(self, function_name):
314*9c5db199SXin Li        """
315*9c5db199SXin Li        Throws an exception if a required tool function hasn't been
316*9c5db199SXin Li        implemented.
317*9c5db199SXin Li        """
318*9c5db199SXin Li        raise FeatureUnavailableError('%s has not implemented %s()' %
319*9c5db199SXin Li                                      (self, function_name))
320*9c5db199SXin Li
321*9c5db199SXin Li
322*9c5db199SXin Li    def __str__(self):
323*9c5db199SXin Li        """
324*9c5db199SXin Li        Tool name accessor for temporary files and logging.
325*9c5db199SXin Li
326*9c5db199SXin Li        Based on class rather than unique instance naming since all
327*9c5db199SXin Li        instances of the same tool have identical functionality.
328*9c5db199SXin Li        """
329*9c5db199SXin Li        return type(self).__name__
330*9c5db199SXin Li
331*9c5db199SXin Li
332*9c5db199SXin Liclass RootfsVerificationTool(DevTool):
333*9c5db199SXin Li    """
334*9c5db199SXin Li    Rootfs verification removal tool.
335*9c5db199SXin Li
336*9c5db199SXin Li    This tool is currently unable to transition from non-verified back
337*9c5db199SXin Li    to verified rootfs; it may potentially require re-flashing an OS.
338*9c5db199SXin Li    Since devices in the test lab run in verified mode, this tool is
339*9c5db199SXin Li    unsuitable for automated testing until this capability is
340*9c5db199SXin Li    implemented.
341*9c5db199SXin Li    """
342*9c5db199SXin Li
343*9c5db199SXin Li
344*9c5db199SXin Li    def is_enabled(self):
345*9c5db199SXin Li        return self._check_enabled(DEV_FEATURE_ROOTFS_VERIFICATION_REMOVED)
346*9c5db199SXin Li
347*9c5db199SXin Li
348*9c5db199SXin Li    def enable(self):
349*9c5db199SXin Li        _send_debugd_command(self._host, 'RemoveRootfsVerification')
350*9c5db199SXin Li        self._host.reboot()
351*9c5db199SXin Li
352*9c5db199SXin Li
353*9c5db199SXin Li    def disable(self):
354*9c5db199SXin Li        raise FeatureUnavailableError('Cannot re-enable rootfs verification')
355*9c5db199SXin Li
356*9c5db199SXin Li
357*9c5db199SXin Liclass BootFromUsbTool(DevTool):
358*9c5db199SXin Li    """USB boot configuration tool."""
359*9c5db199SXin Li
360*9c5db199SXin Li
361*9c5db199SXin Li    def is_enabled(self):
362*9c5db199SXin Li        return self._check_enabled(DEV_FEATURE_BOOT_FROM_USB_ENABLED)
363*9c5db199SXin Li
364*9c5db199SXin Li
365*9c5db199SXin Li    def enable(self):
366*9c5db199SXin Li        _send_debugd_command(self._host, 'EnableBootFromUsb')
367*9c5db199SXin Li
368*9c5db199SXin Li
369*9c5db199SXin Li    def disable(self):
370*9c5db199SXin Li        self._host.run('crossystem dev_boot_usb=0')
371*9c5db199SXin Li
372*9c5db199SXin Li
373*9c5db199SXin Li    def save_state(self):
374*9c5db199SXin Li        self.initial_state = self.is_enabled()
375*9c5db199SXin Li
376*9c5db199SXin Li
377*9c5db199SXin Li    def restore_state(self):
378*9c5db199SXin Li        if self.initial_state:
379*9c5db199SXin Li            self.enable()
380*9c5db199SXin Li        else:
381*9c5db199SXin Li            self.disable()
382*9c5db199SXin Li
383*9c5db199SXin Li
384*9c5db199SXin Liclass SshServerTool(DevTool):
385*9c5db199SXin Li    """
386*9c5db199SXin Li    SSH server tool.
387*9c5db199SXin Li
388*9c5db199SXin Li    SSH configuration has two components, the init file and the test
389*9c5db199SXin Li    keys. Since a system could potentially have none, just the init
390*9c5db199SXin Li    file, or all files, we want to be sure to restore just the files
391*9c5db199SXin Li    that existed before the test started.
392*9c5db199SXin Li    """
393*9c5db199SXin Li
394*9c5db199SXin Li
395*9c5db199SXin Li    PATHS = ('/etc/init/openssh-server.conf',
396*9c5db199SXin Li             '/root/.ssh/authorized_keys',
397*9c5db199SXin Li             '/root/.ssh/id_rsa',
398*9c5db199SXin Li             '/root/.ssh/id_rsa.pub')
399*9c5db199SXin Li
400*9c5db199SXin Li
401*9c5db199SXin Li    def is_enabled(self):
402*9c5db199SXin Li        return self._check_enabled(DEV_FEATURE_SSH_SERVER_CONFIGURED)
403*9c5db199SXin Li
404*9c5db199SXin Li
405*9c5db199SXin Li    def enable(self):
406*9c5db199SXin Li        _send_debugd_command(self._host, 'ConfigureSshServer')
407*9c5db199SXin Li
408*9c5db199SXin Li
409*9c5db199SXin Li    def disable(self):
410*9c5db199SXin Li        for path in self.PATHS:
411*9c5db199SXin Li            self._host.run('rm -f %s' % path)
412*9c5db199SXin Li
413*9c5db199SXin Li
414*9c5db199SXin Li    def save_state(self):
415*9c5db199SXin Li        self._save_files(self.PATHS)
416*9c5db199SXin Li
417*9c5db199SXin Li
418*9c5db199SXin Li    def restore_state(self):
419*9c5db199SXin Li        self._restore_files(self.PATHS)
420*9c5db199SXin Li
421*9c5db199SXin Li
422*9c5db199SXin Liclass SystemPasswordTool(DevTool):
423*9c5db199SXin Li    """
424*9c5db199SXin Li    System password configuration tool.
425*9c5db199SXin Li
426*9c5db199SXin Li    This tool just affects the system password (/etc/shadow). We could
427*9c5db199SXin Li    add a devmode password tool if we want to explicitly test that as
428*9c5db199SXin Li    well.
429*9c5db199SXin Li    """
430*9c5db199SXin Li
431*9c5db199SXin Li
432*9c5db199SXin Li    SYSTEM_PATHS = ('/etc/shadow',)
433*9c5db199SXin Li    DEV_PATHS = ('/mnt/stateful_partition/etc/devmode.passwd',)
434*9c5db199SXin Li
435*9c5db199SXin Li
436*9c5db199SXin Li    def is_enabled(self):
437*9c5db199SXin Li        return self._check_enabled(DEV_FEATURE_SYSTEM_ROOT_PASSWORD_SET)
438*9c5db199SXin Li
439*9c5db199SXin Li
440*9c5db199SXin Li    def enable(self):
441*9c5db199SXin Li        # Save the devmode.passwd file to avoid affecting it.
442*9c5db199SXin Li        self._save_files(self.DEV_PATHS)
443*9c5db199SXin Li        try:
444*9c5db199SXin Li            _send_debugd_command(self._host, 'SetUserPassword',
445*9c5db199SXin Li                                 ('string:root', 'string:test0000'))
446*9c5db199SXin Li        finally:
447*9c5db199SXin Li            # Restore devmode.passwd
448*9c5db199SXin Li            self._restore_files(self.DEV_PATHS)
449*9c5db199SXin Li
450*9c5db199SXin Li
451*9c5db199SXin Li    def disable(self):
452*9c5db199SXin Li        self._host.run('passwd -d root')
453*9c5db199SXin Li
454*9c5db199SXin Li
455*9c5db199SXin Li    def save_state(self):
456*9c5db199SXin Li        self._save_files(self.SYSTEM_PATHS)
457*9c5db199SXin Li
458*9c5db199SXin Li
459*9c5db199SXin Li    def restore_state(self):
460*9c5db199SXin Li        self._restore_files(self.SYSTEM_PATHS)
461