Данило Шеган has proposed merging lp:~danilo/vmbuilder/bug-536940 into 
lp:vmbuilder.

Requested reviews:
  VMBuilder (vmbuilder)
Related bugs:
  #536940 -tmpfs=- option broken in 0.12.2-0ubuntu3
  https://bugs.launchpad.net/bugs/536940


= Bug 536940 =

So, this re-introduces a --tmpfs and --tmp options.  I'm not entirely sure of 
the syntax for --tmpfs, so I implemented what seems to have been the idea 
according to the documentation and old web site snippets I found.  I'd probably 
prefer it if it was two options ("--use-tmpfs" and "--tmpfs-size"), but I don't 
have the time to implement that.

I've also simplified util.tmpfile and util.tmpdir functions: they were always 
called with the same value of 'keep' parameter. I'd be either leaning on 
removing them altogether, or making them useful for something (like providing a 
standard vmbuilder prefix like "vmbuilder" :): they are now single-line calls 
to tempfile module, and we should just use it directly then.

I've done a few more cleanups of my own code as well since I first written it 
up: I've removed catching of VMBuilderUserError exception because a run_cmd 
exception will contain much more useful information than my error message in 
util.set_up_tmpfs and util.clean_up_tmpfs.

There is also one thing I am unsure about: I'm unmounting a tmpfs even when an 
error occurs, but that means that you can't go into a chroot and figure out 
what went wrong.  I found that more useful (and when I did hit a problem that 
needed me to go into chroot directly, and wasn't just an error on my part, I 
simply ran it without --tmpfs option), but I can easily be convinced the 
opposite.

FWIW, that sys.exit(1) call is removed simply because it's never executed with 
a raise just before it.

Note: I won't have much time to work on this.  For instance, to actually make 
tmpfile calls safer, we'd at least do the following: actually create a file 
using tempfile.mkstemp(), and remove it only just prior to doing the operation 
which is going to write to that file.  That wouldn't remove the risks, would 
just make them much, much smaller.  Of course, I understand that vmbuilder is 
not designed to be run on machines with untrusted users, so even if I had time 
to spend on it, I don't think it'd be worthwhile.

-- 
https://code.launchpad.net/~danilo/vmbuilder/bug-536940/+merge/27290
Your team VMBuilder is requested to review the proposed merge of 
lp:~danilo/vmbuilder/bug-536940 into lp:vmbuilder.
=== modified file 'VMBuilder/contrib/cli.py'
--- VMBuilder/contrib/cli.py	2010-04-07 19:47:58 +0000
+++ VMBuilder/contrib/cli.py	2010-06-10 17:22:23 +0000
@@ -22,6 +22,7 @@
 import pwd
 import shutil
 import sys
+import tempfile
 import VMBuilder
 import VMBuilder.util as util
 from   VMBuilder.disk import parse_size
@@ -32,6 +33,7 @@
     arg = 'cli'
 
     def main(self):
+        tmpfs_mount_point = None
         try:
             optparser = optparse.OptionParser()
 
@@ -49,6 +51,19 @@
             group.add_option('--destdir', '-d', type='str', help='Destination directory')
             group.add_option('--only-chroot', action='store_true', help="Only build the chroot. Don't install it on disk images or anything.")
             group.add_option('--existing-chroot', help="Use existing chroot.")
+            group.add_option(
+                '--tmp', '-t', metavar='DIR', dest='tmp_root',
+                default=tempfile.gettempdir(),
+                help=(
+                    'Use TMP as temporary working space for image generation. '
+                    'Defaults to $TMPDIR if it is defined or /tmp otherwise. '
+                    '[default: %default]'))
+            group.add_option(
+                '--tmpfs', metavar="SIZE",
+                help=(
+                    'Use a tmpfs as the working directory, specifying '
+                    'its size or "-" to use tmpfs default '
+                    '(suid,dev,size=1G).'))
             optparser.add_option_group(group)
 
             group = optparse.OptionGroup(optparser, 'Disk')
@@ -105,7 +120,16 @@
                 distro.set_chroot_dir(self.options.existing_chroot)
                 distro.call_hooks('preflight_check')
             else:
-                chroot_dir = util.tmpdir()
+                if self.options.tmpfs is not None:
+                    if str(self.options.tmpfs) == '-':
+                        tmpfs_size = 1024
+                    else:
+                        tmpfs_size = int(self.options.tmpfs)
+                    tmpfs_mount_point = util.set_up_tmpfs(
+                        tmp_root=self.options.tmp_root, size=tmpfs_size)
+                    chroot_dir = tmpfs_mount_point
+                else:
+                    chroot_dir = util.tmpdir(tmp_root=self.options.tmp_root)
                 distro.set_chroot_dir(chroot_dir)
                 distro.build_chroot()
 
@@ -123,12 +147,14 @@
             # and if we reach here, it means the user didn't pass
             # --only-chroot. Hence, we need to remove it to clean
             # up after ourselves.
