2 APIs to write tests and control files that handle partition creation, deletion
5 @copyright: Google 2006-2008
6 @author: Martin Bligh (mbligh@google.com)
9 import os
, re
, string
, sys
, fcntl
, logging
10 from autotest_lib
.client
.bin
import os_dep
, utils
11 from autotest_lib
.client
.common_lib
import error
14 class FsOptions(object):
16 A class encapsulating a filesystem test's parameters.
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.
23 __slots__
= ('fstype', 'mkfs_flags', 'mount_options', 'fs_tag')
25 def __init__(self
, fstype
, fs_tag
, mkfs_flags
=None, mount_options
=None):
27 Fill in our properties.
29 @param fstype: The filesystem type ('ext2', 'ext4', 'xfs', etc.)
30 @param fs_tag: A short name for this filesystem test to use
32 @param mkfs_flags: Optional. Additional command line options to mkfs.
33 @param mount_options: Optional. The options to pass to mount -o.
36 if not fstype
or not fs_tag
:
37 raise ValueError('A filesystem and fs_tag are required.')
40 self
.mkfs_flags
= mkfs_flags
or ""
41 self
.mount_options
= mount_options
or ""
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
))
52 def partname_to_device(part
):
53 """ Converts a partition name to its associated device """
54 return os
.path
.join(os
.sep
, 'dev', part
)
57 def list_mount_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])
69 def list_mount_points():
71 for line
in utils
.system_output('mount').splitlines():
72 mountpoints
.append(line
.split()[2])
76 def get_iosched_path(device_name
, component
):
77 return '/sys/block/%s/queue/%s' % (device_name
, component
)
80 def wipe_filesystem(job
, mountpoint
):
81 wipe_cmd
= 'rm -rf %s/*' % mountpoint
83 utils
.system(wipe_cmd
)
85 job
.record('FAIL', None, wipe_cmd
, error
.format_error())
88 job
.record('GOOD', None, wipe_cmd
)
91 def is_linux_fs_type(device
):
93 Checks if specified partition is type 83
95 @param device: the device, e.g. /dev/sda3
97 @return: False if the supplied partition name is not type 83 linux, True
100 disk_device
= device
.rstrip('0123456789')
102 # Parse fdisk output to get partition info. Ugly but it works.
103 fdisk_fd
= os
.popen("/sbin/fdisk -l -u '%s'" % disk_device
)
104 fdisk_lines
= fdisk_fd
.readlines()
106 for line
in fdisk_lines
:
107 if not line
.startswith(device
):
109 info_tuple
= line
.split()
110 # The Id will be in one of two fields depending on if the boot flag
111 # was set. Caveat: this assumes no boot partition will be 83 blocks.
112 for fsinfo
in info_tuple
[4:6]:
113 if fsinfo
== '83': # hex 83 is the linux fs partition type
118 def get_partition_list(job
, min_blocks
=0, filter_func
=None, exclude_swap
=True,
121 Get a list of partition objects for all disk partitions on the system.
123 Loopback devices and unnumbered (whole disk) devices are always excluded.
125 @param job: The job instance to pass to the partition object
127 @param min_blocks: The minimum number of blocks for a partition to
129 @param filter_func: A callable that returns True if a partition is
130 desired. It will be passed one parameter:
131 The partition name (hdc3, etc.).
132 Some useful filter functions are already defined in this module.
133 @param exclude_swap: If True any partition actively in use as a swap
134 device will be excluded.
135 @param __open: Reserved for unit testing.
137 @return: A list of L{partition} objects.
139 active_swap_devices
= set()
141 for swapline
in open_func('/proc/swaps'):
142 if swapline
.startswith('/'):
143 active_swap_devices
.add(swapline
.split()[0])
146 for partline
in open_func('/proc/partitions').readlines():
147 fields
= partline
.strip().split()
148 if len(fields
) != 4 or partline
.startswith('major'):
150 (major
, minor
, blocks
, partname
) = fields
153 # The partition name better end with a digit, else it's not a partition
154 if not partname
[-1].isdigit():
157 # We don't want the loopback device in the partition list
158 if 'loop' in partname
:
161 device
= partname_to_device(partname
)
162 if exclude_swap
and device
in active_swap_devices
:
163 logging
.debug('Skipping %s - Active swap.' % partname
)
166 if min_blocks
and blocks
< min_blocks
:
167 logging
.debug('Skipping %s - Too small.' % partname
)
170 if filter_func
and not filter_func(partname
):
171 logging
.debug('Skipping %s - Filter func.' % partname
)
174 partitions
.append(partition(job
, device
))
179 def get_mount_info(partition_list
):
181 Picks up mount point information about the machine mounts. By default, we
182 try to associate mount points with UUIDs, because in newer distros the
183 partitions are uniquely identified using them.
186 for p
in partition_list
:
188 uuid
= utils
.system_output('blkid -p -s UUID -o value %s' % p
.device
)
189 except error
.CmdError
:
190 # fall back to using the partition
192 mount_info
.add((uuid
, p
.get_mountpoint()))
197 def filter_partition_list(partitions
, devnames
):
199 Pick and choose which partition to keep.
201 filter_partition_list accepts a list of partition objects and a list
202 of strings. If a partition has the device name of the strings it
203 is returned in a list.
205 @param partitions: A list of L{partition} objects
206 @param devnames: A list of devnames of the form '/dev/hdc3' that
207 specifies which partitions to include in the returned list.
209 @return: A list of L{partition} objects specified by devnames, in the
210 order devnames specified
216 if p
.device
== d
and p
not in filtered_list
:
217 filtered_list
.append(p
)
222 def get_unmounted_partition_list(root_part
, job
=None, min_blocks
=0,
223 filter_func
=None, exclude_swap
=True,
226 Return a list of partition objects that are not mounted.
228 @param root_part: The root device name (without the '/dev/' prefix, example
229 'hda2') that will be filtered from the partition list.
231 Reasoning: in Linux /proc/mounts will never directly mention the
232 root partition as being mounted on / instead it will say that
233 /dev/root is mounted on /. Thus require this argument to filter out
234 the root_part from the ones checked to be mounted.
235 @param job, min_blocks, filter_func, exclude_swap, open_func: Forwarded
236 to get_partition_list().
237 @return List of L{partition} objects that are not mounted.
239 partitions
= get_partition_list(job
=job
, min_blocks
=min_blocks
,
240 filter_func
=filter_func
, exclude_swap
=exclude_swap
, open_func
=open_func
)
243 for part
in partitions
:
244 if (part
.device
!= partname_to_device(root_part
) and
245 not part
.get_mountpoint(open_func
=open_func
)):
246 unmounted
.append(part
)
251 def parallel(partitions
, method_name
, *args
, **dargs
):
253 Run a partition method (with appropriate arguments) in parallel,
254 across a list of partition objects
258 job
= partitions
[0].job
260 if (not hasattr(partition
, method_name
) or
261 not callable(getattr(partition
, method_name
))):
262 err
= "partition.parallel got invalid method %s" % method_name
263 raise RuntimeError(err
)
266 print_args
= list(args
)
267 print_args
+= ['%s=%s' % (key
, dargs
[key
]) for key
in dargs
.keys()]
268 logging
.debug('%s.%s(%s)' % (str(p
), method_name
,
269 ', '.join(print_args
)))
271 def _run_named_method(function
, part
=p
):
272 getattr(part
, method_name
)(*args
, **dargs
)
273 flist
.append((_run_named_method
, ()))
279 Return a list of all available filesystems
281 return [re
.sub('(nodev)?\s*', '', fs
) for fs
in open('/proc/filesystems')]
284 def unmount_partition(device
):
286 Unmount a mounted partition
288 @param device: e.g. /dev/sda1, /dev/hda1
290 p
= partition(job
=None, device
=device
)
291 p
.unmount(record
=False)
294 def is_valid_partition(device
):
296 Checks if a partition is valid
298 @param device: e.g. /dev/sda1, /dev/hda1
300 parts
= get_partition_list(job
=None)
301 p_list
= [ p
.device
for p
in parts
]
308 def is_valid_disk(device
):
310 Checks if a disk is valid
312 @param device: e.g. /dev/sda, /dev/hda
315 for partline
in open('/proc/partitions').readlines():
316 fields
= partline
.strip().split()
317 if len(fields
) != 4 or partline
.startswith('major'):
319 (major
, minor
, blocks
, partname
) = fields
322 if not partname
[-1].isdigit():
323 # Disk name does not end in number, AFAIK
324 # so use it as a reference to a disk
325 if device
.strip("/dev/") == partname
:
331 def run_test_on_partitions(job
, test
, partitions
, mountpoint_func
,
332 tag
, fs_opt
, do_fsck
=True, **dargs
):
334 Run a test that requires multiple partitions. Filesystems will be
335 made on the partitions and mounted, then the test will run, then the
336 filesystems will be unmounted and optionally fsck'd.
338 @param job: A job instance to run the test
339 @param test: A string containing the name of the test
340 @param partitions: A list of partition objects, these are passed to the
342 @param mountpoint_func: A callable that returns a mountpoint given a
344 @param tag: A string tag to make this test unique (Required for control
345 files that make multiple calls to this routine with the same value
347 @param fs_opt: An FsOptions instance that describes what filesystem to make
348 @param do_fsck: include fsck in post-test partition cleanup.
349 @param dargs: Dictionary of arguments to be passed to job.run_test() and
352 # setup the filesystem parameters for all the partitions
354 p
.set_fs_options(fs_opt
)
356 # make and mount all the partitions in parallel
357 parallel(partitions
, 'setup_before_test', mountpoint_func
=mountpoint_func
)
359 mountpoint
= mountpoint_func(partitions
[0])
361 # run the test against all the partitions
362 job
.run_test(test
, tag
=tag
, partitions
=partitions
, dir=mountpoint
, **dargs
)
364 parallel(partitions
, 'unmount') # unmount all partitions in parallel
366 parallel(partitions
, 'fsck') # fsck all partitions in parallel
367 # else fsck is done by caller
370 class partition(object):
372 Class for handling partitions and filesystems
375 def __init__(self
, job
, device
, loop_size
=0, mountpoint
=None):
377 @param job: A L{client.bin.job} instance.
378 @param device: The device in question (e.g."/dev/hda2"). If device is a
379 file it will be mounted as loopback. If you have job config
380 'partition.partitions', e.g.,
381 job.config_set('partition.partitions', ["/dev/sda2", "/dev/sda3"])
382 you may specify a partition in the form of "partN" e.g. "part0",
383 "part1" to refer to elements of the partition list. This is
384 specially useful if you run a test in various machines and you
385 don't want to hardcode device names as those may vary.
386 @param loop_size: Size of loopback device (in MB). Defaults to 0.
388 # NOTE: This code is used by IBM / ABAT. Do not remove.
389 part
= re
.compile(r
'^part(\d+)$')
390 m
= part
.match(device
)
392 number
= int(m
.groups()[0])
393 partitions
= job
.config_get('partition.partitions')
395 device
= partitions
[number
]
397 raise NameError("Partition '" + device
+ "' not available")
400 self
.name
= os
.path
.basename(device
)
402 self
.loop
= loop_size
404 self
.mountpoint
= mountpoint
405 self
.mkfs_flags
= None
406 self
.mount_options
= None
409 cmd
= 'dd if=/dev/zero of=%s bs=1M count=%d' % (device
, loop_size
)
414 return '<Partition: %s>' % self
.device
417 def set_fs_options(self
, fs_options
):
419 Set filesystem options
421 @param fs_options: A L{FsOptions} object
424 self
.fstype
= fs_options
.fstype
425 self
.mkfs_flags
= fs_options
.mkfs_flags
426 self
.mount_options
= fs_options
.mount_options
427 self
.fs_tag
= fs_options
.fs_tag
430 def run_test(self
, test
, **dargs
):
431 self
.job
.run_test(test
, dir=self
.get_mountpoint(), **dargs
)
434 def setup_before_test(self
, mountpoint_func
):
436 Prepare a partition for running a test. Unmounts any
437 filesystem that's currently mounted on the partition, makes a
438 new filesystem (according to this partition's filesystem
439 options) and mounts it where directed by mountpoint_func.
441 @param mountpoint_func: A callable that returns a path as a string,
442 given a partition instance.
444 mountpoint
= mountpoint_func(self
)
446 raise ValueError('Don\'t know where to put this partition')
447 self
.unmount(ignore_status
=True, record
=False)
449 if not os
.path
.isdir(mountpoint
):
450 os
.makedirs(mountpoint
)
451 self
.mount(mountpoint
)
454 def run_test_on_partition(self
, test
, mountpoint_func
, **dargs
):
456 Executes a test fs-style (umount,mkfs,mount,test)
458 Here we unmarshal the args to set up tags before running the test.
459 Tests are also run by first umounting, mkfsing and then mounting
460 before executing the test.
462 @param test: name of test to run
463 @param mountpoint_func: function to return mount point string
465 tag
= dargs
.get('tag')
467 tag
= '%s.%s' % (self
.name
, tag
)
469 tag
= '%s.%s' % (self
.name
, self
.fs_tag
)
473 # If there's a 'suffix' argument, append it to the tag and remove it
474 suffix
= dargs
.pop('suffix', None)
476 tag
= '%s.%s' % (tag
, suffix
)
478 dargs
['tag'] = test
+ '.' + tag
480 def _make_partition_and_run_test(test_tag
, dir=None, **dargs
):
481 self
.setup_before_test(mountpoint_func
)
483 self
.job
.run_test(test
, tag
=test_tag
, dir=mountpoint
, **dargs
)
489 mountpoint
= mountpoint_func(self
)
491 # The tag is the tag for the group (get stripped off by run_group)
492 # The test_tag is the tag for the test itself
493 self
.job
.run_group(_make_partition_and_run_test
,
494 test_tag
=tag
, dir=mountpoint
, **dargs
)
497 def get_mountpoint(self
, open_func
=open, filename
=None):
499 Find the mount point of this partition object.
501 @param open_func: the function to use for opening the file containing
502 the mounted partitions information
503 @param filename: where to look for the mounted partitions information
504 (default None which means it will search /proc/mounts and/or
507 @returns a string with the mount point of the partition or None if not
511 for line
in open_func(filename
).readlines():
513 if parts
[0] == self
.device
or parts
[1] == self
.mountpoint
:
514 return parts
[1] # The mountpoint where it's mounted
517 # no specific file given, look in /proc/mounts
518 res
= self
.get_mountpoint(open_func
=open_func
, filename
='/proc/mounts')
520 # sometimes the root partition is reported as /dev/root in
521 # /proc/mounts in this case, try /etc/mtab
522 res
= self
.get_mountpoint(open_func
=open_func
, filename
='/etc/mtab')
524 # trust /etc/mtab only about /
531 def mkfs_exec(self
, fstype
):
533 Return the proper mkfs executable based on fs
536 if os
.path
.exists('/sbin/mkfs.ext4'):
538 # If ext4 supported e2fsprogs is not installed we use the
539 # autotest supplied one in tools dir which is statically linked"""
540 auto_mkfs
= os
.path
.join(self
.job
.toolsdir
, 'mkfs.ext4dev')
541 if os
.path
.exists(auto_mkfs
):
546 raise NameError('Error creating partition for filesystem type %s' %
550 def mkfs(self
, fstype
=None, args
='', record
=True):
552 Format a partition to filesystem type
554 @param fstype: the filesystem type, e.g.. "ext3", "ext2"
555 @param args: arguments to be passed to mkfs command.
556 @param record: if set, output result of mkfs operation to autotest
560 if list_mount_devices().count(self
.device
):
561 raise NameError('Attempted to format mounted device %s' %
571 args
+= ' ' + self
.mkfs_flags
576 # BAH. Inconsistent mkfs syntax SUCKS.
577 if fstype
.startswith('ext'):
579 elif fstype
== 'reiserfs':
582 # If there isn't already a '-t <type>' argument, add one.
584 args
= "-t %s %s" % (fstype
, args
)
588 mkfs_cmd
= "%s %s %s" % (self
.mkfs_exec(fstype
), args
, self
.device
)
592 # We throw away the output here - we only need it on error, in
593 # which case it's in the exception
594 utils
.system_output("yes | %s" % mkfs_cmd
)
595 except error
.CmdError
, e
:
596 logging
.error(e
.result_obj
)
598 self
.job
.record('FAIL', None, mkfs_cmd
, error
.format_error())
602 self
.job
.record('FAIL', None, mkfs_cmd
, error
.format_error())
606 self
.job
.record('GOOD', None, mkfs_cmd
)
610 def get_fsck_exec(self
):
612 Return the proper mkfs executable based on self.fstype
614 if self
.fstype
== 'ext4':
615 if os
.path
.exists('/sbin/fsck.ext4'):
617 # If ext4 supported e2fsprogs is not installed we use the
618 # autotest supplied one in tools dir which is statically linked"""
619 auto_fsck
= os
.path
.join(self
.job
.toolsdir
, 'fsck.ext4dev')
620 if os
.path
.exists(auto_fsck
):
625 raise NameError('Error creating partition for filesystem type %s' %
629 def fsck(self
, args
='-fy', record
=True):
633 @param args: arguments to filesystem check tool. Default is "-n"
634 which works on most tools.
637 # I hate reiserfstools.
638 # Requires an explit Yes for some inane reason
639 fsck_cmd
= '%s %s %s' % (self
.get_fsck_exec(), self
.device
, args
)
640 if self
.fstype
== 'reiserfs':
641 fsck_cmd
= 'yes "Yes" | ' + fsck_cmd
644 utils
.system_output(fsck_cmd
)
647 self
.job
.record('FAIL', None, fsck_cmd
, error
.format_error())
648 raise error
.TestError('Fsck found errors with the underlying '
652 self
.job
.record('GOOD', None, fsck_cmd
)
655 def mount(self
, mountpoint
=None, fstype
=None, args
='', record
=True):
657 Mount this partition to a mount point
659 @param mountpoint: If you have not provided a mountpoint to partition
660 object or want to use a different one, you may specify it here.
661 @param fstype: Filesystem type. If not provided partition object value
663 @param args: Arguments to be passed to "mount" command.
664 @param record: If True, output result of mount operation to autotest
671 assert(self
.fstype
is None or self
.fstype
== fstype
);
673 if self
.mount_options
:
674 args
+= ' -o ' + self
.mount_options
676 args
+= ' -t ' + fstype
681 if not mountpoint
and not self
.mountpoint
:
682 raise ValueError("No mountpoint specified and no default "
683 "provided to this partition object")
685 mountpoint
= self
.mountpoint
687 mount_cmd
= "mount %s %s %s" % (args
, self
.device
, mountpoint
)
689 if list_mount_devices().count(self
.device
):
690 err
= 'Attempted to mount mounted device'
691 self
.job
.record('FAIL', None, mount_cmd
, err
)
693 if list_mount_points().count(mountpoint
):
694 err
= 'Attempted to mount busy mountpoint'
695 self
.job
.record('FAIL', None, mount_cmd
, err
)
698 mtab
= open('/etc/mtab')
699 # We have to get an exclusive lock here - mount/umount are racy
700 fcntl
.flock(mtab
.fileno(), fcntl
.LOCK_EX
)
703 utils
.system(mount_cmd
)
708 self
.job
.record('FAIL', None, mount_cmd
, error
.format_error())
712 self
.job
.record('GOOD', None, mount_cmd
)
716 def unmount_force(self
):
718 Kill all other jobs accessing this partition. Use fuser and ps to find
719 all mounts on this mountpoint and unmount them.
721 @return: true for success or false for any errors
724 logging
.debug("Standard umount failed, will try forcing. Users:")
726 cmd
= 'fuser ' + self
.get_mountpoint()
728 fuser
= utils
.system_output(cmd
)
730 users
= re
.sub('.*:', '', fuser
).split()
732 m
= re
.match('(\d+)(.*)', user
)
733 (pid
, usage
) = (m
.group(1), m
.group(2))
735 ps
= utils
.system_output('ps -p %s | sed 1d' % pid
)
736 logging
.debug('%s %s %s' % (usage
, pid
, ps
))
739 utils
.system('ls -l ' + self
.device
)
740 umount_cmd
= "umount -f " + self
.device
741 utils
.system(umount_cmd
)
743 except error
.CmdError
:
744 logging
.debug('Umount_force failed for %s' % self
.device
)
749 def unmount(self
, ignore_status
=False, record
=True):
751 Umount this partition.
753 It's easier said than done to umount a partition.
754 We need to lock the mtab file to make sure we don't have any
755 locking problems if we are umounting in paralllel.
757 If there turns out to be a problem with the simple umount we
758 end up calling umount_force to get more agressive.
760 @param ignore_status: should we notice the umount status
761 @param record: if True, output result of umount operation to
765 mountpoint
= self
.get_mountpoint()
767 # It's not even mounted to start with
768 if record
and not ignore_status
:
769 msg
= 'umount for dev %s has no mountpoint' % self
.device
770 self
.job
.record('FAIL', None, msg
, 'Not mounted')
773 umount_cmd
= "umount " + mountpoint
774 mtab
= open('/etc/mtab')
776 # We have to get an exclusive lock here - mount/umount are racy
777 fcntl
.flock(mtab
.fileno(), fcntl
.LOCK_EX
)
780 utils
.system(umount_cmd
)
783 self
.job
.record('GOOD', None, umount_cmd
)
784 except (error
.CmdError
, IOError):
787 # Try the forceful umount
788 if self
.unmount_force():
791 # If we are here we cannot umount this partition
792 if record
and not ignore_status
:
793 self
.job
.record('FAIL', None, umount_cmd
, error
.format_error())
799 Delete all files of a given partition filesystem.
801 wipe_filesystem(self
.job
, self
.get_mountpoint())
804 def get_io_scheduler_list(self
, device_name
):
805 names
= open(self
.__sched
_path
(device_name
)).read()
806 return names
.translate(string
.maketrans('[]', ' ')).split()
809 def get_io_scheduler(self
, device_name
):
810 return re
.split('[\[\]]',
811 open(self
.__sched
_path
(device_name
)).read())[1]
814 def set_io_scheduler(self
, device_name
, name
):
815 if name
not in self
.get_io_scheduler_list(device_name
):
816 raise NameError('No such IO scheduler: %s' % name
)
817 f
= open(self
.__sched
_path
(device_name
), 'w')
822 def __sched_path(self
, device_name
):
823 return '/sys/block/%s/queue/scheduler' % device_name
826 class virtual_partition
:
828 Handles block device emulation using file images of disks.
829 It's important to note that this API can be used only if
830 we have the following programs present on the client machine:
836 def __init__(self
, file_img
, file_size
):
838 Creates a virtual partition, keeping record of the device created
839 under /dev/mapper (device attribute) so test writers can use it
840 on their filesystem tests.
842 @param file_img: Path to the desired disk image file.
843 @param file_size: Size of the desired image in Bytes.
845 logging
.debug('Sanity check before attempting to create virtual '
848 os_dep
.commands('sfdisk', 'losetup', 'kpartx')
849 except ValueError, e
:
850 e_msg
= 'Unable to create virtual partition: %s' % e
851 raise error
.AutotestError(e_msg
)
853 logging
.debug('Creating virtual partition')
854 self
.img
= self
._create
_disk
_img
(file_img
, file_size
)
855 self
.loop
= self
._attach
_img
_loop
(self
.img
)
856 self
._create
_single
_partition
(self
.loop
)
857 self
.device
= self
._create
_entries
_partition
(self
.loop
)
858 logging
.debug('Virtual partition successfuly created')
859 logging
.debug('Image disk: %s', self
.img
)
860 logging
.debug('Loopback device: %s', self
.loop
)
861 logging
.debug('Device path: %s', self
.device
)
866 Removes the virtual partition from /dev/mapper, detaches the image file
867 from the loopback device and removes the image file.
869 logging
.debug('Removing virtual partition - device %s', self
.device
)
870 self
._remove
_entries
_partition
()
871 self
._detach
_img
_loop
()
872 self
._remove
_disk
_img
()
875 def _create_disk_img(self
, img_path
, size
):
877 Creates a disk image using dd.
879 @param img_path: Path to the desired image file.
880 @param size: Size of the desired image in Bytes.
881 @returns: Path of the image created.
883 logging
.debug('Creating disk image %s, size = %d Bytes', img_path
, size
)
885 cmd
= 'dd if=/dev/zero of=%s bs=1024 count=%d' % (img_path
, size
)
887 except error
.CmdError
, e
:
888 e_msg
= 'Error creating disk image %s: %s' % (img_path
, e
)
889 raise error
.AutotestError(e_msg
)
893 def _attach_img_loop(self
, img_path
):
895 Attaches a file image to a loopback device using losetup.
897 @param img_path: Path of the image file that will be attached to a
899 @returns: Path of the loopback device associated.
901 logging
.debug('Attaching image %s to a loop device', img_path
)
904 loop_path
= utils
.system_output(cmd
)
905 cmd
= 'losetup -f %s' % img_path
907 except error
.CmdError
, e
:
908 e_msg
= 'Error attaching image %s to a loop device: %s' % \
910 raise error
.AutotestError(e_msg
)
914 def _create_single_partition(self
, loop_path
):
916 Creates a single partition encompassing the whole 'disk' using cfdisk.
918 @param loop_path: Path to the loopback device.
920 logging
.debug('Creating single partition on %s', loop_path
)
922 single_part_cmd
= '0,,c\n'
923 sfdisk_file_path
= '/tmp/create_partition.sfdisk'
924 sfdisk_cmd_file
= open(sfdisk_file_path
, 'w')
925 sfdisk_cmd_file
.write(single_part_cmd
)
926 sfdisk_cmd_file
.close()
927 utils
.system('sfdisk %s < %s' % (loop_path
, sfdisk_file_path
))
928 except error
.CmdError
, e
:
929 e_msg
= 'Error partitioning device %s: %s' % (loop_path
, e
)
930 raise error
.AutotestError(e_msg
)
933 def _create_entries_partition(self
, loop_path
):
935 Takes the newly created partition table on the loopback device and
936 makes all its devices available under /dev/mapper. As we previously
937 have partitioned it using a single partition, only one partition
940 @param loop_path: Path to the loopback device.
942 logging
.debug('Creating entries under /dev/mapper for %s loop dev',
945 cmd
= 'kpartx -a %s' % loop_path
947 l_cmd
= 'kpartx -l %s | cut -f1 -d " "' % loop_path
948 device
= utils
.system_output(l_cmd
)
949 except error
.CmdError
, e
:
950 e_msg
= 'Error creating entries for %s: %s' % (loop_path
, e
)
951 raise error
.AutotestError(e_msg
)
952 return os
.path
.join('/dev/mapper', device
)
955 def _remove_entries_partition(self
):
957 Removes the entries under /dev/mapper for the partition associated
958 to the loopback device.
960 logging
.debug('Removing the entry on /dev/mapper for %s loop dev',
963 cmd
= 'kpartx -d %s' % self
.loop
965 except error
.CmdError
, e
:
966 e_msg
= 'Error removing entries for loop %s: %s' % (self
.loop
, e
)
967 raise error
.AutotestError(e_msg
)
970 def _detach_img_loop(self
):
972 Detaches the image file from the loopback device.
974 logging
.debug('Detaching image %s from loop device %s', self
.img
,
977 cmd
= 'losetup -d %s' % self
.loop
979 except error
.CmdError
, e
:
980 e_msg
= ('Error detaching image %s from loop device %s: %s' %
981 (self
.img
, self
.loop
, e
))
982 raise error
.AutotestError(e_msg
)
985 def _remove_disk_img(self
):
987 Removes the disk image.
989 logging
.debug('Removing disk image %s', self
.img
)
993 e_msg
= 'Error removing image file %s' % self
.img
994 raise error
.AutotestError(e_msg
)
996 # import a site partition module to allow it to override functions
998 from autotest_lib
.client
.bin
.site_partition
import *