1b6d2993fb77f771a886c41ace0850773f5498bedbarfab@chromium.org# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 20499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan# Use of this source code is governed by a BSD-style license that can be 30499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan# found in the LICENSE file. 40499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5b6d2993fb77f771a886c41ace0850773f5498bedbarfab@chromium.orgimport dbus, gobject, logging, os, stat 6b6d2993fb77f771a886c41ace0850773f5498bedbarfab@chromium.orgfrom dbus.mainloop.glib import DBusGMainLoop 70499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 8b6d2993fb77f771a886c41ace0850773f5498bedbarfab@chromium.orgimport common 90499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanfrom autotest_lib.client.bin import utils 100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanfrom autotest_lib.client.common_lib import autotemp, error 11b6d2993fb77f771a886c41ace0850773f5498bedbarfab@chromium.orgfrom mainloop import ExceptionForward 12b6d2993fb77f771a886c41ace0850773f5498bedbarfab@chromium.orgfrom mainloop import GenericTesterMainLoop 130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan"""This module contains several helper classes for writing tests to verify the 160499e53e22a0fe29789e038976e8ede50af51ca3Ben ChanCrosDisks DBus interface. In particular, the CrosDisksTester class can be used 170499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanto derive functional tests that interact with the CrosDisks server over DBus. 180499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan""" 190499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass ExceptionSuppressor(object): 220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """A context manager class for suppressing certain types of exception. 230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan An instance of this class is expected to be used with the with statement 250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan and takes a set of exception classes at instantiation, which are types of 260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan exception to be suppressed (and logged) in the code block under the with 270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan statement. 280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Example: 300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with ExceptionSuppressor(OSError, IOError): 320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan # An exception, which is a sub-class of OSError or IOError, is 330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan # suppressed in the block code under the with statement. 340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, *args): 360499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.__suppressed_exc_types = (args) 370499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 380499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __enter__(self): 390499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self 400499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 410499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __exit__(self, exc_type, exc_value, traceback): 420499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if exc_type and issubclass(exc_type, self.__suppressed_exc_types): 430499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan try: 440499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.exception('Suppressed exception: %s(%s)', 450499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan exc_type, exc_value) 460499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan except Exception: 470499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan pass 480499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return True 490499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 500499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 510499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 520499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass DBusClient(object): 530499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ A base class of a DBus proxy client to test a DBus server. 540499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 550499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan This class is expected to be used along with a GLib main loop and provides 560499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan some convenient functions for testing the DBus API exposed by a DBus server. 570499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 580499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, main_loop, bus, bus_name, object_path): 590499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Initializes the instance. 600499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 610499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 620499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan main_loop: The GLib main loop. 630499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan bus: The bus where the DBus server is connected to. 640499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan bus_name: The bus name owned by the DBus server. 650499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan object_path: The object path of the DBus server. 660499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 670499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.__signal_content = {} 680499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.main_loop = main_loop 690499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.signal_timeout_in_seconds = 10 700499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Getting D-Bus proxy object on bus "%s" and path "%s"', 710499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan bus_name, object_path) 720499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.proxy_object = bus.get_object(bus_name, object_path) 730499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 740499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def clear_signal_content(self, signal_name): 750499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Clears the content of the signal. 760499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 770499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 780499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan signal_name: The name of the signal. 790499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 800499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if signal_name in self.__signal_content: 810499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.__signal_content[signal_name] = None 820499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 830499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def get_signal_content(self, signal_name): 840499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Gets the content of a signal. 850499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 860499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 870499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan signal_name: The name of the signal. 880499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 890499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 900499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The content of a signal or None if the signal is not being handled. 910499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 920499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self.__signal_content.get(signal_name) 930499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 940499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def handle_signal(self, interface, signal_name, argument_names=()): 950499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Registers a signal handler to handle a given signal. 960499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 970499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 980499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan interface: The DBus interface of the signal. 990499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan signal_name: The name of the signal. 1000499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan argument_names: A list of argument names that the signal contains. 1010499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 1020499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if signal_name in self.__signal_content: 1030499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return 1040499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1050499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.__signal_content[signal_name] = None 1060499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1070499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def signal_handler(*args): 1080499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.__signal_content[signal_name] = dict(zip(argument_names, args)) 1090499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Handling D-Bus signal "%s(%s)" on interface "%s"', 1110499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan signal_name, ', '.join(argument_names), interface) 1120499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.proxy_object.connect_to_signal(signal_name, signal_handler, 1130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan interface) 1140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def wait_for_signal(self, signal_name): 11681904f12dbffa5c415db9ad3026eac59a516725fBen Chan """Waits for the reception of a signal. 11781904f12dbffa5c415db9ad3026eac59a516725fBen Chan 11881904f12dbffa5c415db9ad3026eac59a516725fBen Chan Args: 11981904f12dbffa5c415db9ad3026eac59a516725fBen Chan signal_name: The name of the signal to wait for. 1200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 1220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The content of the signal. 1230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 1240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if signal_name not in self.__signal_content: 1250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return None 1260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def check_signal_content(): 1280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan context = self.main_loop.get_context() 1290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan while context.iteration(False): 1300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan pass 1310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self.__signal_content[signal_name] is not None 1320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Waiting for D-Bus signal "%s"', signal_name) 1340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.poll_for_condition(condition=check_signal_content, 1350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan desc='%s signal' % signal_name, 1360499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan timeout=self.signal_timeout_in_seconds) 1370499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan content = self.__signal_content[signal_name] 1380499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Received D-Bus signal "%s(%s)"', signal_name, content) 1390499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.__signal_content[signal_name] = None 1400499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return content 1410499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 14281904f12dbffa5c415db9ad3026eac59a516725fBen Chan def expect_signal(self, signal_name, expected_content): 14381904f12dbffa5c415db9ad3026eac59a516725fBen Chan """Waits the the reception of a signal and verifies its content. 14481904f12dbffa5c415db9ad3026eac59a516725fBen Chan 14581904f12dbffa5c415db9ad3026eac59a516725fBen Chan Args: 14681904f12dbffa5c415db9ad3026eac59a516725fBen Chan signal_name: The name of the signal to wait for. 14781904f12dbffa5c415db9ad3026eac59a516725fBen Chan expected_content: The expected content of the signal, which can be 14881904f12dbffa5c415db9ad3026eac59a516725fBen Chan partially specified. Only specified fields are 14981904f12dbffa5c415db9ad3026eac59a516725fBen Chan compared between the actual and expected content. 15081904f12dbffa5c415db9ad3026eac59a516725fBen Chan 15181904f12dbffa5c415db9ad3026eac59a516725fBen Chan Returns: 15281904f12dbffa5c415db9ad3026eac59a516725fBen Chan The actual content of the signal. 15381904f12dbffa5c415db9ad3026eac59a516725fBen Chan 15481904f12dbffa5c415db9ad3026eac59a516725fBen Chan Raises: 15581904f12dbffa5c415db9ad3026eac59a516725fBen Chan error.TestFail: A test failure when there is a mismatch between the 15681904f12dbffa5c415db9ad3026eac59a516725fBen Chan actual and expected content of the signal. 15781904f12dbffa5c415db9ad3026eac59a516725fBen Chan """ 15881904f12dbffa5c415db9ad3026eac59a516725fBen Chan actual_content = self.wait_for_signal(signal_name) 15981904f12dbffa5c415db9ad3026eac59a516725fBen Chan logging.debug("%s signal: expected=%s actual=%s", 16081904f12dbffa5c415db9ad3026eac59a516725fBen Chan signal_name, expected_content, actual_content) 16181904f12dbffa5c415db9ad3026eac59a516725fBen Chan for argument, expected_value in expected_content.iteritems(): 16281904f12dbffa5c415db9ad3026eac59a516725fBen Chan if argument not in actual_content: 16381904f12dbffa5c415db9ad3026eac59a516725fBen Chan raise error.TestFail( 16481904f12dbffa5c415db9ad3026eac59a516725fBen Chan ('%s signal missing "%s": expected=%s, actual=%s') % 16581904f12dbffa5c415db9ad3026eac59a516725fBen Chan (signal_name, argument, expected_content, actual_content)) 16681904f12dbffa5c415db9ad3026eac59a516725fBen Chan 16781904f12dbffa5c415db9ad3026eac59a516725fBen Chan if actual_content[argument] != expected_value: 16881904f12dbffa5c415db9ad3026eac59a516725fBen Chan raise error.TestFail( 16981904f12dbffa5c415db9ad3026eac59a516725fBen Chan ('%s signal not matched on "%s": expected=%s, actual=%s') % 17081904f12dbffa5c415db9ad3026eac59a516725fBen Chan (signal_name, argument, expected_content, actual_content)) 17181904f12dbffa5c415db9ad3026eac59a516725fBen Chan return actual_content 17281904f12dbffa5c415db9ad3026eac59a516725fBen Chan 1730499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1740499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass CrosDisksClient(DBusClient): 1750499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """A DBus proxy client for testing the CrosDisks DBus server. 1760499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 1770499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1780499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan CROS_DISKS_BUS_NAME = 'org.chromium.CrosDisks' 1790499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan CROS_DISKS_INTERFACE = 'org.chromium.CrosDisks' 1800499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan CROS_DISKS_OBJECT_PATH = '/org/chromium/CrosDisks' 1810499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan DBUS_PROPERTIES_INTERFACE = 'org.freedesktop.DBus.Properties' 18281904f12dbffa5c415db9ad3026eac59a516725fBen Chan FORMAT_COMPLETED_SIGNAL = 'FormatCompleted' 18381904f12dbffa5c415db9ad3026eac59a516725fBen Chan FORMAT_COMPLETED_SIGNAL_ARGUMENTS = ( 18481904f12dbffa5c415db9ad3026eac59a516725fBen Chan 'status', 'path' 18581904f12dbffa5c415db9ad3026eac59a516725fBen Chan ) 1860499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan MOUNT_COMPLETED_SIGNAL = 'MountCompleted' 1870499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan MOUNT_COMPLETED_SIGNAL_ARGUMENTS = ( 1880499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 'status', 'source_path', 'source_type', 'mount_path' 1890499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan ) 1900499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1910499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, main_loop, bus): 1920499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Initializes the instance. 1930499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 1940499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 1950499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan main_loop: The GLib main loop. 1960499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan bus: The bus where the DBus server is connected to. 1970499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 1980499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan super(CrosDisksClient, self).__init__(main_loop, bus, 1990499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.CROS_DISKS_BUS_NAME, 2000499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.CROS_DISKS_OBJECT_PATH) 2010499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.interface = dbus.Interface(self.proxy_object, 2020499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.CROS_DISKS_INTERFACE) 2030499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.properties = dbus.Interface(self.proxy_object, 2040499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.DBUS_PROPERTIES_INTERFACE) 2050499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.handle_signal(self.CROS_DISKS_INTERFACE, 20681904f12dbffa5c415db9ad3026eac59a516725fBen Chan self.FORMAT_COMPLETED_SIGNAL, 20781904f12dbffa5c415db9ad3026eac59a516725fBen Chan self.FORMAT_COMPLETED_SIGNAL_ARGUMENTS) 20881904f12dbffa5c415db9ad3026eac59a516725fBen Chan self.handle_signal(self.CROS_DISKS_INTERFACE, 2090499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.MOUNT_COMPLETED_SIGNAL, 2100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.MOUNT_COMPLETED_SIGNAL_ARGUMENTS) 2110499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2120499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def is_alive(self): 2130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Invokes the CrosDisks IsAlive method. 2140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 2160499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan True if the CrosDisks server is alive or False otherwise. 2170499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 2180499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self.interface.IsAlive() 2190499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def enumerate_auto_mountable_devices(self): 2210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Invokes the CrosDisks EnumerateAutoMountableDevices method. 2220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 2240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan A list of sysfs paths of devices that are auto-mountable by 2250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan CrosDisks. 2260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 2270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self.interface.EnumerateAutoMountableDevices() 2280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def enumerate_devices(self): 2300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Invokes the CrosDisks EnumerateMountableDevices method. 2310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 2330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan A list of sysfs paths of devices that are recognized by 2340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan CrosDisks. 2350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 2360499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self.interface.EnumerateDevices() 2370499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2380499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def get_device_properties(self, path): 2390499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Invokes the CrosDisks GetDeviceProperties method. 2400499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2410499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 2420499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path: The device path. 2430499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2440499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 2450499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The properties of the device in a dictionary. 2460499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 2470499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self.interface.GetDeviceProperties(path) 2480499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 24981904f12dbffa5c415db9ad3026eac59a516725fBen Chan def format(self, path, filesystem_type=None, options=None): 25081904f12dbffa5c415db9ad3026eac59a516725fBen Chan """Invokes the CrosDisks Format method. 25181904f12dbffa5c415db9ad3026eac59a516725fBen Chan 25281904f12dbffa5c415db9ad3026eac59a516725fBen Chan Args: 25381904f12dbffa5c415db9ad3026eac59a516725fBen Chan path: The device path to format. 25481904f12dbffa5c415db9ad3026eac59a516725fBen Chan filesystem_type: The filesystem type used for formatting the device. 25581904f12dbffa5c415db9ad3026eac59a516725fBen Chan options: A list of options used for formatting the device. 25681904f12dbffa5c415db9ad3026eac59a516725fBen Chan """ 25781904f12dbffa5c415db9ad3026eac59a516725fBen Chan if filesystem_type is None: 25881904f12dbffa5c415db9ad3026eac59a516725fBen Chan filesystem_type = '' 25981904f12dbffa5c415db9ad3026eac59a516725fBen Chan if options is None: 26081904f12dbffa5c415db9ad3026eac59a516725fBen Chan options = [] 26181904f12dbffa5c415db9ad3026eac59a516725fBen Chan self.clear_signal_content(self.FORMAT_COMPLETED_SIGNAL) 26281904f12dbffa5c415db9ad3026eac59a516725fBen Chan self.interface.Format(path, filesystem_type, options) 26381904f12dbffa5c415db9ad3026eac59a516725fBen Chan 26481904f12dbffa5c415db9ad3026eac59a516725fBen Chan def wait_for_format_completion(self): 26581904f12dbffa5c415db9ad3026eac59a516725fBen Chan """Waits for the CrosDisks FormatCompleted signal. 26681904f12dbffa5c415db9ad3026eac59a516725fBen Chan 26781904f12dbffa5c415db9ad3026eac59a516725fBen Chan Returns: 26881904f12dbffa5c415db9ad3026eac59a516725fBen Chan The content of the FormatCompleted signal. 26981904f12dbffa5c415db9ad3026eac59a516725fBen Chan """ 27081904f12dbffa5c415db9ad3026eac59a516725fBen Chan return self.wait_for_signal(self.FORMAT_COMPLETED_SIGNAL) 27181904f12dbffa5c415db9ad3026eac59a516725fBen Chan 27281904f12dbffa5c415db9ad3026eac59a516725fBen Chan def expect_format_completion(self, expected_content): 27381904f12dbffa5c415db9ad3026eac59a516725fBen Chan """Waits and verifies for the CrosDisks FormatCompleted signal. 27481904f12dbffa5c415db9ad3026eac59a516725fBen Chan 27581904f12dbffa5c415db9ad3026eac59a516725fBen Chan Args: 27681904f12dbffa5c415db9ad3026eac59a516725fBen Chan expected_content: The expected content of the FormatCompleted 27781904f12dbffa5c415db9ad3026eac59a516725fBen Chan signal, which can be partially specified. 27881904f12dbffa5c415db9ad3026eac59a516725fBen Chan Only specified fields are compared between the 27981904f12dbffa5c415db9ad3026eac59a516725fBen Chan actual and expected content. 28081904f12dbffa5c415db9ad3026eac59a516725fBen Chan 28181904f12dbffa5c415db9ad3026eac59a516725fBen Chan Returns: 28281904f12dbffa5c415db9ad3026eac59a516725fBen Chan The actual content of the FormatCompleted signal. 28381904f12dbffa5c415db9ad3026eac59a516725fBen Chan 28481904f12dbffa5c415db9ad3026eac59a516725fBen Chan Raises: 28581904f12dbffa5c415db9ad3026eac59a516725fBen Chan error.TestFail: A test failure when there is a mismatch between the 28681904f12dbffa5c415db9ad3026eac59a516725fBen Chan actual and expected content of the FormatCompleted 28781904f12dbffa5c415db9ad3026eac59a516725fBen Chan signal. 28881904f12dbffa5c415db9ad3026eac59a516725fBen Chan """ 28981904f12dbffa5c415db9ad3026eac59a516725fBen Chan return self.expect_signal(self.FORMAT_COMPLETED_SIGNAL, 29081904f12dbffa5c415db9ad3026eac59a516725fBen Chan expected_content) 29181904f12dbffa5c415db9ad3026eac59a516725fBen Chan 2920499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def mount(self, path, filesystem_type=None, options=None): 2930499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Invokes the CrosDisks Mount method. 2940499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 2950499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 2960499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path: The device path to mount. 2970499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan filesystem_type: The filesystem type used for mounting the device. 2980499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options: A list of options used for mounting the device. 2990499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3000499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if filesystem_type is None: 3010499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan filesystem_type = '' 3020499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if options is None: 3030499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options = [] 3040499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.clear_signal_content(self.MOUNT_COMPLETED_SIGNAL) 3050499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.interface.Mount(path, filesystem_type, options) 3060499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3070499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def unmount(self, path, options=None): 3080499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Invokes the CrosDisks Unmount method. 3090499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 3110499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path: The device or mount path to unmount. 3120499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options: A list of options used for unmounting the path. 3130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if options is None: 3150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options = [] 3160499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.interface.Unmount(path, options) 3170499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3180499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def wait_for_mount_completion(self): 3190499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Waits for the CrosDisks MountCompleted signal. 3200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 3220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The content of the MountCompleted signal. 3230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self.wait_for_signal(self.MOUNT_COMPLETED_SIGNAL) 3250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def expect_mount_completion(self, expected_content): 3270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Waits and verifies for the CrosDisks MountCompleted signal. 3280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 3300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan expected_content: The expected content of the MountCompleted 3310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan signal, which can be partially specified. 3320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Only specified fields are compared between the 3330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan actual and expected content. 3340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 3360499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The actual content of the MountCompleted signal. 3370499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3380499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Raises: 3390499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan error.TestFail: A test failure when there is a mismatch between the 3400499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan actual and expected content of the MountCompleted 3410499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan signal. 3420499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 34381904f12dbffa5c415db9ad3026eac59a516725fBen Chan return self.expect_signal(self.MOUNT_COMPLETED_SIGNAL, 34481904f12dbffa5c415db9ad3026eac59a516725fBen Chan expected_content) 3450499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3460499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3470499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass CrosDisksTester(GenericTesterMainLoop): 3480499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """A base tester class for testing the CrosDisks server. 3490499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3500499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan A derived class should override the get_tests method to return a list of 3510499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan test methods. The perform_one_test method invokes each test method in the 3520499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan list to verify some functionalities of CrosDisks server. 3530499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3540499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, test): 3550499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan bus_loop = DBusGMainLoop(set_as_default=True) 3560499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan bus = dbus.SystemBus(mainloop=bus_loop) 3570499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.main_loop = gobject.MainLoop() 3580499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan super(CrosDisksTester, self).__init__(test, self.main_loop) 3590499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.cros_disks = CrosDisksClient(self.main_loop, bus) 3600499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3610499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def get_tests(self): 3620499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Returns a list of test methods to be invoked by perform_one_test. 3630499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3640499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan A derived class should override this method. 3650499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3660499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 3670499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan A list of test methods. 3680499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3690499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return [] 3700499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3710499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan @ExceptionForward 3720499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def perform_one_test(self): 3730499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Exercises each test method in the list returned by get_tests. 3740499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3750499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan tests = self.get_tests() 3760499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.remaining_requirements = set([test.func_name for test in tests]) 3770499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan for test in tests: 3780499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan test() 3790499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.requirement_completed(test.func_name) 3800499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3810499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3820499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass FilesystemTestObject(object): 3830499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """A base class to represent a filesystem test object. 3840499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3850499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan A filesystem test object can be a file, directory or symbolic link. 3860499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan A derived class should override the _create and _verify method to implement 3870499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan how the test object should be created and verified, respectively, on a 3880499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan filesystem. 3890499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3900499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, path, content, mode): 3910499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Initializes the instance. 3920499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 3930499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 3940499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path: The relative path of the test object. 3950499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan content: The content of the test object. 3960499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan mode: The file permissions given to the test object. 3970499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 3980499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._path = path 3990499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._content = content 4000499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mode = mode 4010499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4020499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def create(self, base_dir): 4030499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Creates the test object in a base directory. 4040499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4050499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 4060499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan base_dir: The base directory where the test object is created. 4070499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4080499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 4090499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan True if the test object is created successfully or False otherwise. 4100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 4110499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not self._create(base_dir): 4120499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Failed to create filesystem test object at "%s"', 4130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan os.path.join(base_dir, self._path)) 4140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return True 4160499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4170499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def verify(self, base_dir): 4180499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Verifies the test object in a base directory. 4190499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 4210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan base_dir: The base directory where the test object is expected to be 4220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan found. 4230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 4250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan True if the test object is found in the base directory and matches 4260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan the expected content, or False otherwise. 4270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 4280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not self._verify(base_dir): 4290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Failed to verify filesystem test object at "%s"', 4300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan os.path.join(base_dir, self._path)) 4310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return True 4330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _create(self, base_dir): 4350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4360499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4370499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _verify(self, base_dir): 4380499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4390499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4400499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4410499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass FilesystemTestDirectory(FilesystemTestObject): 4420499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """A filesystem test object that represents a directory.""" 4430499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4440499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, path, content, mode=stat.S_IRWXU|stat.S_IRGRP| \ 4450499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH): 4460499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan super(FilesystemTestDirectory, self).__init__(path, content, mode) 4470499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4480499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _create(self, base_dir): 4490499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path = os.path.join(base_dir, self._path) if self._path else base_dir 4500499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4510499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if self._path: 4520499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with ExceptionSuppressor(OSError): 4530499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan os.makedirs(path) 4540499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan os.chmod(path, self._mode) 4550499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4560499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not os.path.isdir(path): 4570499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4580499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4590499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan for content in self._content: 4600499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not content.create(path): 4610499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4620499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return True 4630499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4640499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _verify(self, base_dir): 4650499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path = os.path.join(base_dir, self._path) if self._path else base_dir 4660499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not os.path.isdir(path): 4670499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4680499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4690499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan for content in self._content: 4700499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not content.verify(path): 4710499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4720499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return True 4730499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4740499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4750499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass FilesystemTestFile(FilesystemTestObject): 4760499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """A filesystem test object that represents a file.""" 4770499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4780499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, path, content, mode=stat.S_IRUSR|stat.S_IWUSR| \ 4790499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan stat.S_IRGRP|stat.S_IROTH): 4800499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan super(FilesystemTestFile, self).__init__(path, content, mode) 4810499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4820499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _create(self, base_dir): 4830499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path = os.path.join(base_dir, self._path) 4840499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with ExceptionSuppressor(IOError): 4850499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with open(path, 'wb+') as f: 4860499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan f.write(self._content) 487f6a74c53fc0e802d3a7b9882a2d04de132ebaccaBen Chan with ExceptionSuppressor(OSError): 488f6a74c53fc0e802d3a7b9882a2d04de132ebaccaBen Chan os.chmod(path, self._mode) 4890499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return True 4900499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4910499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4920499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _verify(self, base_dir): 4930499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path = os.path.join(base_dir, self._path) 4940499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with ExceptionSuppressor(IOError): 4950499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with open(path, 'rb') as f: 4960499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return f.read() == self._content 4970499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 4980499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 4990499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5000499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass DefaultFilesystemTestContent(FilesystemTestDirectory): 5010499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self): 5020499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan super(DefaultFilesystemTestContent, self).__init__('', [ 5030499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan FilesystemTestFile('file1', '0123456789'), 5040499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan FilesystemTestDirectory('dir1', [ 5050499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan FilesystemTestFile('file1', ''), 5060499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan FilesystemTestFile('file2', 'abcdefg'), 5070499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan FilesystemTestDirectory('dir2', [ 5080499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan FilesystemTestFile('file3', 'abcdefg'), 5090499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan ]), 5100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan ]), 5110499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan ], stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH) 5120499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chanclass VirtualFilesystemImage(object): 5150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __init__(self, block_size, block_count, filesystem_type, 5160499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan *args, **kwargs): 5170499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Initializes the instance. 5180499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5190499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 5200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan block_size: The number of bytes of each block in the image. 5210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan block_count: The number of blocks in the image. 5220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan filesystem_type: The filesystem type to be given to the mkfs 5230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan program for formatting the image. 5240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Keyword Args: 5260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan mount_filesystem_type: The filesystem type to be given to the 5270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan mount program for mounting the image. 5280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan mkfs_options: A list of options to be given to the mkfs program. 5290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 5300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._block_size = block_size 5310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._block_count = block_count 5320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._filesystem_type = filesystem_type 5330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mount_filesystem_type = kwargs.get('mount_filesystem_type') 5340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if self._mount_filesystem_type is None: 5350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mount_filesystem_type = filesystem_type 5360499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mkfs_options = kwargs.get('mkfs_options') 5370499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if self._mkfs_options is None: 5380499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mkfs_options = [] 5390499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file = None 5400499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._loop_device = None 5410499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mount_dir = None 5420499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5430499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __del__(self): 5440499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with ExceptionSuppressor(Exception): 5450499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.clean() 5460499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5470499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __enter__(self): 5480499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.create() 5490499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self 5500499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5510499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def __exit__(self, exc_type, exc_value, traceback): 5520499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.clean() 5530499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return False 5540499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5550499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _remove_temp_path(self, temp_path): 5560499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Removes a temporary file or directory created using autotemp.""" 5570499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if temp_path: 5580499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan with ExceptionSuppressor(Exception): 5590499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan path = temp_path.name 5600499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan temp_path.clean() 5610499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Removed "%s"', path) 5620499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5630499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _remove_image_file(self): 5640499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Removes the image file if one has been created.""" 5650499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._remove_temp_path(self._image_file) 5660499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file = None 5670499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5680499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def _remove_mount_dir(self): 5690499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Removes the mount directory if one has been created.""" 5700499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._remove_temp_path(self._mount_dir) 5710499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mount_dir = None 5720499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5730499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan @property 5740499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def image_file(self): 5750499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Gets the path of the image file. 5760499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5770499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 5780499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The path of the image file or None if no image file has been 5790499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan created. 5800499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 5810499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self._image_file.name if self._image_file else None 5820499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5830499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan @property 5840499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def loop_device(self): 5850499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Gets the loop device where the image file is attached to. 5860499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5870499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 5880499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The path of the loop device where the image file is attached to or 5890499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan None if no loop device is attaching the image file. 5900499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 5910499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self._loop_device 5920499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5930499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan @property 5940499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def mount_dir(self): 5950499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Gets the directory where the image file is mounted to. 5960499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 5970499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 5980499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The directory where the image file is mounted to or None if no 5990499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan mount directory has been created. 6000499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 6010499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self._mount_dir.name if self._mount_dir else None 6020499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6030499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def create(self): 6040499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Creates a zero-filled image file with the specified size. 6050499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6060499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The created image file is temporary and removed when clean() 6070499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan is called. 6080499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 6090499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.clean() 6100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file = autotemp.tempfile(unique_id='fsImage') 6110499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan try: 6120499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Creating zero-filled image file at "%s"', 6130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file.name) 6140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.run('dd if=/dev/zero of=%s bs=%s count=%s' % 6150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan (self._image_file.name, self._block_size, 6160499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._block_count)) 6170499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan except error.CmdError as exc: 6180499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._remove_image_file() 6190499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan message = 'Failed to create filesystem image: %s' % exc 6200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan raise RuntimeError(message) 6210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def clean(self): 6230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Removes the image file if one has been created. 6240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Before removal, the image file is detached from the loop device that 6260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan it is attached to. 6270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 6280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.detach_from_loop_device() 6290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._remove_image_file() 6300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def attach_to_loop_device(self): 6320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Attaches the created image file to a loop device. 6330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Creates the image file, if one has not been created, by calling 6350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan create(). 6360499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6370499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Returns: 6380499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan The path of the loop device where the image file is attached to. 6390499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 6400499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if self._loop_device: 6410499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self._loop_device 6420499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6430499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not self._image_file: 6440499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.create() 6450499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6460499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Attaching image file "%s" to loop device', 6470499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file.name) 6480499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.run('losetup -f %s' % self._image_file.name) 6490499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan output = utils.system_output('losetup -j %s' % self._image_file.name) 6500499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan # output should look like: "/dev/loop0: [000d]:6329 (/tmp/test.img)" 6510499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._loop_device = output.split(':')[0] 6520499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Attached image file "%s" to loop device "%s"', 6530499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file.name, self._loop_device) 6540499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self._loop_device 6550499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6560499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def detach_from_loop_device(self): 6570499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Detaches the image file from the loop device.""" 6580499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not self._loop_device: 6590499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return 6600499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6610499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.unmount() 6620499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6630499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Cleaning up remaining mount points of loop device "%s"', 6640499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._loop_device) 6650499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.run('umount -f %s' % self._loop_device, ignore_status=True) 6660499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6670499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Detaching image file "%s" from loop device "%s"', 6680499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file.name, self._loop_device) 6690499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.run('losetup -d %s' % self._loop_device) 6700499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._loop_device = None 6710499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6720499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def format(self): 6730499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Formats the image file as the specified filesystem.""" 6740499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.attach_to_loop_device() 6750499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan try: 6760499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Formatting image file at "%s" as "%s" filesystem', 6770499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file.name, self._filesystem_type) 6780499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.run('yes | mkfs -t %s %s %s' % 6790499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan (self._filesystem_type, ' '.join(self._mkfs_options), 6800499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._loop_device)) 6810499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('blkid: %s', utils.system_output( 6820499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 'blkid -c /dev/null %s' % self._loop_device, 6830499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan ignore_status=True)) 6840499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan except error.CmdError as exc: 6850499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan message = 'Failed to format filesystem image: %s' % exc 6860499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan raise RuntimeError(message) 6870499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6880499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def mount(self, options=None): 6890499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Mounts the image file to a directory. 6900499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6910499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan Args: 6920499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options: An optional list of mount options. 6930499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """ 6940499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if self._mount_dir: 6950499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self._mount_dir.name 6960499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 6970499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if options is None: 6980499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options = [] 6990499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 7000499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options_arg = ','.join(options) 7010499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if options_arg: 7020499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan options_arg = '-o ' + options_arg 7030499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 7040499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self.attach_to_loop_device() 7050499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mount_dir = autotemp.tempdir(unique_id='fsImage') 7060499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan try: 7070499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Mounting image file "%s" (%s) to directory "%s"', 7080499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file.name, self._loop_device, 7090499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mount_dir.name) 7100499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.run('mount -t %s %s %s %s' % 7110499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan (self._mount_filesystem_type, options_arg, 7120499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._loop_device, self._mount_dir.name)) 7130499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan except error.CmdError as exc: 7140499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._remove_mount_dir() 7150499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan message = ('Failed to mount virtual filesystem image "%s": %s' % 7160499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan (self._image_file.name, exc)) 7170499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan raise RuntimeError(message) 7180499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return self._mount_dir.name 7190499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 7200499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan def unmount(self): 7210499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan """Unmounts the image file from the mounted directory.""" 7220499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan if not self._mount_dir: 7230499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan return 7240499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan 7250499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan try: 7260499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan logging.debug('Unmounting image file "%s" (%s) from directory "%s"', 7270499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._image_file.name, self._loop_device, 7280499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._mount_dir.name) 7290499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan utils.run('umount %s' % self._mount_dir.name) 7300499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan except error.CmdError as exc: 7310499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan message = ('Failed to unmount virtual filesystem image "%s": %s' % 7320499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan (self._image_file.name, exc)) 7330499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan raise RuntimeError(message) 7340499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan finally: 7350499e53e22a0fe29789e038976e8ede50af51ca3Ben Chan self._remove_mount_dir() 736