[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