[pkg-eucalyptus-commits] [SCM] managing cloud instances for Eucalyptus branch, master, updated. 3.0.0-alpha3-257-g1da8e3a

Matt Spaulding mspaulding06 at gmail.com
Sun Jun 16 02:31:25 UTC 2013


The following commit has been merged in the master branch:
commit ee9727d6817bd6418dfb1bdef6863046c23c343a
Author: Matt Spaulding <mspaulding06 at gmail.com>
Date:   Tue May 14 14:02:41 2013 -0700

    Fixed issues from gholms code review

diff --git a/euca2ools/commands/bundle/__init__.py b/euca2ools/commands/bundle/__init__.py
index 39fa574..b7e990b 100644
--- a/euca2ools/commands/bundle/__init__.py
+++ b/euca2ools/commands/bundle/__init__.py
@@ -31,7 +31,7 @@
 import argparse
 import os.path
 from euca2ools.commands import Euca2ools
-from euca2ools.commands.argtypes import (delimited_list,
+from euca2ools.commands.argtypes import (delimited_list, filesize,
                                          manifest_block_device_mappings)
 from requestbuilder import Arg
 from requestbuilder.command import BaseCommand
@@ -40,7 +40,7 @@ from requestbuilder.mixins import FileTransferProgressBarMixin
 from requestbuilder.util import set_userregion
 
 
-class BundleCommand(BaseCommand, FileTransferProgressBarMixin):
+class BundleCreator(BaseCommand, FileTransferProgressBarMixin):
     SUITE = Euca2ools
     ARGS = [Arg('-r', '--arch', choices=('i386', 'x86_64', 'armhf'),
                 required=True,
@@ -57,21 +57,24 @@ class BundleCommand(BaseCommand, FileTransferProgressBarMixin):
                 a user and/or region in configuration files'''),
             Arg('--ec2cert', metavar='FILE', help='''file containing the
                 cloud's X.509 certificate'''),
-            Arg('--kernel', metavar='IMAGE', help='''[machine image only] ID
-                of the kernel image to associate with the bundle'''),
-            Arg('--ramdisk', metavar='IMAGE', help='''[machine image only] ID
-                of the ramdisk image to associate with the bundle'''),
+            Arg('--kernel', metavar='IMAGE', help='''ID of the kernel image to
+                associate with the machine bundle'''),
+            Arg('--ramdisk', metavar='IMAGE', help='''ID of the ramdisk image
+                to associate with the machine bundle'''),
             Arg('-B', '--block-device-mappings',
                 metavar='VIRTUAL1=DEVICE1,VIRTUAL2=DEVICE2,...',
                 type=manifest_block_device_mappings,
-                help='''[machine image only] default block device mapping
-                scheme with which to launch instances of this image'''),
+                help='''default block device mapping scheme with which to
+                launch instances of this machine image'''),
             Arg('-d', '--destination', metavar='DIR', help='''location to
                 place the bundle's files (default:  dir named by TMPDIR, TEMP,
                 or TMP environment variables, or otherwise /var/tmp)'''),
+            Arg('--part-size', type=filesize, default=10485760,  # 10m
+                help=argparse.SUPPRESS),
             Arg('--productcodes', metavar='CODE1,CODE2,...',
                 type=delimited_list(','), default=[],
-                help='comma-separated list of product codes')]
+                help='comma-separated list of product codes'),
+            Arg('--batch', action='store_true', help=argparse.SUPPRESS)]
 
     def configure(self):
         BaseCommand.configure(self)
@@ -97,16 +100,11 @@ class BundleCommand(BaseCommand, FileTransferProgressBarMixin):
                 'missing account ID; please supply one with --user')
         self.log.debug('account ID: %s', self.args['user'])
 
-    def process_userregion(self, userregion):
-        if '@' in userregion:
-            user, region = userregion.split('@', 1)
-        else:
-            user = None
-            region = userregion
-        if region and self.config.current_region is None:
-            self.config.current_region = region
-        if user and self.config.current_user is None:
-            self.config.current_user = user
+        if (self.args.get('destination') and
+            os.path.exists(self.args['destination']) and not
+            os.path.isdir(self.args['destination'])):
+            raise ArgumentError("argument -d/--destination: '{0}' is not a "
+                                "directory".format(self.args['destination']))
 
 
 def add_bundle_creds(args, config):
