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

Garrett Holmstrom gholms at fedoraproject.org
Sun Jun 16 02:30:02 UTC 2013


The following commit has been merged in the master branch:
commit 39e2192750afedc8a60ffe5d19df10ea94ddb174
Merge: 93fc062a48d20d4958a8be78f86fab05be2c3ccf f766711d6f9b2f0e834ba346ed8488f39ac1d3c4
Author: Garrett Holmstrom <gholms at fedoraproject.org>
Date:   Sat Feb 2 21:25:09 2013 -0800

    Merge branch 'master' into requestbuilder
    
    Conflicts:
    	euca2ools/commands/euca/bundleinstance.py
    	euca2ools/commands/euca/describeimages.py
    	euca2ools/commands/euca/describesnapshots.py
    	euca2ools/commands/euca/describevolumes.py
    	euca2ools/commands/euca/getconsoleoutput.py
    	euca2ools/commands/euca/rebootinstances.py
    	euca2ools/commands/euca/register.py
    	euca2ools/commands/euca/runinstances.py
    	euca2ools/commands/euca/startinstances.py
    	euca2ools/commands/euca/stopinstances.py
    	euca2ools/commands/euca/terminateinstances.py

diff --combined euca2ools/commands/bundle/bundlevol.py
index f6452d0,9da891c..9da891c
--- a/euca2ools/commands/bundle/bundlevol.py
+++ b/euca2ools/commands/bundle/bundlevol.py
@@@ -118,7 -118,7 +118,7 @@@ class BundleVol(euca2ools.commands.euca
          if os.geteuid() == 0:
              return
          else:
-             print 'Must be superuser to execute this command.'
+             print >> sys.stderr, 'Must be superuser to execute this command.'
              sys.exit()
  
      def parse_excludes(self, excludes_string):
@@@ -137,34 -137,34 +137,34 @@@
                  try:
                      ramdisk_id = md.get_instance_ramdisk()
                  except MetadataReadError:
-                     print 'Unable to read ramdisk id'
+                     print >> sys.stderr, 'Unable to read ramdisk id'
  
              if not kernel_id:
                  try:
                      kernel_id = md.get_instance_kernel()
                  except MetadataReadError:
-                     print 'Unable to read kernel id'
+                     print >> sys.stderr, 'Unable to read kernel id'
  
              if not block_dev_mapping:
                  try:
                      block_dev_mapping = \
                          md.get_instance_block_device_mappings()
                  except MetadataReadError:
-                     print 'Unable to read block device mapping'
+                     print >> sys.stderr, 'Unable to read block device mapping'
  
              try:
                  product_codes = md.get_instance_product_codes().split('\n'
                          )
              except MetadataReadError:
-                 print 'Unable to read product codes'
+                 print >> sys.stderr, 'Unable to read product codes'
  
              try:
                  ancestor_ami_ids = md.get_ancestor_ami_ids().split('\n')
              except MetadataReadError:
-                 print 'Unable to read ancestor ids'
+                 print >> sys.stderr, 'Unable to read ancestor ids'
          except IOError:
  
-             print 'Unable to read instance metadata. Pass the --no-inherit option if you wish to exclude instance metadata.'
+             print >> sys.stderr, 'Unable to read instance metadata. Pass the --no-inherit option if you wish to exclude instance metadata.'
              sys.exit()
  
          return (ramdisk_id, kernel_id, block_dev_mapping, product_codes,
@@@ -244,7 -244,7 +244,7 @@@
          try:
              fsinfo = bundler.get_fs_info(self.volume_path)
          except UnsupportedException, e:
-             print e
+             print >> sys.stderr, e
              sys.exit(1)
          try:
              image_path = bundler.make_image(self.size, excludes, self.prefix,
@@@ -270,7 -270,7 +270,7 @@@
              bundler.copy_volume(image_path, self.volume_path, excludes,
                                  self.generate_fstab, self.fstab_path)
          except CopyError:
-             print 'Unable to copy files'
+             print >> sys.stderr, 'Unable to copy files'
              self.cleanup(image_path)
              sys.exit(1)
          except (NotFoundError, CommandFailed, UnsupportedException):
diff --combined euca2ools/commands/bundle/deletebundle.py
index 5f6201b,f54dbe8..f54dbe8
--- a/euca2ools/commands/bundle/deletebundle.py
+++ b/euca2ools/commands/bundle/deletebundle.py
@@@ -77,7 -77,7 +77,7 @@@ class DeleteBundle(euca2ools.commands.e
          try:
              bucket_instance = s3conn.get_bucket(bucket)
          except S3ResponseError, s3error:
-             print 'Unable to get bucket %s' % bucket
+             print >> sys.stderr, 'Unable to get bucket %s' % bucket
              sys.exit()
          return bucket_instance
  
@@@ -93,7 -93,7 +93,7 @@@
                      if node.nodeType == node.TEXT_NODE:
                          parts.append(node.data)
          except:
-             print 'problem parsing: %s' % manifest_filename
+             print >> sys.stderr, 'problem parsing: %s' % manifest_filename
          return parts
  
      def get_manifests(self, bucket):
@@@ -119,8 -119,8 +119,8 @@@
              except S3ResponseError, s3error:
                  s3error_string = '%s' % s3error
                  if s3error_string.find('200') < 0:
-                     print s3error_string
-                     print 'unable to download manifest %s' % manifest
+                     print >> sys.stderr, s3error_string
+                     print >> sys.stderr, 'unable to download manifest %s' % manifest
                      if os.path.exists(manifest_filename):
                          os.remove(manifest_filename)
                      return False
@@@ -139,8 -139,8 +139,8 @@@
                  except S3ResponseError, s3error:
                      s3error_string = '%s' % s3error
                      if s3error_string.find('200') < 0:
-                         print s3error_string
-                         print 'unable to delete part %s' % part
+                         print >> sys.stderr, s3error_string
+                         print >> sys.stderr, 'unable to delete part %s' % part
                          sys.exit()
  
  
@@@ -153,12 -153,12 +153,12 @@@
              except Exception, s3error:
                  s3error_string = '%s' % s3error
                  if s3error_string.find('200') < 0:
-                     print s3error_string
-                     print 'unable to delete manifest %s' % manifest
+                     print >> sys.stderr, s3error_string
+                     print >> sys.stderr, 'unable to delete manifest %s' % manifest
                      try:
                          bucket = self.ensure_bucket(bucket_name)
                      except ConnectionFailed, e:
-                         print e.message
+                         print >> sys.stderr, e.message
                          sys.exit(1)
          if clear:
              try:
@@@ -166,8 -166,8 +166,8 @@@
              except Exception, s3error:
                  s3error_string = '%s' % s3error
                  if s3error_string.find('200') < 0:
-                     print s3error_string
-                     print 'unable to delete bucket %s' % bucket.name
+                     print >> sys.stderr, s3error_string
+                     print >> sys.stderr, 'unable to delete bucket %s' % bucket.name
  
      def remove_manifests(self, manifests, directory):
          for manifest in manifests:
@@@ -179,9 -179,9 +179,9 @@@
          directory = os.path.abspath('/tmp')
  
          if not self.manifest_path and not self.prefix:
-             print 'Neither a manifestpath nor a prefix was specified.'
-             print 'All bundles in bucket', self.bucket, 'will be deleted.'
-             print ('If this is not what you want, press Ctrl+C in the next '
+             print >> sys.stderr, 'Neither a manifestpath nor a prefix was specified.'
+             print >> sys.stderr, 'All bundles in bucket', self.bucket, 'will be deleted.'
+             print >> sys.stderr, ('If this is not what you want, press Ctrl+C in the next '
                     '10 seconds'),
              for __ in range(10):
                  sys.stdout.write('.')
diff --combined euca2ools/commands/bundle/downloadbundle.py
index 92d87ba,8eea990..8eea990
--- a/euca2ools/commands/bundle/downloadbundle.py
+++ b/euca2ools/commands/bundle/downloadbundle.py
@@@ -63,7 -63,7 +63,7 @@@ class DownloadBundle(euca2ools.commands
          try:
              bucket_instance = s3conn.get_bucket(bucket)
          except S3ResponseError, s3error:
-             print 'Unable to get bucket %s' % bucket
+             print >> sys.stderr, 'Unable to get bucket %s' % bucket
              sys.exit()
          return bucket_instance
  
diff --combined euca2ools/commands/euca/describeinstances.py
index f75c3ad,f53aebe..6621b4e
--- a/euca2ools/commands/euca/describeinstances.py
+++ b/euca2ools/commands/euca/describeinstances.py
@@@ -1,6 -1,6 +1,6 @@@
  # Software License Agreement (BSD License)
  #
 -# Copyright (c) 2009-2011, Eucalyptus Systems, Inc.
 +# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
  # All rights reserved.
  #
  # Redistribution and use of this software in source and binary forms, with or
@@@ -27,80 -27,146 +27,81 @@@
  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  # POSSIBILITY OF SUCH DAMAGE.
 -#
 -# Author: Neil Soman neil at eucalyptus.com
 -#         Mitch Garnaat mgarnaat at eucalyptus.com
 -
 -import euca2ools.commands.eucacommand
 -from boto.roboto.param import Param
 -import euca2ools.utils
 -
 -class DescribeInstances(euca2ools.commands.eucacommand.EucaCommand):
 -
 -    APIVersion = '2010-08-31'
 -    Description = 'Shows information about instances.'
 -    Args = [Param(name='instance', ptype='string',
 -                  cardinality='+', optional=True)]
 -    Filters = [Param(name='architecture', ptype='string',
 -                     doc="""Instance architecture.
 -                     Valid values are i386 | x86_64 | armhf"""),
 -               Param(name='availability-zone', ptype='string',
 -                     doc="Instance's Availability Zone"),
 -               Param(name='block-device-mapping.attach-time',
 -                     ptype='datetime',
 -                     doc="""Attach time for an Amazon EBS volume mapped
 -                     to the instance"""),
 -               Param(name='block-device-mapping.delete-on-termination',
 -                     ptype='boolean',
 -                     doc="""Whether the Amazon EBS volume is deleted on
 -                     instance termination."""),
 -               Param(name='block-device-mapping.device-name', ptype='string',
 -                     doc="""Device name (e.g., /dev/sdh) for an Amazon EBS volume
 -                     mapped to the image."""),
 -               Param(name='block-device-mapping.status', ptype='string',
 -                     doc="""Status for an Amazon EBS volume mapped to the instance.
 -                     Valid Values: attaching | attached | detaching | detached"""),
 -               Param(name='block-device-mapping.volume-id', ptype='string',
 -                     doc="""ID for an Amazon EBS volume mapped to the instance."""),
 -               Param(name='client-token', ptype='string',
 -                     doc="""Idempotency token you provided when you launched
 -                     the instance."""),
 -               Param(name='dns-name', ptype='string',
 -                     doc='Public DNS name of the instance.'),
 -               Param(name='group-id', ptype='string',
 -                     doc='A security group the instance is in.'),
 -               Param(name='hypervisor', ptype='string',
 -                     doc="""Hypervisor type of the instance.
 -                     Valid values are ovm | xen."""),
 -               Param(name='image-id', ptype='string',
 -                     doc='ID of the imageID used to launch the instance'),
 -               Param(name='instance-id', ptype='string',
 -                     doc='ID of the instance'),
 -               Param(name='instance-lifecycle', ptype='string',
 -                     doc='Whether this is a Spot Instance.'),
 -               Param(name='instance-state-code', ptype='integer',
 -                     doc='Code identifying the state of the instance'),
 -               Param(name='instance-state-name', ptype='string',
 -                     doc='State of the instance.'),
 -               Param(name='instance-type', ptype='string',
 -                     doc="""Type of the instance."""),
 -               Param(name='ip-address', ptype='string',
 -                     doc='Public IP address of the instance.'),
 -               Param(name='kernel-id', ptype='string',
 -                     doc='Kernel ID.'),
 -               Param(name='key-name', ptype='string',
 -                     doc="""Name of the key pair used when the
 -                     instance was launched."""),
 -               Param(name='launch-index', ptype='string',
 -                     doc="""When launching multiple instances at once,
 -                     this is the index for the instance in the launch group"""),
 -               Param(name='launch-time', ptype='string',
 -                     doc='Time instance was launched'),
 -               Param(name='monitoring-state', ptype='string',
 -                     doc='Whether monitoring is enabled for the instance.'),
 -               Param(name='owner-id', ptype='string',
 -                     doc='AWS account ID of the image owner.'),
 -               Param(name='placement-group-name', ptype='string',
 -                     doc='Name of the placement group the instance is in'),
 -               Param(name='platform', ptype='string',
 -                     doc="""Use windows if you have Windows based AMIs;
 -                     otherwise leave blank."""),
 -               Param(name='private-dns-name', ptype='string',
 -                     doc='Private DNS name of the instance.'),
 -               Param(name='private-ip-address', ptype='string',
 -                     doc='Private ip address of the instance.'),
 -               Param(name='product-code', ptype='string',
 -                     doc='Product code associated with the AMI.'),
 -               Param(name='ramdisk-id', ptype='string',
 -                     doc='The ramdisk ID.'),
 -               Param(name='reason', ptype='string',
 -                     doc="""Reason for the instance's current state."""),
 -               Param(name='requestor-id', ptype='string',
 -                     doc="""ID of the entity that launched the instance
 -                     on your behalf."""),
 -               Param(name='reservation-id', ptype='string',
 -                     doc="""ID of the instance's reservation."""),
 -               Param(name='root-device-name', ptype='string',
 -                     doc='Root device name of the AMI (e.g., /dev/sda1).'),
 -               Param(name='root-device-type', ptype='string',
 -                     doc="""Root device type the AMI uses.
 -                     Valid Values: ebs | instance-store."""),
 -               Param(name='spot-instance-request-id', ptype='string',
 -                     doc='ID of the Spot Instance request.'),
 -               Param(name='state-reason-code', ptype='string',
 -                     doc='Reason code for the state change.'),
 -               Param(name='state-reason-message', ptype='string',
 -                     doc='Message for the state change.'),
 -               Param(name='subnet-id', ptype='string',
 -                     doc='ID of the subnet the instance is in (VPC).'),
 -               Param(name='tag-key', ptype='string',
 -                     doc='Key of a tag assigned to the resource.'),
 -               Param(name='tag-value', ptype='string',
 -                     doc='Value of a tag assigned to the resource.'),
 -               Param(name='tag:key', ptype='string',
 -                     doc="""Filters the results based on a specific
 -                     tag/value combination."""),
 -               Param(name='virtualization-type', ptype='string',
 -                     doc="""Virtualization type of the instance.
 -                     Valid values: paravirtual | hvm"""),
 -               Param(name='vpc-id', ptype='string',
 -                     doc='ID of the VPC the instance is in.')]
 -    
 -    def display_reservations(self, reservations):
 -        for reservation in reservations:
 -            instances = []
 -            instances = reservation.instances
 -            if len(instances) == 0:
 -                continue
 -            reservation_string = '%s\t%s' % (reservation.id,
 -                    reservation.owner_id)
 -            group_delim = '\t'
 -            for group in reservation.groups:
 -                reservation_string += '%s%s' % (group_delim, group.id)
 -                group_delim = ', '
 -            print 'RESERVATION\t%s' % reservation_string
 -            euca2ools.utils.print_instances(instances)
  
 -    def main(self):
 -        conn = self.make_connection_cli()
 -        return self.make_request_cli(conn, 'get_all_instances',
 -                                     instance_ids=self.instance)
 +from requestbuilder import Arg, Filter, GenericTagFilter
 +from . import EucalyptusRequest
  
 -    def main_cli(self):
 -        reservations = self.main()
 -        self.display_reservations(reservations)
 +class DescribeInstances(EucalyptusRequest):
 +    API_VERSION = '2010-08-31'
 +    DESCRIPTION = 'Show information about instances'
 +    ARGS = [Arg('InstanceId', metavar='INSTANCE', nargs='*',
 +                help='Limit results to one or more instances')]
-     FILTERS = [Filter('architecture', help='CPU architecture'),
++    FILTERS = [Filter('architecture', choices=('i386', 'x86_64', 'armhf'),
++                      help='CPU architecture'),
 +               Filter('availability-zone'),
 +               Filter('block-device-mapping.attach-time',
 +                      help='volume attachment time'),
 +               Filter('block-device-mapping.delete-on-termination', type=bool,
 +                      help='''whether a volume is deleted upon instance
 +                              termination'''),
 +               Filter('block-device-mapping.device-name',
 +                      help='volume device name (e.g. /dev/sdf)'),
 +               Filter('block-device-mapping.status', help='volume status'),
 +               Filter('block-device-mapping.volume-id', help='volume ID'),
 +               Filter('client-token',
 +                      help='idempotency token provided at instance run time'),
 +               Filter('dns-name', help='public DNS name'),
 +               Filter('group-id', help='security group membership'),
 +               Filter('hypervisor', help='hypervisor type'),
 +               Filter('image-id', help='machine image ID'),
 +               Filter('instance-id'),
 +               Filter('instance-lifecycle', choices=['spot'],
 +                      help='whether this is a spot instance'),
 +               Filter('instance-state-code', type=int,
 +                      help='numeric code identifying instance state'),
 +               Filter('instance-state-name', help='instance state'),
 +               Filter('instance-type',),
 +               Filter('ip-address', help='public IP address'),
 +               Filter('kernel-id', help='kernel image ID'),
 +               Filter('key-name',
 +                      help='key pair name provided at instance launch time'),
 +               Filter('launch-index', help='launch index within a reservation'),
 +               Filter('launch-time', help='instance launch time'),
 +               Filter('monitoring-state', help='whether monitoring is enabled'),
 +               Filter('owner-id', help='instance owner\'s account ID'),
 +               Filter('placement-group-name'),
 +               Filter('platform', choices=['windows'],
 +                      help='whether this is a Windows instance'),
 +               Filter('private-dns-name'),
 +               Filter('private-ip-address'),
 +               Filter('product-code'),
 +               Filter('ramdisk-id', help='ramdisk image ID'),
 +               Filter('reason', help='reason for the more recent state change'),
 +               Filter('requestor-id',
 +                      help='ID of the entity that launched an instance'),
 +               Filter('reservation-id'),
 +               Filter('root-device-name',
 +                      help='root device name (e.g. /dev/sda1)'),
 +               Filter('root-device-type', choices=['ebs', 'instance-store'],
 +                      help='root device type (ebs or instance-store)'),
 +               Filter('spot-instance-request-id'),
 +               Filter('state-reason-code',
 +                      help='reason code for the most recent state change'),
 +               Filter('state-reason-message',
 +                      help='message for the most recent state change'),
 +               Filter('subnet-id',
 +                      help='ID of the VPC subnet the instance is in'),
 +               Filter('tag-key',
 +                      help='name of any tag assigned to the instance'),
 +               Filter('tag-value',
 +                      help='value of any tag assigned to the instance'),
 +               GenericTagFilter('tag:KEY',
 +                                help='specific tag key/value combination'),
 +               Filter('virtualization-type', choices=['paravirtual', 'hvm']),
 +               Filter('vpc-id', help='ID of the VPC the instance is in')]
 +    LIST_MARKERS = ['reservationSet', 'instancesSet', 'groupSet', 'tagSet',
 +                    'blockDeviceMapping', 'productCodes']
  
 +    def print_result(self, result):
 +        for reservation in result.get('reservationSet'):
 +            self.print_reservation(reservation)
diff --combined euca2ools/commands/euca/getconsoleoutput.py
index 415e16d,a5f235b..541be6b
--- a/euca2ools/commands/euca/getconsoleoutput.py
+++ b/euca2ools/commands/euca/getconsoleoutput.py
@@@ -1,6 -1,6 +1,6 @@@
  # Software License Agreement (BSD License)
  #
 -# Copyright (c) 2009-2011, Eucalyptus Systems, Inc.
 +# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
  # All rights reserved.
  #
  # Redistribution and use of this software in source and binary forms, with or
@@@ -27,17 -27,43 +27,38 @@@
  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  # POSSIBILITY OF SUCH DAMAGE.
 -#
 -# Author: Neil Soman neil at eucalyptus.com
 -#         Mitch Garnaat mgarnaat at eucalyptus.com
  
 -import euca2ools.commands.eucacommand
 -from boto.roboto.param import Param
 +import base64
 +from requestbuilder import Arg
 +from . import EucalyptusRequest
  
 -class GetConsoleOutput(euca2ools.commands.eucacommand.EucaCommand):
++CHAR_ESCAPES = {
++        u'\x00': u'^@',    u'\x0c': u'^L',    u'\x17': u'^W',
++        u'\x01': u'^A',    u'\x0e': u'^N',    u'\x18': u'^X',
++        u'\x02': u'^B',    u'\x0f': u'^O',    u'\x19': u'^Y',
++        u'\x03': u'^C',    u'\x10': u'^P',    u'\x1a': u'^Z',
++        u'\x04': u'^D',    u'\x11': u'^Q',    u'\x1b': u'^[',
++        u'\x05': u'^E',    u'\x12': u'^R',    u'\x1c': u'^\\',
++        u'\x06': u'^F',    u'\x13': u'^S',    u'\x1d': u'^]',
++        u'\x07': u'^G',    u'\x14': u'^T',    u'\x1e': u'^^',
++        u'\x08': u'^H',    u'\x15': u'^U',    u'\x1f': u'^_',
++        u'\x0b': u'^K',    u'\x16': u'^V',    u'\x7f': u'^?',
++}
+ 
 -    Description = 'Prints console output from a running instance.'
 -    Args = [Param(name='instance_id', ptype='string', optional=False,
 -                  doc="""unique identifier for instance
 -                  to show the console output for.""")]
 -    Options = [Param(name='raw', long_name='raw', ptype='boolean',
 -                     default=False, optional=True,
 -                     doc='''Display raw output without escaping control
 -                     characters''')]
 +class GetConsoleOutput(EucalyptusRequest):
 +    DESCRIPTION = 'Retrieve console output for the specified instance'
 +    ARGS = [Arg('InstanceId', metavar='INSTANCE',
-                 help='instance to obtain console output from')]
++                help='instance to obtain console output from'),
++            Arg('--raw', action='store_true', route_to=None,
++                help='Display raw output without escaping control characters')]
  
 -    def display_console_output(self, console_output):
 -        print console_output.instance_id
 -        print console_output.timestamp
 -        output = console_output.output
 -        if not self.raw:
 +    def print_result(self, result):
 +        print result.get('instanceId', '')
 +        print result.get('timestamp', '')
-         print base64.b64decode(result.get('output', ''))
++        output = base64.b64decode(result.get('output', ''))
++        output = output.decode()
++        if not self.args['raw']:
+             # Escape control characters
 -            esc_ords = (list(range(0x00, 0x09)) + list(range(0x0e, 0x1f)) +
 -                        [0x0b, 0x0c, 0x7f])
 -            for esc_ord in esc_ords:
 -                # Small assumption:  we aren't translating ' or "
 -                output = output.replace(chr(esc_ord),
 -                                        repr(chr(esc_ord)).strip('\'"'))
++            for char, escape in CHAR_ESCAPES.iteritems():
++                output = output.replace(char, escape)
+         print output
 -
 -    def main(self):
 -        conn = self.make_connection_cli()
 -        return self.make_request_cli(conn, 'get_console_output',
 -                                   instance_id=self.instance_id)
 -
 -    def main_cli(self):
 -        co = self.main()
 -        self.display_console_output(co)
diff --combined euca2ools/commands/euca/rebootinstances.py
index 5a8c134,f80de2c..56e1b46
--- a/euca2ools/commands/euca/rebootinstances.py
+++ b/euca2ools/commands/euca/rebootinstances.py
@@@ -1,6 -1,6 +1,6 @@@
  # Software License Agreement (BSD License)
  #
 -# Copyright (c) 2009-2011, Eucalyptus Systems, Inc.
 +# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
  # All rights reserved.
  #
  # Redistribution and use of this software in source and binary forms, with or
@@@ -27,14 -27,26 +27,11 @@@
  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  # POSSIBILITY OF SUCH DAMAGE.
 -#
 -# Author: Neil Soman neil at eucalyptus.com
 -#         Mitch Garnaat mgarnaat at eucalyptus.com
 -
 -import euca2ools.commands.eucacommand
 -from boto.roboto.param import Param
 -
 -class RebootInstances(euca2ools.commands.eucacommand.EucaCommand):
 -
 -    Description = 'Reboots specified instances.'
 -    Args = [Param(name='instance_id', ptype='string',
 -                  optional=False, cardinality='+',
 -                  doc='unique identifier for instance to reboot')]
  
 -    def main(self):
 -        conn = self.make_connection_cli()
 -        return self.make_request_cli(conn, 'reboot_instances',
 -                                     instance_ids=self.instance_id)
 +from requestbuilder import Arg
 +from . import EucalyptusRequest
  
 -    def main_cli(self):
 -        status = self.main()
 -        if not status:
 -            self.error_exit()
 +class RebootInstances(EucalyptusRequest):
 +    DESCRIPTION = 'Reboot one or more instances'
 +    ARGS = [Arg('InstanceId', metavar='INSTANCE', nargs='+',
 +                help='instance(s) to reboot')]
- 
-     def print_result(self, result):
-         pass
diff --combined euca2ools/commands/euca/registerimage.py
index fecba87,0000000..8efe1ea
mode 100644,000000..100644
--- a/euca2ools/commands/euca/registerimage.py
+++ b/euca2ools/commands/euca/registerimage.py
@@@ -1,101 -1,0 +1,101 @@@
 +# Software License Agreement (BSD License)
 +#
 +# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
 +# All rights reserved.
 +#
 +# Redistribution and use of this software in source and binary forms, with or
 +# without modification, are permitted provided that the following conditions
 +# are met:
 +#
 +#   Redistributions of source code must retain the above
 +#   copyright notice, this list of conditions and the
 +#   following disclaimer.
 +#
 +#   Redistributions in binary form must reproduce the above
 +#   copyright notice, this list of conditions and the
 +#   following disclaimer in the documentation and/or other
 +#   materials provided with the distribution.
 +#
 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 +# POSSIBILITY OF SUCH DAMAGE.
 +
 +from requestbuilder import Arg
 +from . import EucalyptusRequest
 +from .argtypes import block_device_mapping
 +
 +class RegisterImage(EucalyptusRequest):
 +    DESCRIPTION = 'Register a new image'
 +    ARGS = [Arg('ImageLocation', metavar='MANIFEST', nargs='?',
 +                help='''location of the image manifest in S3 storage
 +                (required for instance-store images)'''),
 +            Arg('-n', '--name', dest='Name', required=True,
 +                help='name of the new image (required)'),
 +            Arg('-d', '--description', dest='Description',
 +                help='description of the new image'),
 +            Arg('-a', '--architecture', dest='Architecture',
-                 choices=('i386', 'x86_64'),
++                choices=('i386', 'x86_64', 'armhf'),
 +                help='CPU architecture of the new image'),
 +            Arg('--kernel', dest='KernelId', metavar='KERNEL',
 +                help='kernel to associate with the new image'),
 +            Arg('--ramdisk', dest='RamdiskId', metavar='RAMDISK',
 +                help='ramdisk to associate with the new image'),
 +            Arg('--root-device-name', dest='RootDeviceName', metavar='DEVICE',
 +                help='root device name (default: /dev/sda1)'),
 +                # ^ default is added by main()
 +            Arg('--snapshot', route_to=None,
 +                help='snapshot to use for the root device'),
 +            Arg('-b', '--block-device-mapping', metavar='DEVICE=MAPPED',
 +                dest='BlockDeviceMapping', action='append',
 +                type=block_device_mapping, default=[],
 +                help='''define a block device mapping for the image, in the
 +                form DEVICE=MAPPED, where "MAPPED" is "none", "ephemeral(0-3)",
 +                or "[SNAP-ID]:[SIZE]:[true|false]"''')]
 +
 +    def preprocess(self):
 +        if self.args.get('ImageLocation'):
 +            # instance-store image
 +            if self.args.get('RootDeviceName'):
 +                self._cli_parser.error('argument --root-device-name: not '
 +                        'allowed with argument MANIFEST')
 +            if self.args.get('snapshot'):
 +                self._cli_parser.error('argument --snapshot: not allowed '
 +                        'with argument MANIFEST')
 +        else:
 +            # Try for an EBS image
 +            if not self.args.get('RootDeviceName'):
 +                self.args['RootDeviceName'] = '/dev/sda1'
 +            snapshot = self.args.get('snapshot')
 +            # Look for a mapping for the root device
 +            for mapping in self.args['BlockDeviceMapping']:
 +                if mapping.get('DeviceName') == self.args['RootDeviceName']:
 +                    if (snapshot and
 +                        snapshot != mapping.get('Ebs', {}).get('SnapshotId')):
 +                        # The mapping's snapshot differs or doesn't exist
 +                        self._cli_parser.error('snapshot ID supplied with '
 +                                '--snapshot conflicts with block device '
 +                                'mapping for root device ' +
 +                                mapping['DeviceName'])
 +                    else:
 +                        # No need to apply --snapshot since the mapping is
 +                        # already there
 +                        break
 +            else:
 +                if snapshot:
 +                    self.args['BlockDeviceMapping'].append(
 +                            {'DeviceName': self.args['RootDeviceName'],
 +                             'Ebs':        {'SnapshotId': snapshot}})
 +                else:
 +                    self._cli_parser.error('either a manifest location or a '
 +                            'root device snapshot mapping must be specified')
 +
 +    def print_result(self, result):
 +        print self.tabify(('IMAGE', result.get('imageId')))
diff --combined euca2ools/commands/euca/runinstances.py
index a92c4f0,e3f3629..2ef1f59
--- a/euca2ools/commands/euca/runinstances.py
+++ b/euca2ools/commands/euca/runinstances.py
@@@ -1,6 -1,6 +1,6 @@@
  # Software License Agreement (BSD License)
  #
 -# Copyright (c) 20092011, Eucalyptus Systems, Inc.
 +# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
  # All rights reserved.
  #
  # Redistribution and use of this software in source and binary forms, with or
@@@ -27,102 -27,134 +27,108 @@@
  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  # POSSIBILITY OF SUCH DAMAGE.
 -#
 -# Author: Neil Soman neil at eucalyptus.com
 -#         Mitch Garnaat mgarnaat at eucalyptus.com
  
 -import euca2ools.commands.eucacommand
 -from boto.roboto.param import Param
 -import euca2ools.utils
 +import argparse
 +import base64
  import os.path
 +from requestbuilder import Arg, MutuallyExclusiveArgList
 +import sys
 +from . import EucalyptusRequest
 +from .argtypes import b64encoded_file_contents, block_device_mapping
  
 -class RunInstances(euca2ools.commands.eucacommand.EucaCommand):
 -
 -    Description = 'Starts instances.'
 -    Options = [Param(name='count', short_name='n', long_name='instance-count',
 -                     optional=True, ptype='string', default='1',
 -                     doc='Number of instances to run.'),
 -               Param(name='group_name', short_name='g', long_name='group',
 -                     optional=True, ptype='string', cardinality='*',
 -                     doc='Security group to run the instance in.'),
 -               Param(name='keyname', short_name='k', long_name='key',
 -                     optional=True, ptype='string',
 -                     doc='Name of a keypair.'),
 -               Param(name='user_data', short_name='d', long_name='user-data',
 -                     optional=True, doc='User data to pass to the instance.'),
 -               Param(name='user_data_force', long_name='user-data-force',
 -                     optional=True,
 -                     doc='Just like --user-data, but ignore any checks.'),
 -               Param(name='user_data_file',
 -                     short_name='f', long_name='user-data-file',
 -                     optional=True, ptype='file',
 -                     doc='File containing user data to pass to the instance.'),
 -               Param(name='addressing', long_name='addressing',
 -                     optional=True, ptype='string',
 -                     doc=('[Eucalyptus extension] Address assignment method.  '
 -                          'Use "private" to run an instance with no public '
 -                          'address.')),
 -               Param(name='instance_type',
 -                     short_name='t', long_name='instance-type',
 -                     optional=True, ptype='string', default='m1.small',
 -                     doc='VM Image type to run the instance as.'),
 -               Param(name='kernel', long_name='kernel',
 -                     optional=True, ptype='string',
 -                     doc='ID of the kernel to be used.'),
 -               Param(name='ramdisk', long_name='ramdisk',
 -                     optional=True, ptype='string',
 -                     doc='ID of the ramdisk to be used.'),
 -               Param(name='block_device_mapping',
 -                     short_name='b', long_name='block-device-mapping',
 -                     optional=True, ptype='string', cardinality='*',
 -                     doc="""Block device mapping for the instance(s).
 -                     Option may be used multiple times"""),
 -               Param(name='monitor', long_name='monitor',
 -                     optional=True, ptype='boolean', default=False,
 -                     doc='Enable monitoring for the instance.'),
 -               Param(name='subnet', short_name='s', long_name='subnet',
 -                     optional=True, ptype='string',
 -                     doc='Amazon VPC subnet ID for the instance.'),
 -               Param(name='zone', short_name='z', long_name='availability-zone',
 -                     optional=True, ptype='string',
 -                     doc='availability zone to run the instance in'),
 -               Param(name='instance_initiated_shutdown_behavior',
 -                     long_name='instance-initiated-shutdown-behavior',
 -                     optional=True, ptype='string',
 -                     doc='Whether to "stop" (default) or "terminate" instance when it is shut down')]
 -    Args = [Param(name='image_id', ptype='string',
 -                  optional=False,
 -                  doc='ID of the image to run.')]
 +class RunInstances(EucalyptusRequest):
 +    DESCRIPTION = 'Launch instances of a machine image'
 +    ARGS = [Arg('ImageId', metavar='IMAGE', help='image to instantiate'),
 +            Arg('-n', '--instance-count', dest='count', metavar='MIN[-MAX]',
 +                default='1', route_to=None,
 +                help='''number of instances to launch. If this number of
 +                        instances cannot be launched, no instances will launch.
 +                        If specified as a range (min-max), the server will
 +                        attempt to launch the maximum number, but no fewer
 +                        than the minimum number.'''),
 +            Arg('-g', '--group', action='append', default=[], route_to=None,
 +                help='security group(s) in which to launch the instances'),
 +            Arg('-k', '--key', dest='KeyName', metavar='KEYPAIR',
 +                help='name of the key pair to use'),
 +            MutuallyExclusiveArgList(
 +                Arg('-d', '--user-data', dest='UserData', metavar='DATA',
 +                    type=base64.b64encode,
 +                    help='''user data to make available to instances in this
 +                            reservation'''),
 +                Arg('--user-data-force', dest='UserData',
 +                    type=base64.b64encode, help=argparse.SUPPRESS),
 +                    # ^ deprecated  ## TODO:  decide if that should remain the case
 +                Arg('-f', '--user-data-file', dest='UserData',
 +                    metavar='DATA-FILE', type=b64encoded_file_contents,
 +                    help='''file containing user data to make available to the
 +                            instances in this reservation''')),
 +            Arg('--addressing', dest='AddressingType',
 +                choices=('public', 'private'),
-                 help='addressing scheme to launch the instance with'),
++                help=('addressing scheme to launch the instance with.  Use '
++                      '"private" to run an instance with no public address.')),
 +            Arg('-t', '--instance-type', dest='InstanceType',
 +                help='type of instance to launch'),
 +            Arg('--kernel', dest='KernelId', metavar='KERNEL',
 +                help='kernel to launch the instance(s) with'),
 +            Arg('--ramdisk', dest='RamdiskId', metavar='RAMDISK',
 +                help='ramdisk to launch the instance(s) with'),
 +            Arg('-b', '--block-device-mapping', metavar='DEVICE=MAPPED',
 +                dest='BlockDeviceMapping', action='append',
 +                type=block_device_mapping, default=[],
 +                help='''define a block device mapping for the instances, in the
 +                        form DEVICE=MAPPED, where "MAPPED" is "none",
 +                        "ephemeral(0-3)", or
 +                        "[SNAP-ID]:[SIZE]:[true|false]"'''),
 +            Arg('-m', '--monitor', dest='Monitoring.Enabled',
 +                action='store_const', const='true',
 +                help='enable detailed monitoring for the instance(s)'),
 +            Arg('--subnet', dest='SubnetId', metavar='SUBNET',
 +                help='VPC subnet in which to launch the instance(s)'),
 +            Arg('-z', '--availability-zone', metavar='ZONE',
-                 dest='Placement.AvailabilityZone')]
++                dest='Placement.AvailabilityZone'),
++            Arg('--instance-initiated-shutdown-behavior',
++                dest='InstanceInitiatedShutdownBehavior',
++                choices=('stop', 'terminate'),
++                help=('whether to "stop" (default) or terminate EBS instances '
++                      'when they shut down'))]
 +    LIST_MARKERS = ['reservationSet', 'instancesSet', 'groupSet', 'tagSet',
 +                    'blockDeviceMapping', 'productCodes']
  
 -    def display_reservations(self, reservation):
 -        reservation_string = '%s\t%s' % (reservation.id,
 -                reservation.owner_id)
 -        group_delim = '\t'
 -        for group in reservation.groups:
 -            reservation_string += '%s%s' % (group_delim, group.id)
 -            group_delim = ', '
 -        print 'RESERVATION\t%s' % reservation_string
 -        euca2ools.utils.print_instances(reservation.instances)
 +    def preprocess(self):
 +        counts = self.args['count'].split('-')
 +        if len(counts) == 1:
 +            try:
 +                self.params['MinCount'] = int(counts[0])
 +                self.params['MaxCount'] = int(counts[0])
 +            except ValueError:
 +                self._cli_parser.error('argument -n/--instance-count: '
 +                                       'instance count must be an integer')
 +        elif len(counts) == 2:
 +            try:
 +                self.params['MinCount'] = int(counts[0])
 +                self.params['MaxCount'] = int(counts[1])
 +            except ValueError:
 +                self._cli_parser.error('argument -n/--instance-count: '
 +                        'instance count range must be must be comprised of '
 +                        'integers')
 +        else:
 +            self._cli_parser.error('argument -n/--instance-count: value must '
 +                                   'have format "1" or "1-2"')
 +        if self.params['MinCount'] < 1 or self.params['MaxCount'] < 1:
 +            self._cli_parser.error('argument -n/--instance-count: instance '
 +                                   'count must be positive')
 +        if self.params['MinCount'] > self.params['MaxCount']:
 +            self.log.debug('MinCount > MaxCount; swapping')
 +            self.params.update({'MinCount': self.params['MaxCount'],
 +                                'MaxCount': self.params['MinCount']})
  
 -    def read_user_data(self, user_data_filename):
 -        fp = open(user_data_filename)
 -        user_data = fp.read()
 -        fp.close()
 -        return user_data
 -
 -    def main(self):
 -        t = self.count.split('-')
 -        try:
 -            if len(t) > 1:
 -                min_count = int(t[0])
 -                max_count = int(t[1])
 +        for group in self.args['group']:
 +            if group.startswith('sg-'):
 +                self.params.setdefault('SecurityGroupId', [])
 +                self.params['SecurityGroupId'].append(group)
              else:
 -                min_count = max_count = int(t[0])
 -        except ValueError:
 -            msg = 'Invalid value for --instance-count: %s' % count
 -            self.display_error_and_exit(msg)
 -                
 -        if self.user_data and os.path.isfile(self.user_data):
 -            msg = ('string provided as user-data [%s] is a file.\nTry %s or %s'
 -                    % (self.user_data, '--user-data-file', '--user-data-force'))
 -            self.display_error_and_exit(msg)
 -
 -        if self.user_data_force:
 -            self.user_data = self.user_data_force
 -
 -        if not self.user_data:
 -            if self.user_data_file:
 -                self.user_data = self.read_user_data(self.user_data_file)
 -
 -        if self.block_device_mapping:
 -            self.block_device_mapping = self.parse_block_device_args(self.block_device_mapping)
 -        conn = self.make_connection_cli()
 -        return self.make_request_cli(conn, 'run_instances',
 -                                     image_id=self.image_id,
 -                                     min_count=min_count,
 -                                     max_count=max_count,
 -                                     key_name=self.keyname,
 -                                     security_groups=self.group_name,
 -                                     user_data=self.user_data,
 -                                     addressing_type=self.addressing,
 -                                     instance_type=self.instance_type,
 -                                     placement=self.zone,
 -                                     kernel_id=self.kernel,
 -                                     ramdisk_id=self.ramdisk,
 -                                     block_device_map=self.block_device_mapping,
 -                                     monitoring_enabled=self.monitor,
 -                                     subnet_id=self.subnet,
 -                                     instance_initiated_shutdown_behavior=self.instance_initiated_shutdown_behavior)
 -
 -    def main_cli(self):
 -        reservation = self.main()
 -        self.display_reservations(reservation)
 +                self.params.setdefault('SecurityGroup', [])
 +                self.params['SecurityGroup'].append(group)
  
 +    def print_result(self, result):
 +        self.print_reservation(result)
diff --combined euca2ools/commands/eustore/installimage.py
index d2f8e42,de877aa..373fd31
--- a/euca2ools/commands/eustore/installimage.py
+++ b/euca2ools/commands/eustore/installimage.py
@@@ -38,6 -38,7 +38,7 @@@ import tarfil
  import hashlib
  import re
  import zlib
+ import shutil
  import tempfile
  import urllib2
  import boto
@@@ -48,8 -49,8 +49,8 @@@ from boto.s3.connection import Locatio
  import euca2ools.bundler
  import euca2ools.commands.eustore
  import euca2ools.utils
 -from euca2ools.commands.euca.bundleimage import BundleImage
 -from euca2ools.commands.euca.uploadbundle import UploadBundle
 +from euca2ools.commands.bundle.bundleimage import BundleImage
 +from euca2ools.commands.bundle.uploadbundle import UploadBundle
  from euca2ools.commands.euca.register import Register
  from euca2ools.exceptions import NotFoundError, CommandFailed
  
@@@ -184,15 -185,15 +185,15 @@@ class InstallImage(AWSQueryRequest)
              if name_match:
                  if kernel_id=='true' and img.type=='kernel':
                      if self.promptReplace("Kernel", img.name):
-                         ret_id=img.name
+                         ret_id=img.id
                      break
                  elif ramdisk_id=='true' and img.type=='ramdisk':
                      if self.promptReplace("Ramdisk", img.name):
-                         ret_id=img.name
+                         ret_id=img.id
                      break
                  elif kernel_id!='true' and ramdisk_id!='true' and img.type=='machine':
                      if self.promptReplace("Image", img.name):
-                         ret_id=img.name
+                         ret_id=img.id
                      break
  
          if ret_id:
@@@ -247,10 -248,10 +248,10 @@@
          try:
              names = bundler.untarzip_image(self.destination, file)
          except OSError:
-             print "Error: cannot unbundle image, possibly corrupted file"
+             print >> sys.stderr, "Error: cannot unbundle image, possibly corrupted file"
              sys.exit(-1)
          except IOError:
-             print "Error: cannot unbundle image, possibly corrupted file"
+             print >> sys.stderr, "Error: cannot unbundle image, possibly corrupted file"
              sys.exit(-1)
          kernel_dir=None
          if not(self.cli_options.kernel_type==None):
@@@ -285,14 -286,15 +286,15 @@@
                                  print ramdisk_id
                  if not(kernel_found):
                      if not(kernel_dir):
-                         print "Error: couldn't find kernel. Check your parameters or specify an existing kernel/ramdisk"
+                         print >> sys.stderr, "Error: couldn't find kernel. Check your parameters or specify an existing kernel/ramdisk"
                          sys.exit(-1);
                      elif i==0:
-                         print "Error: couldn't find kernel. Check your parameters or specify an existing kernel/ramdisk"
+                         print >> sys.stderr, "Error: couldn't find kernel. Check your parameters or specify an existing kernel/ramdisk"
                          sys.exit(-1);
                  else:
                      break
          #now, install the image, referencing the kernel/ramdisk
+         image_id = None
          for path in names:
              name = os.path.basename(path)
              if not name.startswith('.'):
@@@ -303,7 -305,10 +305,10 @@@
                      else:
                          name = name[:-len('.img')]
                      id = self.bundleFile(path, name, description, arch, kernel_id, ramdisk_id)
-                     return id
+                     image_id = id
+         # make good faith attempt to remove working directory and all files within
+         shutil.rmtree(self.destination, True)
+         return image_id
  
      def main(self, **args):
          self.process_args()
@@@ -314,30 -319,30 +319,30 @@@
  
          # tarball and image option are mutually exclusive
          if (not(self.cli_options.image_name) and not(self.cli_options.tarball)):
-             print "Error: one of -i or -t must be specified"
+             print >> sys.stderr, "Error: one of -i or -t must be specified"
              sys.exit(-1)
  
          if (self.cli_options.image_name and self.cli_options.tarball):
-             print "Error: -i and -t cannot be specified together"
+             print >> sys.stderr, "Error: -i and -t cannot be specified together"
              sys.exit(-1)
  
          if (self.cli_options.tarball and \
              (not(self.cli_options.description) or not(self.cli_options.architecture))):
-             print "Error: when -t is specified, -s and -a are required"
+             print >> sys.stderr, "Error: when -t is specified, -s and -a are required"
              sys.exit(-1)
  
          if (self.cli_options.architecture and \
              not(self.cli_options.architecture == 'i386' or self.cli_options.architecture == 'x86_64')):
-             print "Error: architecture must be either 'i386' or 'x86_64'"
+             print >> sys.stderr, "Error: architecture must be either 'i386' or 'x86_64'"
              sys.exit(-1)
  
          if (self.cli_options.kernel and not(self.cli_options.ramdisk)) or \
             (not(self.cli_options.kernel) and self.cli_options.ramdisk):
-             print "Error: kernel and ramdisk must both be overridden"
+             print >> sys.stderr, "Error: kernel and ramdisk must both be overridden"
              sys.exit(-1)
  
          if (self.cli_options.architecture and self.cli_options.image_name):
-             print "Warning: you may be overriding the default architecture of this image!"
+             print >> sys.stderr, "Warning: you may be overriding the default architecture of this image!"
  
  
          euare_svc = EuareService()
@@@ -348,7 -353,7 +353,7 @@@
                      is_secure=euare_svc.args['is_secure'])
          userinfo  = conn.get_user().arn.split(':')
          if not(userinfo[4]=='eucalyptus') and not(self.cli_options.kernel):
-             print "Error: must be cloud admin to upload kernel/ramdisk. try specifying existing ones with --kernel and --ramdisk"
+             print >> sys.stderr, "Error: must be cloud admin to upload kernel/ramdisk. try specifying existing ones with --kernel and --ramdisk"
              sys.exit(-1)
          self.eustore_url = self.ServiceClass.StoreBaseURL
  
@@@ -356,6 -361,8 +361,8 @@@
          ec2_conn = boto.connect_euca(host=euare_svc.args['host'], \
                          aws_access_key_id=euare_svc.args['aws_access_key_id'],\
                          aws_secret_access_key=euare_svc.args['aws_secret_access_key'])
+         ec2_conn.APIVersion = '2012-03-01'
+                         
          self.ImageList = ec2_conn.get_all_images()
  
          if os.environ.has_key('EUSTORE_URL'):
@@@ -388,10 -395,12 +395,12 @@@
                      # more param checking now
                      if image['single-kernel']=='True':
                          if self.cli_options.kernel_type:
-                             print "The -k option will be ignored because the image is single-kernel"
+                             print >> sys.stderr, "The -k option will be ignored because the image is single-kernel"
                      else:
-                         if not(self.cli_options.kernel_type):
-                             print "Error: The -k option must be specified because this image has separate kernels"
+                         # Warn about kernel type for multi-kernel images, but not if already installed
+                         # kernel/ramdisk have been specified.
+                         if not(self.cli_options.kernel_type) and not(self.cli_options.kernel):
+                             print >> sys.stderr, "Error: The -k option must be specified because this image has separate kernels"
                              sys.exit(-1)
                      print "Downloading Image : ",image['description']
                      imageURL = self.eustore_url+image['url']
@@@ -419,10 -428,9 +428,9 @@@
                      if image['name'] == crc.rjust(10,"0"):
                          print "Installed image: "+self.bundleAll(fp.name, None, image['description'], image['architecture'])
                      else:
-                         print "Error: Downloaded image was incomplete or corrupt, please try again"
-                     os.remove(fp.name)
+                         print >> sys.stderr, "Error: Downloaded image was incomplete or corrupt, please try again"
                  else:
-                     print "Image name not found, please run eustore-describe-images"
+                     print >> sys.stderr, "Image name not found, please run eustore-describe-images"
  
      def main_cli(self):
          euca2ools.utils.print_version_if_necessary()
diff --combined setup.py
index e1d2633,f8c190d..1c541c7
--- a/setup.py
+++ b/setup.py
@@@ -34,10 -34,21 +34,21 @@@
  from distutils.core import setup
  from distutils.command.build_scripts import build_scripts
  from distutils.command.install_scripts import install_scripts
+ from distutils.command.sdist import sdist
  import os.path
+ import re
+ import subprocess
  
  from euca2ools import __version__
  
+ def get_version():
+     try:
+         popen = subprocess.Popen(['git', 'describe'], stdout=subprocess.PIPE)
+         popen.wait()
+         return popen.stdout.read().strip()
+     except:
+         return __version__
+ 
  # Cheap hack:  install symlinks separately from regular files.
  # cmd.copy_tree accepts a preserve_symlinks option, but when we call
  # ``setup.py install'' more than once the method fails when it encounters
@@@ -64,8 -75,35 +75,35 @@@ class install_scripts_and_symlinks(inst
                  if not os.path.exists(newlink):
                      os.symlink(target, newlink)
  
+ class sdist_with_git_version(sdist):
+     '''Like sdist, but using the output of ``git describe'' to fill in
+        __init__.__version__'''
+     def make_release_tree(self, base_dir, files):
+         sdist.make_release_tree(self, base_dir, files)
+ 
+         try:
+             popen = subprocess.Popen(['git', 'describe'],
+                                      stdout=subprocess.PIPE)
+             popen.wait()
+             version = popen.stdout.read().strip()
+             version_line = '__version__ = \'{0}\'\n'.format(version)
+             old_init_file_name = os.path.join(base_dir, 'euca2ools/__init__.py')
+             new_init_file_name = old_init_file_name + '.new'
+             with open(new_init_file_name, 'w') as new_init_file:
+                 with open(old_init_file_name) as old_init_file:
+                     for line in old_init_file:
+                         if re.match("__version__ *= *'.*'", line):
+                             new_init_file.write(version_line)
+                         else:
+                             new_init_file.write(line)
+                 new_init_file.flush()
+             os.rename(new_init_file_name, old_init_file_name)
+         except:
+             # Not really a problem; we'll just leave it as-is
+             pass
+ 
  setup(name = "euca2ools",
-       version = __version__,
+       version = get_version(),
        description = "Elastic Utility Computing Architecture Command Line Tools",
        long_description="Elastic Utility Computing Architecture Command Line Tools",
        author = "Mitch Garnaat",
@@@ -195,12 -233,11 +233,12 @@@
                   "bin/eustore-install-image"],
        url = "http://open.eucalyptus.com",
        packages = ["euca2ools", "euca2ools.nc", "euca2ools.commands",
 -                  "euca2ools.commands.euca", "euca2ools.commands.euare",
 -                  "euca2ools.commands.eustore"],
 +                  "euca2ools.commands.bundle", "euca2ools.commands.euca",
 +                  "euca2ools.commands.euare", "euca2ools.commands.eustore",
 +                  "euca2ools.commands.walrus"],
        license = 'BSD (Simplified)',
        platforms = 'Posix; MacOS X; Windows',
 -      classifiers = ['Development Status :: 5 - Production/Stable',
 +      classifiers = ['Development Status :: 2 - Pre-Alpha',
                       'Intended Audience :: Users',
                       'License :: OSI Approved :: Simplified BSD License',
                       'Operating System :: OS Independent',
@@@ -210,4 -247,5 +248,5 @@@
                       'Programming Language :: Python :: 2.7',
                       'Topic :: Internet'],
        cmdclass = {'build_scripts':   build_scripts_except_symlinks,
-                   'install_scripts': install_scripts_and_symlinks})
+                   'install_scripts': install_scripts_and_symlinks,
+                   'sdist':           sdist_with_git_version})

-- 
managing cloud instances for Eucalyptus



More information about the pkg-eucalyptus-commits mailing list