1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li""" 3*9c5db199SXin LiAPIs to write tests and control files that handle partition creation, deletion 4*9c5db199SXin Liand formatting. 5*9c5db199SXin Li 6*9c5db199SXin Li@copyright: Google 2006-2008 7*9c5db199SXin Li@author: Martin Bligh ([email protected]) 8*9c5db199SXin Li""" 9*9c5db199SXin Li 10*9c5db199SXin Li# pylint: disable=missing-docstring 11*9c5db199SXin Li 12*9c5db199SXin Liimport os, re, string, sys, fcntl, logging 13*9c5db199SXin Lifrom autotest_lib.client.bin import os_dep, utils 14*9c5db199SXin Lifrom autotest_lib.client.common_lib import error 15*9c5db199SXin Li 16*9c5db199SXin Li 17*9c5db199SXin Liclass FsOptions(object): 18*9c5db199SXin Li """ 19*9c5db199SXin Li A class encapsulating a filesystem test's parameters. 20*9c5db199SXin Li """ 21*9c5db199SXin Li # NOTE(gps): This class could grow or be merged with something else in the 22*9c5db199SXin Li # future that actually uses the encapsulated data (say to run mkfs) rather 23*9c5db199SXin Li # than just being a container. 24*9c5db199SXin Li 25*9c5db199SXin Li __slots__ = ('fstype', 'mkfs_flags', 'mount_options', 'fs_tag') 26*9c5db199SXin Li 27*9c5db199SXin Li def __init__(self, fstype, fs_tag, mkfs_flags=None, mount_options=None): 28*9c5db199SXin Li """ 29*9c5db199SXin Li Fill in our properties. 30*9c5db199SXin Li 31*9c5db199SXin Li @param fstype: The filesystem type ('ext2', 'ext4', 'xfs', etc.) 32*9c5db199SXin Li @param fs_tag: A short name for this filesystem test to use 33*9c5db199SXin Li in the results. 34*9c5db199SXin Li @param mkfs_flags: Optional. Additional command line options to mkfs. 35*9c5db199SXin Li @param mount_options: Optional. The options to pass to mount -o. 36*9c5db199SXin Li """ 37*9c5db199SXin Li 38*9c5db199SXin Li if not fstype or not fs_tag: 39*9c5db199SXin Li raise ValueError('A filesystem and fs_tag are required.') 40*9c5db199SXin Li self.fstype = fstype 41*9c5db199SXin Li self.fs_tag = fs_tag 42*9c5db199SXin Li self.mkfs_flags = mkfs_flags or "" 43*9c5db199SXin Li self.mount_options = mount_options or "" 44*9c5db199SXin Li 45*9c5db199SXin Li 46*9c5db199SXin Li def __str__(self): 47*9c5db199SXin Li val = ('FsOptions(fstype=%r, mkfs_flags=%r, ' 48*9c5db199SXin Li 'mount_options=%r, fs_tag=%r)' % 49*9c5db199SXin Li (self.fstype, self.mkfs_flags, 50*9c5db199SXin Li self.mount_options, self.fs_tag)) 51*9c5db199SXin Li return val 52*9c5db199SXin Li 53*9c5db199SXin Li 54*9c5db199SXin Lidef partname_to_device(part): 55*9c5db199SXin Li """ Converts a partition name to its associated device """ 56*9c5db199SXin Li return os.path.join(os.sep, 'dev', part) 57*9c5db199SXin Li 58*9c5db199SXin Li 59*9c5db199SXin Lidef list_mount_devices(): 60*9c5db199SXin Li devices = [] 61*9c5db199SXin Li # list mounted filesystems 62*9c5db199SXin Li for line in utils.system_output('mount').splitlines(): 63*9c5db199SXin Li devices.append(line.split()[0]) 64*9c5db199SXin Li # list mounted swap devices 65*9c5db199SXin Li for line in utils.system_output('swapon -s').splitlines(): 66*9c5db199SXin Li if line.startswith('/'): # skip header line 67*9c5db199SXin Li devices.append(line.split()[0]) 68*9c5db199SXin Li return devices 69*9c5db199SXin Li 70*9c5db199SXin Li 71*9c5db199SXin Lidef list_mount_points(): 72*9c5db199SXin Li mountpoints = [] 73*9c5db199SXin Li for line in utils.system_output('mount').splitlines(): 74*9c5db199SXin Li mountpoints.append(line.split()[2]) 75*9c5db199SXin Li return mountpoints 76*9c5db199SXin Li 77*9c5db199SXin Li 78*9c5db199SXin Lidef get_iosched_path(device_name, component): 79*9c5db199SXin Li return '/sys/block/%s/queue/%s' % (device_name, component) 80*9c5db199SXin Li 81*9c5db199SXin Li 82*9c5db199SXin Lidef wipe_filesystem(job, mountpoint): 83*9c5db199SXin Li wipe_cmd = 'rm -rf %s/*' % mountpoint 84*9c5db199SXin Li try: 85*9c5db199SXin Li utils.system(wipe_cmd) 86*9c5db199SXin Li except: 87*9c5db199SXin Li job.record('FAIL', None, wipe_cmd, error.format_error()) 88*9c5db199SXin Li raise 89*9c5db199SXin Li else: 90*9c5db199SXin Li job.record('GOOD', None, wipe_cmd) 91*9c5db199SXin Li 92*9c5db199SXin Li 93*9c5db199SXin Lidef is_linux_fs_type(device): 94*9c5db199SXin Li """ 95*9c5db199SXin Li Checks if specified partition is type 83 96*9c5db199SXin Li 97*9c5db199SXin Li @param device: the device, e.g. /dev/sda3 98*9c5db199SXin Li 99*9c5db199SXin Li @return: False if the supplied partition name is not type 83 linux, True 100*9c5db199SXin Li otherwise 101*9c5db199SXin Li """ 102*9c5db199SXin Li disk_device = device.rstrip('0123456789') 103*9c5db199SXin Li 104*9c5db199SXin Li # Parse fdisk output to get partition info. Ugly but it works. 105*9c5db199SXin Li fdisk_fd = os.popen("/sbin/fdisk -l -u '%s'" % disk_device) 106*9c5db199SXin Li fdisk_lines = fdisk_fd.readlines() 107*9c5db199SXin Li fdisk_fd.close() 108*9c5db199SXin Li for line in fdisk_lines: 109*9c5db199SXin Li if not line.startswith(device): 110*9c5db199SXin Li continue 111*9c5db199SXin Li info_tuple = line.split() 112*9c5db199SXin Li # The Id will be in one of two fields depending on if the boot flag 113*9c5db199SXin Li # was set. Caveat: this assumes no boot partition will be 83 blocks. 114*9c5db199SXin Li for fsinfo in info_tuple[4:6]: 115*9c5db199SXin Li if fsinfo == '83': # hex 83 is the linux fs partition type 116*9c5db199SXin Li return True 117*9c5db199SXin Li return False 118*9c5db199SXin Li 119*9c5db199SXin Li 120*9c5db199SXin Lidef get_partition_list(job, min_blocks=0, filter_func=None, exclude_swap=True, 121*9c5db199SXin Li open_func=open): 122*9c5db199SXin Li """ 123*9c5db199SXin Li Get a list of partition objects for all disk partitions on the system. 124*9c5db199SXin Li 125*9c5db199SXin Li Loopback devices and unnumbered (whole disk) devices are always excluded. 126*9c5db199SXin Li 127*9c5db199SXin Li @param job: The job instance to pass to the partition object 128*9c5db199SXin Li constructor. 129*9c5db199SXin Li @param min_blocks: The minimum number of blocks for a partition to 130*9c5db199SXin Li be considered. 131*9c5db199SXin Li @param filter_func: A callable that returns True if a partition is 132*9c5db199SXin Li desired. It will be passed one parameter: 133*9c5db199SXin Li The partition name (hdc3, etc.). 134*9c5db199SXin Li Some useful filter functions are already defined in this module. 135*9c5db199SXin Li @param exclude_swap: If True any partition actively in use as a swap 136*9c5db199SXin Li device will be excluded. 137*9c5db199SXin Li @param __open: Reserved for unit testing. 138*9c5db199SXin Li 139*9c5db199SXin Li @return: A list of L{partition} objects. 140*9c5db199SXin Li """ 141*9c5db199SXin Li active_swap_devices = set() 142*9c5db199SXin Li if exclude_swap: 143*9c5db199SXin Li for swapline in open_func('/proc/swaps'): 144*9c5db199SXin Li if swapline.startswith('/'): 145*9c5db199SXin Li active_swap_devices.add(swapline.split()[0]) 146*9c5db199SXin Li 147*9c5db199SXin Li partitions = [] 148*9c5db199SXin Li for partline in open_func('/proc/partitions').readlines(): 149*9c5db199SXin Li fields = partline.strip().split() 150*9c5db199SXin Li if len(fields) != 4 or partline.startswith('major'): 151*9c5db199SXin Li continue 152*9c5db199SXin Li (major, minor, blocks, partname) = fields 153*9c5db199SXin Li blocks = int(blocks) 154*9c5db199SXin Li 155*9c5db199SXin Li # The partition name better end with a digit, else it's not a partition 156*9c5db199SXin Li if not partname[-1].isdigit(): 157*9c5db199SXin Li continue 158*9c5db199SXin Li 159*9c5db199SXin Li # We don't want the loopback device in the partition list 160*9c5db199SXin Li if 'loop' in partname: 161*9c5db199SXin Li continue 162*9c5db199SXin Li 163*9c5db199SXin Li device = partname_to_device(partname) 164*9c5db199SXin Li if exclude_swap and device in active_swap_devices: 165*9c5db199SXin Li logging.debug('Skipping %s - Active swap.', partname) 166*9c5db199SXin Li continue 167*9c5db199SXin Li 168*9c5db199SXin Li if min_blocks and blocks < min_blocks: 169*9c5db199SXin Li logging.debug('Skipping %s - Too small.', partname) 170*9c5db199SXin Li continue 171*9c5db199SXin Li 172*9c5db199SXin Li if filter_func and not filter_func(partname): 173*9c5db199SXin Li logging.debug('Skipping %s - Filter func.', partname) 174*9c5db199SXin Li continue 175*9c5db199SXin Li 176*9c5db199SXin Li partitions.append(partition(job, device)) 177*9c5db199SXin Li 178*9c5db199SXin Li return partitions 179*9c5db199SXin Li 180*9c5db199SXin Li 181*9c5db199SXin Lidef get_mount_info(partition_list): 182*9c5db199SXin Li """ 183*9c5db199SXin Li Picks up mount point information about the machine mounts. By default, we 184*9c5db199SXin Li try to associate mount points with UUIDs, because in newer distros the 185*9c5db199SXin Li partitions are uniquely identified using them. 186*9c5db199SXin Li """ 187*9c5db199SXin Li mount_info = set() 188*9c5db199SXin Li for p in partition_list: 189*9c5db199SXin Li try: 190*9c5db199SXin Li uuid = utils.system_output('blkid -p -s UUID -o value %s' % p.device) 191*9c5db199SXin Li except error.CmdError: 192*9c5db199SXin Li # fall back to using the partition 193*9c5db199SXin Li uuid = p.device 194*9c5db199SXin Li mount_info.add((uuid, p.get_mountpoint())) 195*9c5db199SXin Li 196*9c5db199SXin Li return mount_info 197*9c5db199SXin Li 198*9c5db199SXin Li 199*9c5db199SXin Lidef filter_partition_list(partitions, devnames): 200*9c5db199SXin Li """ 201*9c5db199SXin Li Pick and choose which partition to keep. 202*9c5db199SXin Li 203*9c5db199SXin Li filter_partition_list accepts a list of partition objects and a list 204*9c5db199SXin Li of strings. If a partition has the device name of the strings it 205*9c5db199SXin Li is returned in a list. 206*9c5db199SXin Li 207*9c5db199SXin Li @param partitions: A list of L{partition} objects 208*9c5db199SXin Li @param devnames: A list of devnames of the form '/dev/hdc3' that 209*9c5db199SXin Li specifies which partitions to include in the returned list. 210*9c5db199SXin Li 211*9c5db199SXin Li @return: A list of L{partition} objects specified by devnames, in the 212*9c5db199SXin Li order devnames specified 213*9c5db199SXin Li """ 214*9c5db199SXin Li 215*9c5db199SXin Li filtered_list = [] 216*9c5db199SXin Li for p in partitions: 217*9c5db199SXin Li for d in devnames: 218*9c5db199SXin Li if p.device == d and p not in filtered_list: 219*9c5db199SXin Li filtered_list.append(p) 220*9c5db199SXin Li 221*9c5db199SXin Li return filtered_list 222*9c5db199SXin Li 223*9c5db199SXin Li 224*9c5db199SXin Lidef get_unmounted_partition_list(root_part, job=None, min_blocks=0, 225*9c5db199SXin Li filter_func=None, exclude_swap=True, 226*9c5db199SXin Li open_func=open): 227*9c5db199SXin Li """ 228*9c5db199SXin Li Return a list of partition objects that are not mounted. 229*9c5db199SXin Li 230*9c5db199SXin Li @param root_part: The root device name (without the '/dev/' prefix, example 231*9c5db199SXin Li 'hda2') that will be filtered from the partition list. 232*9c5db199SXin Li 233*9c5db199SXin Li Reasoning: in Linux /proc/mounts will never directly mention the 234*9c5db199SXin Li root partition as being mounted on / instead it will say that 235*9c5db199SXin Li /dev/root is mounted on /. Thus require this argument to filter out 236*9c5db199SXin Li the root_part from the ones checked to be mounted. 237*9c5db199SXin Li @param job, min_blocks, filter_func, exclude_swap, open_func: Forwarded 238*9c5db199SXin Li to get_partition_list(). 239*9c5db199SXin Li @return List of L{partition} objects that are not mounted. 240*9c5db199SXin Li """ 241*9c5db199SXin Li partitions = get_partition_list(job=job, min_blocks=min_blocks, 242*9c5db199SXin Li filter_func=filter_func, exclude_swap=exclude_swap, open_func=open_func) 243*9c5db199SXin Li 244*9c5db199SXin Li unmounted = [] 245*9c5db199SXin Li for part in partitions: 246*9c5db199SXin Li if (part.device != partname_to_device(root_part) and 247*9c5db199SXin Li not part.get_mountpoint(open_func=open_func)): 248*9c5db199SXin Li unmounted.append(part) 249*9c5db199SXin Li 250*9c5db199SXin Li return unmounted 251*9c5db199SXin Li 252*9c5db199SXin Li 253*9c5db199SXin Lidef parallel(partitions, method_name, *args, **dargs): 254*9c5db199SXin Li """ 255*9c5db199SXin Li Run a partition method (with appropriate arguments) in parallel, 256*9c5db199SXin Li across a list of partition objects 257*9c5db199SXin Li """ 258*9c5db199SXin Li if not partitions: 259*9c5db199SXin Li return 260*9c5db199SXin Li job = partitions[0].job 261*9c5db199SXin Li flist = [] 262*9c5db199SXin Li if (not hasattr(partition, method_name) or 263*9c5db199SXin Li not callable(getattr(partition, method_name))): 264*9c5db199SXin Li err = "partition.parallel got invalid method %s" % method_name 265*9c5db199SXin Li raise RuntimeError(err) 266*9c5db199SXin Li 267*9c5db199SXin Li for p in partitions: 268*9c5db199SXin Li print_args = list(args) 269*9c5db199SXin Li print_args += ['%s=%s' % (key, dargs[key]) for key in dargs.keys()] 270*9c5db199SXin Li logging.debug('%s.%s(%s)', str(p), method_name, 271*9c5db199SXin Li ', '.join(print_args)) 272*9c5db199SXin Li sys.stdout.flush() 273*9c5db199SXin Li def _run_named_method(function, part=p): 274*9c5db199SXin Li getattr(part, method_name)(*args, **dargs) 275*9c5db199SXin Li flist.append((_run_named_method, ())) 276*9c5db199SXin Li job.parallel(*flist) 277*9c5db199SXin Li 278*9c5db199SXin Li 279*9c5db199SXin Lidef filesystems(): 280*9c5db199SXin Li """ 281*9c5db199SXin Li Return a list of all available filesystems 282*9c5db199SXin Li """ 283*9c5db199SXin Li return [re.sub('(nodev)?\s*', '', fs) for fs in open('/proc/filesystems')] 284*9c5db199SXin Li 285*9c5db199SXin Li 286*9c5db199SXin Lidef unmount_partition(device): 287*9c5db199SXin Li """ 288*9c5db199SXin Li Unmount a mounted partition 289*9c5db199SXin Li 290*9c5db199SXin Li @param device: e.g. /dev/sda1, /dev/hda1 291*9c5db199SXin Li """ 292*9c5db199SXin Li p = partition(job=None, device=device) 293*9c5db199SXin Li p.unmount(record=False) 294*9c5db199SXin Li 295*9c5db199SXin Li 296*9c5db199SXin Lidef is_valid_partition(device): 297*9c5db199SXin Li """ 298*9c5db199SXin Li Checks if a partition is valid 299*9c5db199SXin Li 300*9c5db199SXin Li @param device: e.g. /dev/sda1, /dev/hda1 301*9c5db199SXin Li """ 302*9c5db199SXin Li parts = get_partition_list(job=None) 303*9c5db199SXin Li p_list = [ p.device for p in parts ] 304*9c5db199SXin Li if device in p_list: 305*9c5db199SXin Li return True 306*9c5db199SXin Li 307*9c5db199SXin Li return False 308*9c5db199SXin Li 309*9c5db199SXin Li 310*9c5db199SXin Lidef is_valid_disk(device): 311*9c5db199SXin Li """ 312*9c5db199SXin Li Checks if a disk is valid 313*9c5db199SXin Li 314*9c5db199SXin Li @param device: e.g. /dev/sda, /dev/hda 315*9c5db199SXin Li """ 316*9c5db199SXin Li partitions = [] 317*9c5db199SXin Li for partline in open('/proc/partitions').readlines(): 318*9c5db199SXin Li fields = partline.strip().split() 319*9c5db199SXin Li if len(fields) != 4 or partline.startswith('major'): 320*9c5db199SXin Li continue 321*9c5db199SXin Li (major, minor, blocks, partname) = fields 322*9c5db199SXin Li blocks = int(blocks) 323*9c5db199SXin Li 324*9c5db199SXin Li if not partname[-1].isdigit(): 325*9c5db199SXin Li # Disk name does not end in number, AFAIK 326*9c5db199SXin Li # so use it as a reference to a disk 327*9c5db199SXin Li if device.strip("/dev/") == partname: 328*9c5db199SXin Li return True 329*9c5db199SXin Li 330*9c5db199SXin Li return False 331*9c5db199SXin Li 332*9c5db199SXin Li 333*9c5db199SXin Lidef run_test_on_partitions(job, test, partitions, mountpoint_func, 334*9c5db199SXin Li tag, fs_opt, do_fsck=True, **dargs): 335*9c5db199SXin Li """ 336*9c5db199SXin Li Run a test that requires multiple partitions. Filesystems will be 337*9c5db199SXin Li made on the partitions and mounted, then the test will run, then the 338*9c5db199SXin Li filesystems will be unmounted and optionally fsck'd. 339*9c5db199SXin Li 340*9c5db199SXin Li @param job: A job instance to run the test 341*9c5db199SXin Li @param test: A string containing the name of the test 342*9c5db199SXin Li @param partitions: A list of partition objects, these are passed to the 343*9c5db199SXin Li test as partitions= 344*9c5db199SXin Li @param mountpoint_func: A callable that returns a mountpoint given a 345*9c5db199SXin Li partition instance 346*9c5db199SXin Li @param tag: A string tag to make this test unique (Required for control 347*9c5db199SXin Li files that make multiple calls to this routine with the same value 348*9c5db199SXin Li of 'test'.) 349*9c5db199SXin Li @param fs_opt: An FsOptions instance that describes what filesystem to make 350*9c5db199SXin Li @param do_fsck: include fsck in post-test partition cleanup. 351*9c5db199SXin Li @param dargs: Dictionary of arguments to be passed to job.run_test() and 352*9c5db199SXin Li eventually the test 353*9c5db199SXin Li """ 354*9c5db199SXin Li # setup the filesystem parameters for all the partitions 355*9c5db199SXin Li for p in partitions: 356*9c5db199SXin Li p.set_fs_options(fs_opt) 357*9c5db199SXin Li 358*9c5db199SXin Li # make and mount all the partitions in parallel 359*9c5db199SXin Li parallel(partitions, 'setup_before_test', mountpoint_func=mountpoint_func) 360*9c5db199SXin Li 361*9c5db199SXin Li mountpoint = mountpoint_func(partitions[0]) 362*9c5db199SXin Li 363*9c5db199SXin Li # run the test against all the partitions 364*9c5db199SXin Li job.run_test(test, tag=tag, partitions=partitions, dir=mountpoint, **dargs) 365*9c5db199SXin Li 366*9c5db199SXin Li parallel(partitions, 'unmount') # unmount all partitions in parallel 367*9c5db199SXin Li if do_fsck: 368*9c5db199SXin Li parallel(partitions, 'fsck') # fsck all partitions in parallel 369*9c5db199SXin Li # else fsck is done by caller 370*9c5db199SXin Li 371*9c5db199SXin Li 372*9c5db199SXin Liclass partition(object): 373*9c5db199SXin Li """ 374*9c5db199SXin Li Class for handling partitions and filesystems 375*9c5db199SXin Li """ 376*9c5db199SXin Li 377*9c5db199SXin Li def __init__(self, job, device, loop_size=0, mountpoint=None): 378*9c5db199SXin Li """ 379*9c5db199SXin Li @param job: A L{client.bin.job} instance. 380*9c5db199SXin Li @param device: The device in question (e.g."/dev/hda2"). If device is a 381*9c5db199SXin Li file it will be mounted as loopback. 382*9c5db199SXin Li @param loop_size: Size of loopback device (in MB). Defaults to 0. 383*9c5db199SXin Li """ 384*9c5db199SXin Li self.device = device 385*9c5db199SXin Li self.name = os.path.basename(device) 386*9c5db199SXin Li self.job = job 387*9c5db199SXin Li self.loop = loop_size 388*9c5db199SXin Li self.fstype = None 389*9c5db199SXin Li self.mountpoint = mountpoint 390*9c5db199SXin Li self.mkfs_flags = None 391*9c5db199SXin Li self.mount_options = None 392*9c5db199SXin Li self.fs_tag = None 393*9c5db199SXin Li if self.loop: 394*9c5db199SXin Li cmd = 'dd if=/dev/zero of=%s bs=1M count=%d' % (device, loop_size) 395*9c5db199SXin Li utils.system(cmd) 396*9c5db199SXin Li 397*9c5db199SXin Li 398*9c5db199SXin Li def __repr__(self): 399*9c5db199SXin Li return '<Partition: %s>' % self.device 400*9c5db199SXin Li 401*9c5db199SXin Li 402*9c5db199SXin Li def set_fs_options(self, fs_options): 403*9c5db199SXin Li """ 404*9c5db199SXin Li Set filesystem options 405*9c5db199SXin Li 406*9c5db199SXin Li @param fs_options: A L{FsOptions} object 407*9c5db199SXin Li """ 408*9c5db199SXin Li 409*9c5db199SXin Li self.fstype = fs_options.fstype 410*9c5db199SXin Li self.mkfs_flags = fs_options.mkfs_flags 411*9c5db199SXin Li self.mount_options = fs_options.mount_options 412*9c5db199SXin Li self.fs_tag = fs_options.fs_tag 413*9c5db199SXin Li 414*9c5db199SXin Li 415*9c5db199SXin Li def run_test(self, test, **dargs): 416*9c5db199SXin Li self.job.run_test(test, dir=self.get_mountpoint(), **dargs) 417*9c5db199SXin Li 418*9c5db199SXin Li 419*9c5db199SXin Li def setup_before_test(self, mountpoint_func): 420*9c5db199SXin Li """ 421*9c5db199SXin Li Prepare a partition for running a test. Unmounts any 422*9c5db199SXin Li filesystem that's currently mounted on the partition, makes a 423*9c5db199SXin Li new filesystem (according to this partition's filesystem 424*9c5db199SXin Li options) and mounts it where directed by mountpoint_func. 425*9c5db199SXin Li 426*9c5db199SXin Li @param mountpoint_func: A callable that returns a path as a string, 427*9c5db199SXin Li given a partition instance. 428*9c5db199SXin Li """ 429*9c5db199SXin Li mountpoint = mountpoint_func(self) 430*9c5db199SXin Li if not mountpoint: 431*9c5db199SXin Li raise ValueError('Don\'t know where to put this partition') 432*9c5db199SXin Li self.unmount(ignore_status=True, record=False) 433*9c5db199SXin Li self.mkfs() 434*9c5db199SXin Li if not os.path.isdir(mountpoint): 435*9c5db199SXin Li os.makedirs(mountpoint) 436*9c5db199SXin Li self.mount(mountpoint) 437*9c5db199SXin Li 438*9c5db199SXin Li 439*9c5db199SXin Li def run_test_on_partition(self, test, mountpoint_func, **dargs): 440*9c5db199SXin Li """ 441*9c5db199SXin Li Executes a test fs-style (umount,mkfs,mount,test) 442*9c5db199SXin Li 443*9c5db199SXin Li Here we unmarshal the args to set up tags before running the test. 444*9c5db199SXin Li Tests are also run by first umounting, mkfsing and then mounting 445*9c5db199SXin Li before executing the test. 446*9c5db199SXin Li 447*9c5db199SXin Li @param test: name of test to run 448*9c5db199SXin Li @param mountpoint_func: function to return mount point string 449*9c5db199SXin Li """ 450*9c5db199SXin Li tag = dargs.get('tag') 451*9c5db199SXin Li if tag: 452*9c5db199SXin Li tag = '%s.%s' % (self.name, tag) 453*9c5db199SXin Li elif self.fs_tag: 454*9c5db199SXin Li tag = '%s.%s' % (self.name, self.fs_tag) 455*9c5db199SXin Li else: 456*9c5db199SXin Li tag = self.name 457*9c5db199SXin Li 458*9c5db199SXin Li # If there's a 'suffix' argument, append it to the tag and remove it 459*9c5db199SXin Li suffix = dargs.pop('suffix', None) 460*9c5db199SXin Li if suffix: 461*9c5db199SXin Li tag = '%s.%s' % (tag, suffix) 462*9c5db199SXin Li 463*9c5db199SXin Li dargs['tag'] = test + '.' + tag 464*9c5db199SXin Li 465*9c5db199SXin Li def _make_partition_and_run_test(test_tag, dir=None, **dargs): 466*9c5db199SXin Li self.setup_before_test(mountpoint_func) 467*9c5db199SXin Li try: 468*9c5db199SXin Li self.job.run_test(test, tag=test_tag, dir=mountpoint, **dargs) 469*9c5db199SXin Li finally: 470*9c5db199SXin Li self.unmount() 471*9c5db199SXin Li self.fsck() 472*9c5db199SXin Li 473*9c5db199SXin Li 474*9c5db199SXin Li mountpoint = mountpoint_func(self) 475*9c5db199SXin Li 476*9c5db199SXin Li # The tag is the tag for the group (get stripped off by run_group) 477*9c5db199SXin Li # The test_tag is the tag for the test itself 478*9c5db199SXin Li self.job.run_group(_make_partition_and_run_test, 479*9c5db199SXin Li test_tag=tag, dir=mountpoint, **dargs) 480*9c5db199SXin Li 481*9c5db199SXin Li 482*9c5db199SXin Li def get_mountpoint(self, open_func=open, filename=None): 483*9c5db199SXin Li """ 484*9c5db199SXin Li Find the mount point of this partition object. 485*9c5db199SXin Li 486*9c5db199SXin Li @param open_func: the function to use for opening the file containing 487*9c5db199SXin Li the mounted partitions information 488*9c5db199SXin Li @param filename: where to look for the mounted partitions information 489*9c5db199SXin Li (default None which means it will search /proc/mounts and/or 490*9c5db199SXin Li /etc/mtab) 491*9c5db199SXin Li 492*9c5db199SXin Li @returns a string with the mount point of the partition or None if not 493*9c5db199SXin Li mounted 494*9c5db199SXin Li """ 495*9c5db199SXin Li if filename: 496*9c5db199SXin Li for line in open_func(filename).readlines(): 497*9c5db199SXin Li parts = line.split() 498*9c5db199SXin Li if parts[0] == self.device or parts[1] == self.mountpoint: 499*9c5db199SXin Li return parts[1] # The mountpoint where it's mounted 500*9c5db199SXin Li return None 501*9c5db199SXin Li 502*9c5db199SXin Li # no specific file given, look in /proc/mounts 503*9c5db199SXin Li res = self.get_mountpoint(open_func=open_func, filename='/proc/mounts') 504*9c5db199SXin Li if not res: 505*9c5db199SXin Li # sometimes the root partition is reported as /dev/root in 506*9c5db199SXin Li # /proc/mounts in this case, try /etc/mtab 507*9c5db199SXin Li res = self.get_mountpoint(open_func=open_func, filename='/etc/mtab') 508*9c5db199SXin Li 509*9c5db199SXin Li # trust /etc/mtab only about / 510*9c5db199SXin Li if res != '/': 511*9c5db199SXin Li res = None 512*9c5db199SXin Li 513*9c5db199SXin Li return res 514*9c5db199SXin Li 515*9c5db199SXin Li 516*9c5db199SXin Li def mkfs_exec(self, fstype): 517*9c5db199SXin Li """ 518*9c5db199SXin Li Return the proper mkfs executable based on fs 519*9c5db199SXin Li """ 520*9c5db199SXin Li if fstype == 'ext4': 521*9c5db199SXin Li if os.path.exists('/sbin/mkfs.ext4'): 522*9c5db199SXin Li return 'mkfs' 523*9c5db199SXin Li # If ext4 supported e2fsprogs is not installed we use the 524*9c5db199SXin Li # autotest supplied one in tools dir which is statically linked""" 525*9c5db199SXin Li auto_mkfs = os.path.join(self.job.toolsdir, 'mkfs.ext4dev') 526*9c5db199SXin Li if os.path.exists(auto_mkfs): 527*9c5db199SXin Li return auto_mkfs 528*9c5db199SXin Li else: 529*9c5db199SXin Li return 'mkfs' 530*9c5db199SXin Li 531*9c5db199SXin Li raise NameError('Error creating partition for filesystem type %s' % 532*9c5db199SXin Li fstype) 533*9c5db199SXin Li 534*9c5db199SXin Li 535*9c5db199SXin Li def mkfs(self, fstype=None, args='', record=True): 536*9c5db199SXin Li """ 537*9c5db199SXin Li Format a partition to filesystem type 538*9c5db199SXin Li 539*9c5db199SXin Li @param fstype: the filesystem type, e.g.. "ext3", "ext2" 540*9c5db199SXin Li @param args: arguments to be passed to mkfs command. 541*9c5db199SXin Li @param record: if set, output result of mkfs operation to autotest 542*9c5db199SXin Li output 543*9c5db199SXin Li """ 544*9c5db199SXin Li 545*9c5db199SXin Li if list_mount_devices().count(self.device): 546*9c5db199SXin Li raise NameError('Attempted to format mounted device %s' % 547*9c5db199SXin Li self.device) 548*9c5db199SXin Li 549*9c5db199SXin Li if not fstype: 550*9c5db199SXin Li if self.fstype: 551*9c5db199SXin Li fstype = self.fstype 552*9c5db199SXin Li else: 553*9c5db199SXin Li fstype = 'ext2' 554*9c5db199SXin Li 555*9c5db199SXin Li if self.mkfs_flags: 556*9c5db199SXin Li args += ' ' + self.mkfs_flags 557*9c5db199SXin Li if fstype == 'xfs': 558*9c5db199SXin Li args += ' -f' 559*9c5db199SXin Li 560*9c5db199SXin Li if self.loop: 561*9c5db199SXin Li # BAH. Inconsistent mkfs syntax SUCKS. 562*9c5db199SXin Li if fstype.startswith('ext'): 563*9c5db199SXin Li args += ' -F' 564*9c5db199SXin Li elif fstype == 'reiserfs': 565*9c5db199SXin Li args += ' -f' 566*9c5db199SXin Li 567*9c5db199SXin Li # If there isn't already a '-t <type>' argument, add one. 568*9c5db199SXin Li if not "-t" in args: 569*9c5db199SXin Li args = "-t %s %s" % (fstype, args) 570*9c5db199SXin Li 571*9c5db199SXin Li args = args.strip() 572*9c5db199SXin Li 573*9c5db199SXin Li mkfs_cmd = "%s %s %s" % (self.mkfs_exec(fstype), args, self.device) 574*9c5db199SXin Li 575*9c5db199SXin Li sys.stdout.flush() 576*9c5db199SXin Li try: 577*9c5db199SXin Li # We throw away the output here - we only need it on error, in 578*9c5db199SXin Li # which case it's in the exception 579*9c5db199SXin Li utils.system_output("yes | %s" % mkfs_cmd) 580*9c5db199SXin Li except error.CmdError as e: 581*9c5db199SXin Li logging.error(e.result_obj) 582*9c5db199SXin Li if record: 583*9c5db199SXin Li self.job.record('FAIL', None, mkfs_cmd, error.format_error()) 584*9c5db199SXin Li raise 585*9c5db199SXin Li except: 586*9c5db199SXin Li if record: 587*9c5db199SXin Li self.job.record('FAIL', None, mkfs_cmd, error.format_error()) 588*9c5db199SXin Li raise 589*9c5db199SXin Li else: 590*9c5db199SXin Li if record: 591*9c5db199SXin Li self.job.record('GOOD', None, mkfs_cmd) 592*9c5db199SXin Li self.fstype = fstype 593*9c5db199SXin Li 594*9c5db199SXin Li 595*9c5db199SXin Li def get_fsck_exec(self): 596*9c5db199SXin Li """ 597*9c5db199SXin Li Return the proper mkfs executable based on self.fstype 598*9c5db199SXin Li """ 599*9c5db199SXin Li if self.fstype == 'ext4': 600*9c5db199SXin Li if os.path.exists('/sbin/fsck.ext4'): 601*9c5db199SXin Li return 'fsck' 602*9c5db199SXin Li # If ext4 supported e2fsprogs is not installed we use the 603*9c5db199SXin Li # autotest supplied one in tools dir which is statically linked""" 604*9c5db199SXin Li auto_fsck = os.path.join(self.job.toolsdir, 'fsck.ext4dev') 605*9c5db199SXin Li if os.path.exists(auto_fsck): 606*9c5db199SXin Li return auto_fsck 607*9c5db199SXin Li else: 608*9c5db199SXin Li return 'fsck' 609*9c5db199SXin Li 610*9c5db199SXin Li raise NameError('Error creating partition for filesystem type %s' % 611*9c5db199SXin Li self.fstype) 612*9c5db199SXin Li 613*9c5db199SXin Li 614*9c5db199SXin Li def fsck(self, args='-fy', record=True): 615*9c5db199SXin Li """ 616*9c5db199SXin Li Run filesystem check 617*9c5db199SXin Li 618*9c5db199SXin Li @param args: arguments to filesystem check tool. Default is "-n" 619*9c5db199SXin Li which works on most tools. 620*9c5db199SXin Li """ 621*9c5db199SXin Li 622*9c5db199SXin Li # I hate reiserfstools. 623*9c5db199SXin Li # Requires an explit Yes for some inane reason 624*9c5db199SXin Li fsck_cmd = '%s %s %s' % (self.get_fsck_exec(), self.device, args) 625*9c5db199SXin Li if self.fstype == 'reiserfs': 626*9c5db199SXin Li fsck_cmd = 'yes "Yes" | ' + fsck_cmd 627*9c5db199SXin Li sys.stdout.flush() 628*9c5db199SXin Li try: 629*9c5db199SXin Li utils.system_output(fsck_cmd) 630*9c5db199SXin Li except: 631*9c5db199SXin Li if record: 632*9c5db199SXin Li self.job.record('FAIL', None, fsck_cmd, error.format_error()) 633*9c5db199SXin Li raise error.TestError('Fsck found errors with the underlying ' 634*9c5db199SXin Li 'file system') 635*9c5db199SXin Li else: 636*9c5db199SXin Li if record: 637*9c5db199SXin Li self.job.record('GOOD', None, fsck_cmd) 638*9c5db199SXin Li 639*9c5db199SXin Li 640*9c5db199SXin Li def mount(self, mountpoint=None, fstype=None, args='', record=True): 641*9c5db199SXin Li """ 642*9c5db199SXin Li Mount this partition to a mount point 643*9c5db199SXin Li 644*9c5db199SXin Li @param mountpoint: If you have not provided a mountpoint to partition 645*9c5db199SXin Li object or want to use a different one, you may specify it here. 646*9c5db199SXin Li @param fstype: Filesystem type. If not provided partition object value 647*9c5db199SXin Li will be used. 648*9c5db199SXin Li @param args: Arguments to be passed to "mount" command. 649*9c5db199SXin Li @param record: If True, output result of mount operation to autotest 650*9c5db199SXin Li output. 651*9c5db199SXin Li """ 652*9c5db199SXin Li 653*9c5db199SXin Li if fstype is None: 654*9c5db199SXin Li fstype = self.fstype 655*9c5db199SXin Li else: 656*9c5db199SXin Li assert(self.fstype is None or self.fstype == fstype); 657*9c5db199SXin Li 658*9c5db199SXin Li if self.mount_options: 659*9c5db199SXin Li args += ' -o ' + self.mount_options 660*9c5db199SXin Li if fstype: 661*9c5db199SXin Li args += ' -t ' + fstype 662*9c5db199SXin Li if self.loop: 663*9c5db199SXin Li args += ' -o loop' 664*9c5db199SXin Li args = args.lstrip() 665*9c5db199SXin Li 666*9c5db199SXin Li if not mountpoint and not self.mountpoint: 667*9c5db199SXin Li raise ValueError("No mountpoint specified and no default " 668*9c5db199SXin Li "provided to this partition object") 669*9c5db199SXin Li if not mountpoint: 670*9c5db199SXin Li mountpoint = self.mountpoint 671*9c5db199SXin Li 672*9c5db199SXin Li mount_cmd = "mount %s %s %s" % (args, self.device, mountpoint) 673*9c5db199SXin Li 674*9c5db199SXin Li if list_mount_devices().count(self.device): 675*9c5db199SXin Li err = 'Attempted to mount mounted device' 676*9c5db199SXin Li self.job.record('FAIL', None, mount_cmd, err) 677*9c5db199SXin Li raise NameError(err) 678*9c5db199SXin Li if list_mount_points().count(mountpoint): 679*9c5db199SXin Li err = 'Attempted to mount busy mountpoint' 680*9c5db199SXin Li self.job.record('FAIL', None, mount_cmd, err) 681*9c5db199SXin Li raise NameError(err) 682*9c5db199SXin Li 683*9c5db199SXin Li mtab = open('/etc/mtab') 684*9c5db199SXin Li # We have to get an exclusive lock here - mount/umount are racy 685*9c5db199SXin Li fcntl.flock(mtab.fileno(), fcntl.LOCK_EX) 686*9c5db199SXin Li sys.stdout.flush() 687*9c5db199SXin Li try: 688*9c5db199SXin Li utils.system(mount_cmd) 689*9c5db199SXin Li mtab.close() 690*9c5db199SXin Li except: 691*9c5db199SXin Li mtab.close() 692*9c5db199SXin Li if record: 693*9c5db199SXin Li self.job.record('FAIL', None, mount_cmd, error.format_error()) 694*9c5db199SXin Li raise 695*9c5db199SXin Li else: 696*9c5db199SXin Li if record: 697*9c5db199SXin Li self.job.record('GOOD', None, mount_cmd) 698*9c5db199SXin Li self.fstype = fstype 699*9c5db199SXin Li 700*9c5db199SXin Li 701*9c5db199SXin Li def unmount_force(self): 702*9c5db199SXin Li """ 703*9c5db199SXin Li Kill all other jobs accessing this partition. Use fuser and ps to find 704*9c5db199SXin Li all mounts on this mountpoint and unmount them. 705*9c5db199SXin Li 706*9c5db199SXin Li @return: true for success or false for any errors 707*9c5db199SXin Li """ 708*9c5db199SXin Li 709*9c5db199SXin Li logging.debug("Standard umount failed, will try forcing. Users:") 710*9c5db199SXin Li try: 711*9c5db199SXin Li cmd = 'fuser ' + self.get_mountpoint() 712*9c5db199SXin Li logging.debug(cmd) 713*9c5db199SXin Li fuser = utils.system_output(cmd) 714*9c5db199SXin Li logging.debug(fuser) 715*9c5db199SXin Li users = re.sub('.*:', '', fuser).split() 716*9c5db199SXin Li for user in users: 717*9c5db199SXin Li m = re.match('(\d+)(.*)', user) 718*9c5db199SXin Li (pid, usage) = (m.group(1), m.group(2)) 719*9c5db199SXin Li try: 720*9c5db199SXin Li ps = utils.system_output('ps -p %s | sed 1d' % pid) 721*9c5db199SXin Li logging.debug('%s %s %s', usage, pid, ps) 722*9c5db199SXin Li except Exception: 723*9c5db199SXin Li pass 724*9c5db199SXin Li utils.system('ls -l ' + self.device) 725*9c5db199SXin Li umount_cmd = "umount -f " + self.device 726*9c5db199SXin Li utils.system(umount_cmd) 727*9c5db199SXin Li return True 728*9c5db199SXin Li except error.CmdError: 729*9c5db199SXin Li logging.debug('Umount_force failed for %s', self.device) 730*9c5db199SXin Li return False 731*9c5db199SXin Li 732*9c5db199SXin Li 733*9c5db199SXin Li 734*9c5db199SXin Li def unmount(self, ignore_status=False, record=True): 735*9c5db199SXin Li """ 736*9c5db199SXin Li Umount this partition. 737*9c5db199SXin Li 738*9c5db199SXin Li It's easier said than done to umount a partition. 739*9c5db199SXin Li We need to lock the mtab file to make sure we don't have any 740*9c5db199SXin Li locking problems if we are umounting in paralllel. 741*9c5db199SXin Li 742*9c5db199SXin Li If there turns out to be a problem with the simple umount we 743*9c5db199SXin Li end up calling umount_force to get more agressive. 744*9c5db199SXin Li 745*9c5db199SXin Li @param ignore_status: should we notice the umount status 746*9c5db199SXin Li @param record: if True, output result of umount operation to 747*9c5db199SXin Li autotest output 748*9c5db199SXin Li """ 749*9c5db199SXin Li 750*9c5db199SXin Li mountpoint = self.get_mountpoint() 751*9c5db199SXin Li if not mountpoint: 752*9c5db199SXin Li # It's not even mounted to start with 753*9c5db199SXin Li if record and not ignore_status: 754*9c5db199SXin Li msg = 'umount for dev %s has no mountpoint' % self.device 755*9c5db199SXin Li self.job.record('FAIL', None, msg, 'Not mounted') 756*9c5db199SXin Li return 757*9c5db199SXin Li 758*9c5db199SXin Li umount_cmd = "umount " + mountpoint 759*9c5db199SXin Li mtab = open('/etc/mtab') 760*9c5db199SXin Li 761*9c5db199SXin Li # We have to get an exclusive lock here - mount/umount are racy 762*9c5db199SXin Li fcntl.flock(mtab.fileno(), fcntl.LOCK_EX) 763*9c5db199SXin Li sys.stdout.flush() 764*9c5db199SXin Li try: 765*9c5db199SXin Li utils.system(umount_cmd) 766*9c5db199SXin Li mtab.close() 767*9c5db199SXin Li if record: 768*9c5db199SXin Li self.job.record('GOOD', None, umount_cmd) 769*9c5db199SXin Li except (error.CmdError, IOError): 770*9c5db199SXin Li mtab.close() 771*9c5db199SXin Li 772*9c5db199SXin Li # Try the forceful umount 773*9c5db199SXin Li if self.unmount_force(): 774*9c5db199SXin Li return 775*9c5db199SXin Li 776*9c5db199SXin Li # If we are here we cannot umount this partition 777*9c5db199SXin Li if record and not ignore_status: 778*9c5db199SXin Li self.job.record('FAIL', None, umount_cmd, error.format_error()) 779*9c5db199SXin Li raise 780*9c5db199SXin Li 781*9c5db199SXin Li 782*9c5db199SXin Li def wipe(self): 783*9c5db199SXin Li """ 784*9c5db199SXin Li Delete all files of a given partition filesystem. 785*9c5db199SXin Li """ 786*9c5db199SXin Li wipe_filesystem(self.job, self.get_mountpoint()) 787*9c5db199SXin Li 788*9c5db199SXin Li 789*9c5db199SXin Li def get_io_scheduler_list(self, device_name): 790*9c5db199SXin Li names = open(self.__sched_path(device_name)).read() 791*9c5db199SXin Li return names.translate(string.maketrans('[]', ' ')).split() 792*9c5db199SXin Li 793*9c5db199SXin Li 794*9c5db199SXin Li def get_io_scheduler(self, device_name): 795*9c5db199SXin Li return re.split('[\[\]]', 796*9c5db199SXin Li open(self.__sched_path(device_name)).read())[1] 797*9c5db199SXin Li 798*9c5db199SXin Li 799*9c5db199SXin Li def set_io_scheduler(self, device_name, name): 800*9c5db199SXin Li if name not in self.get_io_scheduler_list(device_name): 801*9c5db199SXin Li raise NameError('No such IO scheduler: %s' % name) 802*9c5db199SXin Li f = open(self.__sched_path(device_name), 'w') 803*9c5db199SXin Li f.write(name) 804*9c5db199SXin Li f.close() 805*9c5db199SXin Li 806*9c5db199SXin Li 807*9c5db199SXin Li def __sched_path(self, device_name): 808*9c5db199SXin Li return '/sys/block/%s/queue/scheduler' % device_name 809*9c5db199SXin Li 810*9c5db199SXin Li 811*9c5db199SXin Liclass virtual_partition: 812*9c5db199SXin Li """ 813*9c5db199SXin Li Handles block device emulation using file images of disks. 814*9c5db199SXin Li It's important to note that this API can be used only if 815*9c5db199SXin Li we have the following programs present on the client machine: 816*9c5db199SXin Li 817*9c5db199SXin Li * dd 818*9c5db199SXin Li * losetup 819*9c5db199SXin Li * truncate 820*9c5db199SXin Li """ 821*9c5db199SXin Li def __init__(self, file_img, file_size): 822*9c5db199SXin Li """ 823*9c5db199SXin Li Creates a virtual partition, keeping record of the device created 824*9c5db199SXin Li under /dev/mapper (device attribute) so test writers can use it 825*9c5db199SXin Li on their filesystem tests. 826*9c5db199SXin Li 827*9c5db199SXin Li @param file_img: Path to the desired disk image file. 828*9c5db199SXin Li @param file_size: Size of the desired image in Bytes. 829*9c5db199SXin Li """ 830*9c5db199SXin Li logging.debug('Quick check before attempting to create virtual ' 831*9c5db199SXin Li 'partition') 832*9c5db199SXin Li try: 833*9c5db199SXin Li os_dep.commands('dd', 'losetup', 'truncate') 834*9c5db199SXin Li except ValueError as e: 835*9c5db199SXin Li e_msg = 'Unable to create virtual partition: %s' % e 836*9c5db199SXin Li raise error.AutotestError(e_msg) 837*9c5db199SXin Li 838*9c5db199SXin Li logging.debug('Creating virtual partition') 839*9c5db199SXin Li self.size = file_size 840*9c5db199SXin Li self.img = self._create_disk_img(file_img) 841*9c5db199SXin Li self.loop = self._attach_img_loop() 842*9c5db199SXin Li self.device = self.loop 843*9c5db199SXin Li logging.debug('Virtual partition successfuly created') 844*9c5db199SXin Li logging.debug('Image disk: %s', self.img) 845*9c5db199SXin Li logging.debug('Loopback device: %s', self.loop) 846*9c5db199SXin Li logging.debug('Device path: %s', self.device) 847*9c5db199SXin Li 848*9c5db199SXin Li 849*9c5db199SXin Li def destroy(self): 850*9c5db199SXin Li """ 851*9c5db199SXin Li Removes the virtual partition from /dev/mapper, detaches the image file 852*9c5db199SXin Li from the loopback device and removes the image file. 853*9c5db199SXin Li """ 854*9c5db199SXin Li logging.debug('Removing virtual partition - device %s', self.device) 855*9c5db199SXin Li self._detach_img_loop() 856*9c5db199SXin Li self._remove_disk_img() 857*9c5db199SXin Li 858*9c5db199SXin Li 859*9c5db199SXin Li def _create_disk_img(self, img_path): 860*9c5db199SXin Li """ 861*9c5db199SXin Li Creates a disk image using dd. 862*9c5db199SXin Li 863*9c5db199SXin Li @param img_path: Path to the desired image file. 864*9c5db199SXin Li @param size: Size of the desired image in MB. 865*9c5db199SXin Li @returns: Path of the image created. 866*9c5db199SXin Li """ 867*9c5db199SXin Li logging.debug('Creating disk image %s, size = %d MB', 868*9c5db199SXin Li img_path, self.size) 869*9c5db199SXin Li try: 870*9c5db199SXin Li cmd = 'truncate %s --size %dM' % (img_path, self.size) 871*9c5db199SXin Li utils.run(cmd) 872*9c5db199SXin Li except error.CmdError as e: 873*9c5db199SXin Li e_msg = 'Error creating disk image %s: %s' % (img_path, e) 874*9c5db199SXin Li raise error.AutotestError(e_msg) 875*9c5db199SXin Li return img_path 876*9c5db199SXin Li 877*9c5db199SXin Li 878*9c5db199SXin Li def _attach_img_loop(self): 879*9c5db199SXin Li """ 880*9c5db199SXin Li Attaches a file image to a loopback device using losetup. 881*9c5db199SXin Li @returns: Path of the loopback device associated. 882*9c5db199SXin Li """ 883*9c5db199SXin Li logging.debug('Attaching image %s to a loop device', self.img) 884*9c5db199SXin Li try: 885*9c5db199SXin Li cmd = 'losetup -f' 886*9c5db199SXin Li loop_path = utils.system_output(cmd) 887*9c5db199SXin Li cmd = 'losetup -f %s' % self.img 888*9c5db199SXin Li utils.run(cmd) 889*9c5db199SXin Li except error.CmdError as e: 890*9c5db199SXin Li e_msg = ('Error attaching image %s to a loop device: %s' % 891*9c5db199SXin Li (self.img, e)) 892*9c5db199SXin Li raise error.AutotestError(e_msg) 893*9c5db199SXin Li return loop_path 894*9c5db199SXin Li 895*9c5db199SXin Li 896*9c5db199SXin Li def _detach_img_loop(self): 897*9c5db199SXin Li """ 898*9c5db199SXin Li Detaches the image file from the loopback device. 899*9c5db199SXin Li """ 900*9c5db199SXin Li logging.debug('Detaching image %s from loop device %s', self.img, 901*9c5db199SXin Li self.loop) 902*9c5db199SXin Li try: 903*9c5db199SXin Li cmd = 'losetup -d %s' % self.loop 904*9c5db199SXin Li utils.run(cmd) 905*9c5db199SXin Li except error.CmdError as e: 906*9c5db199SXin Li e_msg = ('Error detaching image %s from loop device %s: %s' % 907*9c5db199SXin Li (self.img, self.loop, e)) 908*9c5db199SXin Li raise error.AutotestError(e_msg) 909*9c5db199SXin Li 910*9c5db199SXin Li 911*9c5db199SXin Li def _remove_disk_img(self): 912*9c5db199SXin Li """ 913*9c5db199SXin Li Removes the disk image. 914*9c5db199SXin Li """ 915*9c5db199SXin Li logging.debug('Removing disk image %s', self.img) 916*9c5db199SXin Li try: 917*9c5db199SXin Li os.remove(self.img) 918*9c5db199SXin Li except: 919*9c5db199SXin Li e_msg = 'Error removing image file %s' % self.img 920*9c5db199SXin Li raise error.AutotestError(e_msg) 921*9c5db199SXin Li 922*9c5db199SXin Li 923*9c5db199SXin Li# import a site partition module to allow it to override functions 924*9c5db199SXin Litry: 925*9c5db199SXin Li from autotest_lib.client.bin.site_partition import * 926*9c5db199SXin Liexcept ImportError: 927*9c5db199SXin Li pass 928