diff --git a/euca2ools/commands/bundle/bundleimage.py b/euca2ools/commands/bundle/bundleimage.py
index 1d2e02c..fd0cd4c 100644
--- a/euca2ools/commands/bundle/bundleimage.py
+++ b/euca2ools/commands/bundle/bundleimage.py
@@ -31,10 +31,7 @@
 import argparse
 import binascii
 import euca2ools
-from euca2ools.commands import Euca2ools
-from euca2ools.commands.argtypes import (delimited_list, filesize,
-                                         manifest_block_device_mappings)
-from euca2ools.commands.bundle import add_bundle_creds
+from euca2ools.commands.bundle import BundleCreator
 from euca2ools.commands.bundle.bundle import Bundle
 from euca2ools.utils import mkdtemp_for_large_files
 import hashlib
@@ -42,80 +39,22 @@ import lxml.etree
 import lxml.objectify
 import os.path
 from requestbuilder import Arg
-from requestbuilder.command import BaseCommand
 from requestbuilder.exceptions import ArgumentError
-from requestbuilder.mixins import FileTransferProgressBarMixin
-from requestbuilder.util import set_userregion
 import subprocess
 
 
-class BundleImage(BaseCommand, FileTransferProgressBarMixin):
+class BundleImage(BundleCreator):
     DESCRIPTION = 'Prepare an image for uploading to a cloud'
-    SUITE = Euca2ools
     ARGS = [Arg('-i', '--image', metavar='FILE', required=True,
                 help='file containing the image to bundle (required)'),
-            Arg('-r', '--arch', choices=('i386', 'x86_64', 'armhf'),
-                required=True,
-                help="the image's processor architecture (required)"),
-            Arg('--region', dest='userregion', metavar='USER at REGION',
-                help='''use encryption keys and the account ID specified for
-                a user and/or region in configuration files'''),
-            Arg('-c', '--cert', metavar='FILE',
-                help='file containing your X.509 certificate.'),
-            Arg('-k', '--privatekey', metavar='FILE', help='''file containing
-                the private key to sign the bundle's manifest with.  This
-                private key will also be required to unbundle the image in
-                the future.'''),
-            Arg('-u', '--user', metavar='ACCOUNT', help='your account ID'),
-            Arg('-d', '--destination', metavar='DIR', help='''location to
-                place the bundle's files (default:  dir named by TMPDIR, TEMP,
-                or TMP environment variables, or otherwise /var/tmp)'''),
             Arg('-p', '--prefix', help='''the file name prefix to give the
                 bundle's files (default: the image's file name)'''),
-            Arg('--ec2cert', metavar='FILE', help='''file containing the
-                cloud's X.509 certificate'''),
-            Arg('--kernel', metavar='IMAGE', help='''[machine image only] ID
-                of the kernel image to associate with the bundle'''),
-            Arg('--ramdisk', metavar='IMAGE', help='''[machine image only] ID
-                of the ramdisk image to associate with the bundle'''),
-            Arg('--block-device-mappings',
-                metavar='VIRTUAL1=DEVICE1,VIRTUAL2=DEVICE2,...',
-                type=manifest_block_device_mappings,
-                help='''[machine image only] default block device mapping
-                scheme with which to launch instances of this image'''),
-            Arg('--productcodes', metavar='CODE1,CODE2,...',
-                type=delimited_list(','),
-                help='comma-separated list of product codes'),
-            Arg('--batch', action='store_true', help=argparse.SUPPRESS),
-            Arg('--part-size', type=filesize, default=10485760,  # 10m
-                help=argparse.SUPPRESS),
             Arg('--image-type', choices=('machine', 'kernel', 'ramdisk'),
                 default='machine', help=argparse.SUPPRESS),
             Arg('--progressbar-label', help=argparse.SUPPRESS)]
 
     def configure(self):