-            if chroot_dir:
+            if chroot_dir is not None:
                 util.run_cmd('rm', '-rf', '--one-file-system', chroot_dir)
         except VMBuilderException, e:
             logging.error(e)
             raise
-            sys.exit(1)
+        finally:
+            if tmpfs_mount_point is not None:
+                util.clean_up_tmpfs(tmpfs_mount_point)
 
     def fix_ownership(self, filename):
         """
@@ -195,19 +221,19 @@
             swapsize = parse_size(self.options.swapsize)
             optsize = parse_size(self.options.optsize)
             if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
-                tmpfile = util.tmpfile(keep=False)
+                tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
                 hypervisor.add_filesystem(filename=tmpfile, size='%dM' % rootsize, type='ext3', mntpnt='/')
-                tmpfile = util.tmpfile(keep=False)
+                tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
                 hypervisor.add_filesystem(filename=tmpfile, size='%dM' % swapsize, type='swap', mntpnt=None)
                 if optsize > 0:
-                    tmpfile = util.tmpfile(keep=False)
+                    tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
                     hypervisor.add_filesystem(filename=tmpfile, size='%dM' % optsize, type='ext3', mntpnt='/opt')
             else:
                 if self.options.raw:
                     disk = hypervisor.add_disk(filename=self.options.raw)
                 else:
                     size = rootsize + swapsize + optsize
-                    tmpfile = util.tmpfile(keep=False)
+                    tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
                     disk = hypervisor.add_disk(tmpfile, size='%dM' % size)
                 offset = 0
                 disk.add_part(offset, rootsize, default_filesystem, '/')
@@ -222,7 +248,8 @@
                 try:
                     for line in file(self.options.part):
                         elements = line.strip().split(' ')
-                        tmpfile = util.tmpfile(keep=False)
+                        tmpfile = util.tmp_filename(
+                            tmp_root=self.options.tmp_root)
                         if elements[0] == 'root':
                             hypervisor.add_filesystem(elements[1], default_filesystem, filename=tmpfile, mntpnt='/')
                         elif elements[0] == 'swap':
@@ -256,10 +283,12 @@
 
                 except IOError, (errno, strerror):
                     hypervisor.optparser.error("%s parsing --part option: %s" % (errno, strerror))
-    
+
     def do_disk(self, hypervisor, curdisk, size):
         default_filesystem = hypervisor.distro.preferred_filesystem()
-        disk = hypervisor.add_disk(util.tmpfile(keep=False), size+1)
+        disk = hypervisor.add_disk(
+            util.tmp_filename(tmp_root=self.options.tmp_root),
+            size+1)
         logging.debug("do_disk - size: %d" % size)
         offset = 0
         for pair in curdisk:

=== modified file 'VMBuilder/util.py'
--- VMBuilder/util.py	2010-02-25 23:41:18 +0000
+++ VMBuilder/util.py	2010-06-10 17:22:23 +0000
@@ -168,18 +168,36 @@
     logging.debug('No such method')
     return
 
-def tmpfile(suffix='', keep=True):
-    (fd, filename) = tempfile.mkstemp(suffix=suffix)
-    os.close(fd)
-    if not keep:
-        os.unlink(filename)
-    return filename
-
-def tmpdir(suffix='', keep=True):
-    dir = tempfile.mkdtemp(suffix=suffix)
-    if not keep:
-        os.rmdir(dir)
-    return dir
+def tmp_filename(suffix='', tmp_root=None):
+    # There is a risk in using tempfile.mktemp(): it's not recommended
+    # to run vmbuilder on machines with untrusted users.
+    return tempfile.mktemp(suffix=suffix, dir=tmp_root)
+
+def tmpdir(suffix='', tmp_root=None):
+    return tempfile.mkdtemp(suffix=suffix, dir=tmp_root)
+
+def set_up_tmpfs(tmp_root=None, size=1024):
+    """Sets up a tmpfs storage under `tmp_root` with the size of `size` MB.
+
+    `tmp_root` defaults to tempfile.gettempdir().
+    """
+    mount_point = tmpdir('tmpfs', tmp_root)
+    mount_cmd = ["mount", "-t", "tmpfs",
+                 "-o", "size=%dM,mode=0770" % int(size),
+                 "tmpfs", mount_point ]
+    logging.info('Mounting tmpfs under %s' % mount_point)
+    logging.debug('Executing: %s' % mount_cmd)
+    run_cmd(*mount_cmd)
+
+    return mount_point
+
+def clean_up_tmpfs(mount_point):
+    """Unmounts a tmpfs storage under `mount_point`."""
+    umount_cmd = ["umount", "-t", "tmpfs", mount_point ]
+    logging.info('Unmounting tmpfs from %s' % mount_point)
+    logging.debug('Executing: %s' % umount_cmd)
+    run_cmd(*umount_cmd)
+
 
 def get_conf_value(context, confparser, key):
     confvalue = None

_______________________________________________
Mailing list: https://launchpad.net/~vmbuilder
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~vmbuilder
More help   : https://help.launchpad.net/ListHelp

Reply via email to