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