-        BaseCommand.configure(self)
-        set_userregion(self.config, self.args.get('userregion'))
-        set_userregion(self.config, os.getenv('EUCA_REGION'))
-
-        # Get creds
-        add_bundle_creds(self.args, self.config)
-        if not self.args.get('cert'):
-            raise ArgumentError(
-                'missing certificate; please supply one with -c')
-        self.log.debug('certificate: %s', self.args['cert'])
-        if not self.args.get('privatekey'):
-            raise ArgumentError(
-                'missing private key; please supply one with -k')
-        self.log.debug('private key: %s', self.args['privatekey'])
-        if not self.args.get('ec2cert'):
-            raise ArgumentError(
-                'missing cloud certificate; please supply one with --ec2cert')
-        self.log.debug('cloud certificate: %s', self.args['ec2cert'])
-        if not self.args.get('user'):
-            raise ArgumentError(
-                'missing account ID; please supply one with --user')
-        self.log.debug('account ID: %s', self.args['user'])
+        BundleCreator.configure(self)
 
         # kernel/ramdisk image IDs
         if self.args.get('kernel') == 'true':
@@ -136,11 +75,6 @@ class BundleImage(BaseCommand, FileTransferProgressBarMixin):
             if self.args.get('ramdisk') and self.args['ramdisk'] != 'true':
                 raise ArgumentError("argument --ramdisk: not compatible with "
                                     "image type 'ramdisk'")
-        if (self.args.get('destination') and
-            os.path.exists(self.args['destination']) and not
-            os.path.isdir(self.args['destination'])):
-            raise ArgumentError("argument -d/--destination: '{0}' is not a "
-                                "directory".format(self.args['destination']))
 
     def main(self):
         prefix = (self.args.get('prefix') or
@@ -171,17 +105,6 @@ class BundleImage(BaseCommand, FileTransferProgressBarMixin):
             print 'Wrote', part_filename
         print 'Wrote manifest', result[1]  # manifest
 
-    def process_userregion(self, userregion):
-        if '@' in userregion:
-            user, region = userregion.split('@', 1)
-        else:
-            user = None
-            region = userregion
-        if region and self.config.current_region is None:
-            self.config.current_region = region
-        if user and self.config.current_user is None:
-            self.config.current_user = user
-
     def generate_manifest_xml(self, bundle):
         manifest = lxml.objectify.Element('manifest')
 
diff --git a/euca2ools/commands/bundle/bundlevol.py b/euca2ools/commands/bundle/bundlevol.py
index 654585c..2a8b15c 100644
--- a/euca2ools/commands/bundle/bundlevol.py
+++ b/euca2ools/commands/bundle/bundlevol.py
@@ -31,34 +31,36 @@
 # Author: Neil Soman neil at eucalyptus.com
 #         Mitch Garnaat mgarnaat at eucalyptus.com
 
-import argparse
 import copy
 import os
 import sys
 from euca2ools.commands.argtypes import delimited_list, filesize
-from euca2ools.commands.bundle import BundleCommand
+from euca2ools.commands.bundle import BundleCreator
+from euca2ools.commands.bundle.bundle import Bundle
 from euca2ools.commands.bundle.bundleimage import BundleImage
 from euca2ools.commands.bundle.helpers import (check_metadata, get_metadata,
                                                get_metadata_dict,
                                                get_metadata_list)
-from euca2ools.commands.bundle.imagecreator import ImageCreator, IMAGE_MAX_SIZE
+from euca2ools.commands.bundle.imagecreator import ImageCreator
 from requestbuilder import Arg, MutuallyExclusiveArgList
 from requestbuilder.command import BaseCommand
 from requestbuilder.exceptions import ServerError
 
 
-IMAGE_MAX_SIZE_IN_MB = IMAGE_MAX_SIZE / 1024 // 1024
+IMAGE_MAX_SIZE_IN_MB = Bundle.EC2_IMAGE_SIZE_LIMIT / 1024 // 1024
 #
 # We pass our args dict along to BundleImage so we need to remove all the
 # args that it doesn't understand.
 #
 BUNDLE_IMAGE_ARG_FILTER = ('generate_fstab', 'fstab', 'bundle_all_dirs',
-                           'filter', 'inherit', 'size', 'volume',
-                           'exclude', 'include', 'ancestor_image_ids')
+                           'filter', 'no_inherit', 'inherit', 'size',
+                           'volume', 'exclude', 'include',
+                           'ancestor_image_ids')
 
 
-class BundleVol(BundleCommand):
-    DESCRIPTION = 'Bundles an image for use with Eucalyptus or Amazon EC2.'
+class BundleVol(BundleCreator):
+    DESCRIPTION = '''Bundle an image for use with Eucalyptus or Amazon EC2
+                  (requires superuser privileges).'''
     ARGS = [Arg('-s', '--size', metavar='MB',
                 type=filesize, default=IMAGE_MAX_SIZE_IN_MB,
                 help='''Size of the image in MB (default: {0}; recommended
@@ -70,12 +72,12 @@ class BundleVol(BundleCommand):
                 help='''Bundle all directories (including mounted
                 filesystems).'''),
             MutuallyExclusiveArgList(
-                Arg('--no-inherit', dest='inherit', default=False,
-                    action='store_false', help='''Do not add instance metadata to
-                    the bundled image (use the --inherit option to explicitly
-                    inherit instance metadata).'''),
-                Arg('--inherit', dest='inherit', default=True,
-                    action='store_true', help=argparse.SUPPRESS)),
+                Arg('--no-inherit', dest='no_inherit', action='store_true',
+                    help='''Do not add instance metadata to the bundled image
+                    (defaults to inherting metadata).'''),
+                Arg('--inherit', dest='inherit', action='store_true',
+                    help='''Explicity inhert instance metadata and add it to
+                    the bundled image (this is the default behavior)''')),
             Arg('-i', '--include', metavar='FILE1,FILE2,...',
                 type=delimited_list(','), help='''Comma-separated list of
                 absolute file paths to include.'''),
@@ -91,13 +93,13 @@ class BundleVol(BundleCommand):
                 Arg('--fstab', metavar='PATH', help='''Path to the fstab to be
                     bundled with image.'''),
                 Arg('--generate-fstab', default=False, action='store_true',
-                    help='Generate fstab to bundle in image.')),
-            Arg('--batch',
-                help='Run in batch mode.  For compatibility has no effect')]
+                    help='Generate fstab to bundle in image.'))]
 
     def __init__(self, **kwargs):
         #
-        # Do root check before anything else happens
+        # We want to do this before arguments to the command are processed.
+        # Users should be informed if they don't have sufficient privileges
+        # before being told about missing required arguments.
         #
         if os.geteuid() != 0:
             raise Exception("must be root user to run euca-bundle-vol.")
@@ -129,8 +131,10 @@ class BundleVol(BundleCommand):
             # aren't always there.
             #
             try:
-                self.args['productcodes'].extend(
-                    get_metadata_list('product-codes'))
+                productcodes = get_metadata_list('product-codes')
+                self.args['productcodes'].extend(productcodes)
+                self.log.debug("inheriting product codes: {0}"
+                               .format(productcodes))
             except ServerError:
                 msg = 'unable to read product codes from metadata.'
                 print sys.stderr, msg
@@ -138,10 +142,12 @@ class BundleVol(BundleCommand):
             try:
                 if not self.args.get('ancestor_image_ids'):
                     self.args['ancestor_image_ids'] = []
-                self.args['ancestor_image_ids'].extend(
-                    get_metadata_list('ancestor-ami-ids'))
+                ancestor_ids = get_metadata_list('ancestor-ami-ids')
+                self.args['ancestor_image_ids'].extend(ancestor_ids)
+                self.log.debug("inheriting ancestor ids: {0}"
+                               .format(ancestor_ids))
             except ServerError:
-                msg = 'unable to read ancestor ids.'
+                msg = 'unable to read ancestor ids from metadata.'
                 print sys.stderr, msg
                 self.log.warn(msg)
         except ServerError:
@@ -163,11 +169,11 @@ class BundleVol(BundleCommand):
         return args
 
     def configure(self):
-        BundleCommand.configure(self)
+        BundleCreator.configure(self)
         self.args['user'] = self.args.get('user').replace('-', '')
 
     def main(self):
-        if self.args.get('inherit') is True:
+        if self.args.get('inherit') or not self.args.get('no_inherit'):
             self._inherit_metadata()
         
         image_file = ImageCreator(log=self.log, **self.args).run()
diff --git a/euca2ools/commands/bundle/helpers.py b/euca2ools/commands/bundle/helpers.py
index 94e388e..7d64eca 100644
--- a/euca2ools/commands/bundle/helpers.py
+++ b/euca2ools/commands/bundle/helpers.py
@@ -113,9 +113,10 @@ def get_metadata(*paths):
         url = urljoin(url, "/".join(paths))
 
     try:
-        response = requests.get(url)
+        response = requests.get(url, timeout=METADATA_TIMEOUT)
     except Timeout:
-        raise ClientError("timeout occurred when getting instance metadata.")
+        raise ClientError("timeout occurred when getting metadata from {0}"
+                          .format(url))
 
     if response.ok:
         return response.content
@@ -140,4 +141,4 @@ def get_metadata_dict(*paths):
     """
     items = get_metadata_list(*paths)
     return dict((item, get_metadata(*(list(paths) + [item]))) \
-			for item in items)
+                    for item in items)
diff --git a/euca2ools/commands/bundle/imagecreator.py b/euca2ools/commands/bundle/imagecreator.py
index f3f717a..0d4186f 100644
--- a/euca2ools/commands/bundle/imagecreator.py
+++ b/euca2ools/commands/bundle/imagecreator.py
@@ -29,36 +29,36 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
-from euca2ools.exceptions import CommandFailed, UnsupportedException
-from euca2ools.utils import execute, check_command, sanitize_path
+from euca2ools.utils import sanitize_path
 from euca2ools.utils import mkdtemp_for_large_files as mkdtemp
-from requestbuilder.exceptions import ArgumentError
+import glob
 import os
 import sys
 import platform
 import shutil
 import stat
+import subprocess
 import time
 
 
 NO_EXCLUDE_ENVAR = 'EUCA_BUNDLE_VOL_EMPTY_EXCLUDES'
 BLKID_TAGS = ('LABEL', 'TYPE', 'UUID')
-ALLOWED_FS_TYPES = ('ext2', 'ext3', 'xfs', 'jfs', 'reiserfs')
+ALLOWED_FS_TYPES = ('ext2', 'ext3', 'ext4', 'xfs', 'jfs', 'reiserfs')
 EXCLUDED_DIRS = ('/dev', '/media', '/mnt', '/proc',
                  '/sys', '/cdrom', '/tmp')
 SYSTEM_DIRS = ('proc', 'tmp', 'dev', 'mnt', 'sys')
-DEVICE_NODES = (('dev/console', 'c', '5', '1'),
-                ('dev/full', 'c', '1', '7'),
-                ('dev/null', 'c', '1', '3'),
-                ('dev/zero', 'c', '1', '5'),
-                ('dev/tty', 'c', '5', '0'),
-                ('dev/tty0', 'c', '4', '0'),
-                ('dev/tty1', 'c', '4', '1'),
-                ('dev/tty2', 'c', '4', '2'),
-                ('dev/tty3', 'c', '4', '3'),
-                ('dev/tty4', 'c', '4', '4'),
-                ('dev/tty5', 'c', '4', '5'),
-                ('dev/xvc0', 'c', '204', '191'))
+DEVICE_NODES = {'dev/console': ['c', '5', '1'],
+                'dev/full': ['c', '1', '7'],
+                'dev/null': ['c', '1', '3'],
+                'dev/zero': ['c', '1', '5'],
+                'dev/tty': ['c', '5', '0'],
+                'dev/tty0': ['c', '4', '0'],
+                'dev/tty1': ['c', '4', '1'],
+                'dev/tty2': ['c', '4', '2'],
+                'dev/tty3': ['c', '4', '3'],
+                'dev/tty4': ['c', '4', '4'],
+                'dev/tty5': ['c', '4', '5'],
+                'dev/xvc0': ['c', '204', '191']}
 FSTAB_BODY_TEMPLATE = dict(
     i386="""/dev/sda1\t/\text3\tdefaults 1 1
 /dev/sdb\t/mnt\text3\tdefaults 0 0
@@ -95,14 +95,13 @@ DEFAULT_PATTERN_EXCLUDES = [
     '*/.ssh/authorized_keys',
     '*/.bash_history',
 ]
-DEFAULT_FS_EXCLUDES = [
-    '/dev',
-    '/media',
-    '/mnt',
-    '/proc',
-    '/sys',
-]
-IMAGE_MAX_SIZE = 10 * 1024 * 1024 * 1024 # 10GB Max Size in bytes
+#
+# We're using /etc/mtab since this is what the AWS tools do. Other tools like
+# df also read the /etc/mtab file for mounts. Even though /proc/mounts is
+# more up to date, we'll stick with this unless there's a good reason to
+# change it.
+#
+MOUNTS_FILE = '/etc/mtab'
 
 
 class VolumeSync(object):
@@ -163,7 +162,7 @@ class VolumeSync(object):
 
     def install_fstab_from_file(self, fstab):
         if not os.path.exists(fstab):
-            raise ArgumentError(
+            raise ValueError(
                 "fstab file '{0}' does not exist.".format(fstab))
         self.fstab = fstab
 
@@ -187,7 +186,7 @@ class VolumeSync(object):
         if self.mpoint.find(self.volume) == 0:
             self.exclude(self.mpoint)
         if not self.bundle_all_dirs:
-            self._add_mtab_exclusions()
+            self._add_mounts_exclusions()
         self.excludes.extend(EXCLUDED_DIRS)
         if self.filter:
             self.excludes.extend(DEFAULT_PATTERN_EXCLUDES)
@@ -196,13 +195,13 @@ class VolumeSync(object):
                 ['/etc/udev/rules.d/70-persistent-net.rules',
                  '/etc/udev/rules.d/z25_persistent-net.rules'])
 
-    def _add_mtab_exclusions(self):
+    def _add_mounts_exclusions(self):
         """Exclude locations from the volume rsync based on whether we are
         allowed to sync the type of filesystem. If you have chosen to bundle
         all using '--all' then this will not get called.
         """
-        with open('/etc/mtab', 'r') as mtab:
-            for line in mtab.readlines():
+        with open(MOUNTS_FILE, 'r') as mounts:
+            for line in mounts.readlines():
                 (mount, type) = line.split()[1:3]
                 #
                 # If we find that a mount in our volume's mtab file is
@@ -219,8 +218,8 @@ class VolumeSync(object):
         """Find all tmpfs mounts on our volume and make sure that they are
         created on the image.
         """
-        with open('/etc/mtab', 'r') as mtab:
-            for line in mtab.readlines():
+        with open(MOUNTS_FILE, 'r') as mounts:
+            for line in mounts.readlines():
                 (mount, type) = line.split()[1:3]
                 if type == 'tmpfs':
                     fullpath = os.path.join(self.mpoint, mount[1:])
@@ -229,11 +228,9 @@ class VolumeSync(object):
 
     def _populate_device_nodes(self):
         """Populate the /dev directory in our image with common device nodes."""
-        template = 'mknod {0} {1} {2} {3}'
-        for node in DEVICE_NODES:
-            cmd = template.format(os.path.join(self.mpoint, node[0]),
-                                  *node[1:])
-            execute(cmd, log=self.log)
+        for node, args in DEVICE_NODES.iteritems():
+            subprocess.check_call(['mknod',
+                                   os.path.join(self.mpoint, node)] + args)
 
     def _populate_system_dirs(self):
         """Populate our image with common system directories."""
@@ -256,44 +253,44 @@ class VolumeSync(object):
             fp.write(content)
 
     def _sync_files(self):
-        check_command('rsync')
         cmd = ['rsync', '-aXS']
         self._update_exclusions()
         for exclude in self.excludes:
             cmd.extend(['--exclude', exclude])
         for include in self.includes:
             cmd.extend(['--include', include])
-        cmd.extend([os.path.join(self.volume, '*'), self.mpoint])
-        (out, _, retval) = execute(cmd, shell=True, raise_exception=False,
-                                   log=self.log)
-
-        #
-        # rsync return code 23: Partial transfer due to error
-        # rsync return code 24: Partial transfer due to vanished source files
-        #
-        if retval in (23, 24):
+        cmd.extend(glob.glob(os.path.join(self.volume, '*')))
+        cmd.append(self.mpoint + os.path.sep)
+        
+        try:
             if self.log:
-                self.log.warn('rsync reports files partially copied.')
-            print sys.stderr, "Warning: rsync reports files partially copied."
-        elif retval != 0:
-            raise Exception('rsync failed with return code {0}.'.format(retval))
-
-    def _sync_disks(self):
-        execute('sync', log=self.log)
+                self.log.debug('executing {0}'.format(cmd))
+            subprocess.check_call(cmd)
+        except subprocess.CalledProcessError as err:
+            #
+            # rsync return code 23: Partial transfer due to error
+            # rsync return code 24: Partial transfer due to missing source files
+            #
+            if err.returncode in (23, 24):
+                if self.log:
+                    self.log.warn('rsync reports files partially copied.')
+                print sys.stderr, "Warning: rsync reports files partially copied."
+            else:
+                raise
 
     def mount(self):
-        self._sync_disks()
+        subprocess.check_call('sync')
         if not os.path.exists(self.mpoint):
             os.makedirs(self.mpoint)
-        check_command('mount')
-        cmd = ['mount', '-o', 'loop', self.image, self.mpoint]
-        execute(cmd, log=self.log)
+        if self.log:
+            self.log.debug("mounting {0}".format(self.mpoint))
+        subprocess.check_call(['mount', '-o', 'loop', self.image, self.mpoint])
 
     def unmount(self):
-        self._sync_disks()
-        check_command('umount')
-        cmd = ['umount', '-d', self.mpoint]
-        execute(cmd, log=self.log)
+        subprocess.check_call('sync')
+        if self.log:
+            self.log.debug("unmounting {0}".format(self.mpoint))
+        subprocess.check_call(['umount', '-d', self.mpoint])
         if os.path.exists(self.mpoint):
             os.rmdir(self.mpoint)
 
@@ -329,17 +326,17 @@ class ImageCreator(object):
         # Validate settings
         #
         if not self.volume:
-            raise ArgumentError("must supply a source volume.")
+            raise ValueError("must supply a source volume.")
         self.volume = sanitize_path(self.volume)
         if not self.size:
-            raise ArgumentError("must supply a size for the generated image.")
+            raise ValueError("must supply a size for the generated image.")
         if not self.prefix:
-            raise ArgumentError("must supply a prefix.")
+            raise ValueError("must supply a prefix.")
         if not self.volume:
-            raise ArgumentError("must supply a volume.")
+            raise ValueError("must supply a volume.")
         if not (os.path.exists(self.destination) or \
                     os.path.isdir(self.destination)):
-            raise ArgumentError("'{0}' is not a directory or does not exist."
+            raise ValueError("'{0}' is not a directory or does not exist."
                                 .format(self.destination))
 
     def run(self):
@@ -350,8 +347,7 @@ class ImageCreator(object):
         self._create_raw_diskimage()
         self._populate_filesystem_info()
         self._make_filesystem(**self.fs)
-        sys.stderr.write(" done\n")
-        sys.stderr.flush()
+        print >> sys.stderr, " done"
         #
         # Inside the VolumeSync context we will mount our image
         # as a loop device. If for any reason a failure occurs
@@ -370,16 +366,15 @@ class ImageCreator(object):
             volsync.exclude(self.excludes)
             volsync.include(self.includes)
             volsync.run()
-        sys.stderr.write(" done\n")
-        sys.stderr.flush()
+        print >> sys.stderr, " done"
 
         return self.image
 
     def _create_raw_diskimage(self):
         """Create a sparse raw image file."""
-        template = 'dd if=/dev/zero of={0} count=1 bs=1M seek={1}'
-        cmd = template.format(self.image, self.size - 1)
-        execute(cmd, log=self.log)
+        subprocess.check_call(['dd', 'if=/dev/zero',
+                               'of={0}'.format(self.image), 'count=1', 'bs=1M',
+                               'seek={0}'.format(self.size - 1)])
 
     def _populate_filesystem_info(self):
         """Create a temporary device node for the volume we're going
@@ -390,15 +385,16 @@ class ImageCreator(object):
         devid = os.makedev(os.major(st_dev), os.minor(st_dev))
         directory = mkdtemp(prefix='devnode-')
         devnode = os.path.join(directory, 'rootdev')
-        os.mknod(devnode, 0400 | stat.S_IFBLK, devid)
-        template = 'blkid -s {0} -ovalue {1}'
+        os.mknod(devnode, 0o400 | stat.S_IFBLK, devid)
         try:
             for tag in BLKID_TAGS:
-                cmd = template.format(tag, devnode)
                 try:
-                    (out, _, _) = execute(cmd, log=self.log)
+                    out = subprocess.Popen(['blkid', '-s', tag,
+                                            '-ovalue', devnode],
+                                           stdout=subprocess.PIPE
+                                           ).communicate()[0]
                     self.fs[tag.lower()] = out.rstrip()
-                except CommandFailed:
+                except subprocess.CalledProcessError:
                     pass
         finally:
             os.remove(devnode)
@@ -423,17 +419,16 @@ class ImageCreator(object):
         elif type == 'btrfs':
             mkfs = [mkfs_cmd, self.image]
             if uuid:
-                raise UnsupportedException("btrfs with uuid not supported")
+                raise Exception("btrfs with uuid not supported")
         else:
-            raise UnsupportedException("unsupported fs {0}".format(type))
+            raise Exception("unsupported fs {0}".format(type))
 
         if label:
             mkfs.extend(['-L', label])
-        check_command(mkfs)
-        execute(mkfs, log=self.log)
+        subprocess.check_call(mkfs)
+
         if tunefs:
-            check_command(tunefs)
-            execute(tunefs, log=self.log)
+            subprocess.check_call(tunefs)
 
 
 def _generate_fstab_content(arch=platform.machine()):
@@ -449,5 +444,4 @@ def _generate_fstab_content(arch=platform.machine()):
                 time.strftime(FSTAB_TIME_FORMAT)),
                          FSTAB_BODY_TEMPLATE.get(arch)])
     else:
-        raise UnsupportedException(
-            "platform architecture {0} not supported".format(arch))
+        raise Exception("platform architecture {0} not supported".format(arch))
diff --git a/euca2ools/utils.py b/euca2ools/utils.py
index 7b03ff0..e05295c 100644
--- a/euca2ools/utils.py
+++ b/euca2ools/utils.py
@@ -33,90 +33,17 @@
 
 import base64
 import os
-from subprocess import Popen, PIPE
 import sys
 import tempfile
 from euca2ools import exceptions, __version__
 
 
-def execute(command, raise_exception=True, exception=None, success=0,
-            shell=False, log=None):
-    """Execute a process with optional arguments.
-    Returns a tuple containing stdout, stderr and return code.
-    :param command: A string or list of arguments to execute.
-    :param raise_exception: (optional) True if we should throw an exception
-    when the returncode is not equal to the success value.
-    :param exception: (optional) Exception object to use when raising an
-    exception. If this is not set a CommandFailed exception will be used.
-    :param success: (optional) Set the returncode that signifies success.
-    :param shell: (optional) Tell Popen to use a shell to execute the command.
-    :param log: (optional) Logger to use when logging debug information.
-    This is generally a security risk, so only do it when necessary with
-    trusted input.
-    """
-    #
-    # If we're using a shell we have to provide Popen a string to execute,
-    # otherwise Popen wants a list.
-    #
-    if shell is True:
-        if isinstance(command, list):
-            command = " ".join(command)
-    else:
-        if isinstance(command, basestring):
-            command = command.split()
-    if log:
-        log.debug("executing command: {0}".format(command))
-    proc = Popen(command, stdout=PIPE, stderr=PIPE, shell=shell)
-    (out, err) = proc.communicate()
-    if log:
-        log.debug("command retval: {0}".format(proc.returncode))
-    if proc.returncode != success:
-        if raise_exception:
-            if exception:
-                raise exception
-            else:
-                if isinstance(command, list):
-                    command = " ".join(command)
-                raise exceptions.CommandFailed(command, err)
-    return (out, err, proc.returncode)
-
-
-def check_command(command, **kwargs):
-    """Check if an executable exists on the current system. If an exception
-    is not raised, it is assumed that the command exists.
-    :param command: The executable to check existence of.
-    :param kwargs: (optional) Arguments to pass along to the execute method.
-    """
-    try:
-        if isinstance(command, basestring):
-            command = command.split()
-        if command:
-            #
-            # We don't want to raise a CommandFailed exception here.
-            # We only care if we get an OSError exception, which means the
-            # executable doesn't exist.
-            #
-            kwargs.update(raise_exception=False)
-            execute(command[0], **kwargs)
-        else:
-            raise ArgumentError("No executable supplied to check command.")
-    except OSError as err:
-        if 'No such' in err.message:
-            print >> sys.stderr, 'Command {0} not found. Is it installed?'.format(command)
-            raise exceptions.NotFoundError
-        else:
-            raise
-
-
 def sanitize_path(path):
     """Make a fully expanded and absolute path for us to work with.
     Returns a santized path string.
     :param path: The path string to sanitize.
     """
-    return os.path.abspath(
-        os.path.expandvars(
-            os.path.expanduser(
-                os.path.normpath(path))))
+    return os.path.abspath(os.path.expandvars(os.path.expanduser(path)))
 
 
 def parse_config(config, dict, keylist):

-- 
managing cloud instances for Eucalyptus



More information about the pkg-eucalyptus-commits mailing list