[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 93fc062a48d20d4958a8be78f86fab05be2c3ccf
Author: Garrett Holmstrom <gholms at fedoraproject.org>
Date:   Sat Feb 2 18:47:37 2013 -0800

    Port to newer requestbuilder

diff --git a/euca2ools/commands/__init__.py b/euca2ools/commands/__init__.py
index 043b5cb..b1f3d94 100644
--- a/euca2ools/commands/__init__.py
+++ b/euca2ools/commands/__init__.py
@@ -36,20 +36,35 @@ import requestbuilder.request
 from .. import __version__, __codename__
 
 class Euca2oolsCommand(requestbuilder.command.BaseCommand):
-    Version = 'euca2ools {0} ({1})'.format(__version__, __codename__)
+    VERSION = 'euca2ools {0} ({1})'.format(__version__, __codename__)
 
     def __init__(self, **kwargs):
-        self.CONFIG_FILES.append('/etc/euca2ools.ini')
-        user_config_glob = os.path.join(os.path.expanduser('~/.euca'), '*.ini')
-        for configfile in sorted(glob.glob(user_config_glob)):
-            self.CONFIG_FILES.append(configfile)
+        self._config_files = None
         requestbuilder.request.BaseCommand.__init__(self, **kwargs)
 
-class Euca2oolsRequest(Euca2oolsCommand, requestbuilder.request.BaseRequest):
+    @property
+    def config_files(self):
+        if self._config_files is None:
+            self._config_files = ['/etc/euca2ools.ini']
+            user_glob = os.path.join(os.path.expanduser('~/.euca'), '*.ini')
+            self._config_files.extend(sorted(glob.glob(user_glob)))
+        return self._config_files
+
+class Euca2oolsRequest(requestbuilder.request.BaseRequest):
+    VERSION = 'euca2ools {0} ({1})'.format(__version__, __codename__)
+
     def __init__(self, **kwargs):
-        Euca2oolsCommand.__init__(self, **kwargs)
-        requestbuilder.request.BaseRequest.__init__(self, **kwargs)
+        self._config_files = None
         self.__user_agent = None
+        requestbuilder.request.BaseRequest.__init__(self, **kwargs)
+
+    @property
+    def config_files(self):
+        if self._config_files is None:
+            self._config_files = ['/etc/euca2ools.ini']
+            user_glob = os.path.join(os.path.expanduser('~/.euca'), '*.ini')
+            self._config_files.extend(sorted(glob.glob(user_glob)))
+        return self._config_files
 
     @property
     def user_agent(self):
diff --git a/euca2ools/commands/euare/__init__.py b/euca2ools/commands/euare/__init__.py
index 1d4ebb3..233316e 100644
--- a/euca2ools/commands/euare/__init__.py
+++ b/euca2ools/commands/euare/__init__.py
@@ -28,7 +28,8 @@
 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 # POSSIBILITY OF SUCH DAMAGE.
 
-from requestbuilder import Arg, SERVICE, STD_AUTH_ARGS
+from requestbuilder import Arg, SERVICE
+import requestbuilder.auth
 import requestbuilder.service
 from .. import Euca2oolsRequest
 
@@ -36,20 +37,21 @@ class Euare(requestbuilder.service.BaseService):
     NAME = 'iam'
     DESCRIPTION = 'Eucalyptus User, Authorization and Reporting Environment'
     API_VERSION = '2010-05-08'
+    AUTH_CLASS = requestbuilder.auth.QuerySigV2Auth
     ENV_URL = 'EUARE_URL'
 
 class EuareRequest(Euca2oolsRequest):
     SERVICE_CLASS = Euare
     ARGS = [Arg('-U', '--url', dest='url', metavar='URL', route_to=SERVICE,
-                help='identity service endpoint URL')] + STD_AUTH_ARGS
+                help='identity service endpoint URL')]
 
     def parse_response(self, response):
         response_dict = Euca2oolsRequest.parse_response(self, response)
         # EUARE responses enclose their useful data inside FooResponse
         # elements.  If that's all we have after stripping out ResponseMetadata
         # then just return its contents.
-        useful_keys = filter(lambda x: x != 'ResponseMetadata',
-                             response_dict.keys())
+        useful_keys = list(filter(lambda x: x != 'ResponseMetadata',
+                                  response_dict.keys()))
         if len(useful_keys) == 1:
             return response_dict[useful_keys[0]]
         else:
diff --git a/euca2ools/commands/euare/createuser.py b/euca2ools/commands/euare/createuser.py
index f3d58dc..ad892ec 100644
--- a/euca2ools/commands/euare/createuser.py
+++ b/euca2ools/commands/euare/createuser.py
@@ -49,23 +49,24 @@ class CreateUser(EuareRequest):
                 help="print the new user's ARN and GUID"),
             DELEGATE]
 
-    def main(self):
-        user_data = self.send()
+    def postprocess(self, result):
         if self.args.get('group_name'):
-            obj = AddUserToGroup(UserName=self.args['UserName'],
+            obj = AddUserToGroup(service=self.service,
+                    UserName=self.args['UserName'],
                     GroupName=self.args['group_name'],
-                    DelegateAccount=self.args.get('DelegateAccount'))
+                    DelegateAccount=self.args['DelegateAccount'])
             obj.main()
         if self.args.get('create_accesskey'):
-            obj = CreateAccessKey(UserName=self.args['UserName'],
-                    DelegateAccount=self.args.get('DelegateAccount'))
-            self.keyresponse = obj.main()
-        return user_data
+            obj = CreateAccessKey(service=self.service,
+                    UserName=self.args['UserName'],
+                    DelegateAccount=self.args['DelegateAccount'])
+            key_result = obj.main()
+            result.update(key_result)
 
     def print_result(self, result):
         if self.args['verbose']:
             print result['User']['Arn']
             print result['User']['UserId']
-        if 'keyresponse' in dir(self):
-            print self.keyresponse['AccessKey']['AccessKeyId']
-            print self.keyresponse['AccessKey']['SecretAccessKey']
+        if 'AccessKey' in result:
+            print result['AccessKey']['AccessKeyId']
+            print result['AccessKey']['SecretAccessKey']
diff --git a/euca2ools/commands/euare/deleteloginprofile.py b/euca2ools/commands/euare/deleteloginprofile.py
index b8e6338..d7250e6 100644
--- a/euca2ools/commands/euare/deleteloginprofile.py
+++ b/euca2ools/commands/euare/deleteloginprofile.py
@@ -40,6 +40,3 @@ class DeleteLoginProfile(EuareRequest):
                 required=True, help='''name of the user whose login profile
                 should be deleted (required)'''),
             DELEGATE]
-
-    def print_result(self, result):
-        pass
diff --git a/euca2ools/commands/euca/__init__.py b/euca2ools/commands/euca/__init__.py
index 835cf74..6812f27 100644
--- a/euca2ools/commands/euca/__init__.py
+++ b/euca2ools/commands/euca/__init__.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -31,149 +31,143 @@
 import argparse
 from operator import itemgetter
 import os.path
-from requestbuilder import Arg, MutuallyExclusiveArgList, AUTH, SERVICE, \
-        STD_AUTH_ARGS
+from requestbuilder import Arg, MutuallyExclusiveArgList, AUTH, SERVICE
+from requestbuilder.auth import QuerySigV2Auth
+from requestbuilder.exceptions import AuthError
 from requestbuilder.mixins import TabifyingCommand
 import requestbuilder.service
+import requests
 import shlex
 from string import Template
 import sys
 from .. import Euca2oolsRequest
 
+class EC2CompatibleQuerySigV2Auth(QuerySigV2Auth):
+    # -a and -s are deprecated; remove them in 3.2
+    ARGS = [Arg('-a', '--access-key', metavar='KEY_ID',
+                dest='deprecated_key_id', route_to=AUTH,
+                help=argparse.SUPPRESS),
+            Arg('-s', metavar='KEY', dest='deprecated_sec_key', route_to=AUTH,
+                help=argparse.SUPPRESS)]
+
+    def preprocess_arg_objs(self, arg_objs):
+        # If something else defines '-a' or '-s' args, resolve conflicts with
+        # this class's old-style auth args by capitalizing this class's args.
+        #
+        # This behavior is deprecated and will be removed in version 3.2 along
+        # with the -a and -s arguments themselves.
+        a_arg_objs = _find_args_by_parg(arg_objs, '-a')
+        s_arg_objs = _find_args_by_parg(arg_objs, '-s')
+        if len(a_arg_objs) > 1 or len(s_arg_objs) > 1:
+            for arg_obj in a_arg_objs:
+                if arg_obj.kwargs.get('dest') == 'deprecated_key_id':
+                    # Capitalize the "-a"
+                    arg_obj.pargs = tuple('-A' if parg == '-a' else parg
+                                          for parg in arg_obj.pargs)
+            for arg_obj in s_arg_objs:
+                if arg_obj.kwargs.get('dest') == 'deprecated_sec_key':
+                    # Remove it since regular -S already covers this case
+                    arg_objs.remove(arg_obj)
+
+    def configure(self):
+        # Shell-style config file given at the CLI
+        # Deprecated; should be removed in 3.2
+        if os.path.isfile(self.args['shell_configfile']):
+            config = _parse_shell_configfile(self.args['shell_configfile'])
+            if 'EC2_ACCESS_KEY' in config:
+                self.args.setdefault('key_id', config['EC2_ACCESS_KEY'])
+            if 'EC2_SECRET_KEY' in config:
+                self.args.setdefault('secret_key', config['EC2_SECRET_KEY'])
+        # Environment (for compatibility with EC2 tools)
+        if 'EC2_ACCESS_KEY' in os.environ:
+            self.args.setdefault('key_id', os.getenv('EC2_ACCESS_KEY'))
+        if 'EC2_SECRET_KEY' in os.environ:
+            self.args.setdefault('secret_key', os.getenv('EC2_SECRET_KEY'))
+        # AWS credential file (location given in the environment)
+        self.configure_from_aws_credential_file()
+        # Regular config file
+        self.configure_from_configfile()
+        # User, systemwide shell-style config files
+        # Deprecated; should be removed in 3.2
+        for configfile_name in ('~/.eucarc', '~/.eucarc/eucarc'):
+            configfile_name = os.path.expandvars(configfile_name)
+            configfile_name = os.path.expanduser(configfile_name)
+            if os.path.isfile(configfile_name):
+                config = _parse_shell_configfile(configfile_name)
+                if 'EC2_ACCESS_KEY' in os.environ:
+                    sefl.args.setdefault('key_id', config['EC2_ACCESS_KEY'])
+                if 'EC2_SECRET_KEY' in config:
+                    self.args.setdefault('secret_key', config['EC2_SECRET_KEY'])
+
+        # That's it; make sure we have everything we need
+        if not self.args.get('key_id'):
+            raise AuthError('missing access key ID')
+        if not self.args.get('secret_key'):
+            raise AuthError('missing secret key')
+
 class Eucalyptus(requestbuilder.service.BaseService):
     NAME = 'ec2'
     DESCRIPTION = 'Eucalyptus compute cloud service'
     API_VERSION = '2009-11-30'
+    AUTH_CLASS  = EC2CompatibleQuerySigV2Auth
     ENV_URL = 'EC2_URL'
 
-    def __init__(self, config, log, shell_configfile=None,
-                 deprecated_key_id=None, deprecated_sec_key=None,
-                 auth_args=None, **kwargs):
-        self.shell_configfile_name = shell_configfile
-        if deprecated_key_id and not auth_args.get('key_id'):
-            auth_args['key_id'] = deprecated_key_id
-            msg = 'given access key ID argument is deprecated; use -I instead'
-            log.warn(msg)
-            print >> sys.stderr, 'warning:', msg
-        if deprecated_sec_key and not auth_args.get('secret_key'):
-            auth_args['secret_key'] = deprecated_sec_key
-            msg = 'argument -s is deprecated; use -S instead'
-            log.warn(msg)
-            print >> sys.stderr, 'warning:', msg
-        requestbuilder.service.BaseService.__init__(self, config, log,
-                                                    auth_args=auth_args,
-                                                    **kwargs)
+    ARGS = [Arg('--config', dest='shell_configfile', metavar='CFGFILE',
+                 default='', route_to=SERVICE, help=argparse.SUPPRESS),
+            MutuallyExclusiveArgList(
+                Arg('--region', dest='userregion', metavar='REGION',
+                    route_to=SERVICE,
+                    help='region name to connect to, with optional identity'),
+                Arg('-U', '--url', metavar='URL', route_to=SERVICE,
+                    help='compute service endpoint URL'))]
 
-    def read_config(self):
-        # CLI-given shell-style config file
-        if os.path.isfile(self.shell_configfile_name or ''):
-            config = _parse_shell_configfile(self.shell_configfile_name)
-            self._populate_self_from_env_config(config)
-        # AWS credential file (path is in the environment)
-        self.read_aws_credential_file()
+    def configure(self):
+        # self.args gets highest precedence for self.endpoint and user/region
+        self.process_url(self.args.get('url'))
+        if self.args.get('userregion'):
+            self.process_userregion(self.args['userregion'])
+        # Shell-style config file given at the CLI
+        # Deprecated; should be removed in 3.2
+        if os.path.isfile(self.args['shell_configfile']):
+            config = _parse_shell_configfile(self.args['shell_configfile'])
+            self.process_url(config.get(self.ENV_URL))
+            if self.ENV_URL in config:
+                self.process_url(config[self.ENV_URL])
         # Environment
-        config = self._get_env_config_from_env()
-        self._populate_self_from_env_config(config)
-        # Requestbuilder config files
-        self.read_requestbuilder_config()
+        self.process_url(os.getenv(self.ENV_URL))
+        # Regular config file
+        self.process_url(self.config.get_region_option(self.NAME + '-url'))
         # User, systemwide shell-style config files
+        # Deprecated; should be removed in 3.2
         for configfile_name in ('~/.eucarc', '~/.eucarc/eucarc'):
             configfile_name = os.path.expandvars(configfile_name)
             configfile_name = os.path.expanduser(configfile_name)
             if os.path.isfile(configfile_name):
                 config = _parse_shell_configfile(configfile_name)
-                self._populate_self_from_env_config(config)
+                if self.ENV_URL in config:
+                    self.process_url(config[self.ENV_URL])
 
-    def _get_env_config_from_env(self):
-        envconfig = {}
-        for key in ('EC2_ACCESS_KEY', 'EC2_SECRET_KEY', 'EC2_URL'):
-            if key in os.environ:
-                envconfig[key] = os.getenv(key)
-        return envconfig
+        # Ensure everything is okay and finish up
+        self.validate_config()
+        if self.auth is not None:
+            # HACK:  this was an easy way to make a CLI-supplied shell-style
+            # config file name available to the auth handler.
+            # Remove this line in 3.2.
+            self.auth.args['shell_configfile'] = self.args['shell_configfile']
+            self.auth.configure()
 
-    def _populate_self_from_env_config(self, envconfig):
-        '''
-        Populate this service from the contents of an environment
-        variable-like dict.
-        '''
-        for (env_key, val) in envconfig.iteritems():
-            if (env_key == 'EC2_ACCESS_KEY' and
-                not self._auth_args.get('key_id')):
-                self._auth_args['key_id'] = val
-            elif (env_key == 'EC2_SECRET_KEY' and
-                  not self._auth_args.get('secret_key')):
-                self._auth_args['secret_key'] = val
-            elif env_key == 'EC2_URL' and not self.endpoint_url:
-                self._set_url_vars(val)
-
-def _parse_shell_configfile(configfile_name):
-    def sourcehook(filename):
-        filename = filename.strip('"\'')
-        filename = Template(filename).safe_substitute(config)
-        filename = os.path.expandvars(filename)
-        filename = os.path.expanduser(filename)
-        return (filename, open(filename))
-
-    config = {}
-    configfile_name = os.path.expandvars(configfile_name)
-    configfile_name = os.path.expanduser(configfile_name)
-    with open(configfile_name) as configfile:
-        ## TODO:  deal with $BASH_SOURCE
-        lexer = shlex.shlex(configfile)
-        lexer.whitespace_split = True
-        lexer.source = 'source'
-        lexer.sourcehook = sourcehook
-        for token in lexer:
-            if '=' in token:
-                (key, val) = token.split('=', 1)
-                val = val.strip('"\'')
-                if not config.get(key):
-                    config[key] = Template(val).safe_substitute(config)
-    return config
 
 class EucalyptusRequest(Euca2oolsRequest, TabifyingCommand):
     SERVICE_CLASS = Eucalyptus
 
-    # For compatibility with euca2ools versions earlier than 3, we include the
-    # old -a/--access-key/-s args.  As before, if either -a or -s conflicts
-    # with another arg, both are capitalized.  All are deprecated.  They are no
-    # longer documented and using them will result in warnings.
-    ARGS = [Arg('-a', '--access-key', metavar='KEY_ID',
-                dest='deprecated_key_id', route_to=SERVICE,
-                help=argparse.SUPPRESS),
-            Arg('-s', metavar='KEY', dest='deprecated_sec_key',
-                route_to=SERVICE, help=argparse.SUPPRESS),
-            Arg('--config', dest='shell_configfile', metavar='CFGFILE',
-                 route_to=SERVICE, help=argparse.SUPPRESS),
-            MutuallyExclusiveArgList(
-                Arg('--region', dest='regionspec', metavar='REGION',
-                    route_to=SERVICE,
-                    help='region name to connect to, with optional identity'),
-                Arg('-U', '--url', metavar='URL', route_to=SERVICE,
-                    help='compute service endpoint URL'))] + STD_AUTH_ARGS
-
     def __init__(self, **kwargs):
-        # If an inheriting class defines '-a' or '-s' args, resolve conflicts
-        # with this class's old-style auth args by capitalizing this class's
-        # auth args.
-        args = self.aggregate_subclass_fields('ARGS')
-        a_args = _find_args_by_parg(args, '-a')
-        s_args = _find_args_by_parg(args, '-s')
-        if len(a_args) > 1 or len(s_args) > 1:
-            for arg in a_args:
-                if arg.kwargs.get('dest') == 'deprecated_key_id':
-                    arg.pargs = tuple('-A' if parg == '-a' else parg
-                                      for parg in arg.pargs)
-            for arg in s_args:
-                if arg.kwargs.get('dest') == 'deprecated_sec_key':
-                    arg.kwargs['dest'] = argparse.SUPPRESS
         Euca2oolsRequest.__init__(self, **kwargs)
         self.method = 'POST'  ## FIXME
 
     def parse_http_response(self, response_body):
         response = Euca2oolsRequest.parse_http_response(self, response_body)
         # Compute cloud controller responses enclose their useful data inside
-        # FooResponse # elements.  If that's all we have after stripping out
+        # FooResponse elements.  If that's all we have after stripping out
         # RequestId then just return its contents.
         useful_keys = filter(lambda x: x != 'RequestId', response.keys())
         if len(useful_keys) == 1:
@@ -197,8 +191,8 @@ class EucalyptusRequest(Euca2oolsRequest, TabifyingCommand):
             self.print_instance(instance)
 
     def print_instance(self, instance):
-        ## FIXME: Amazon's documentation doesn't say what order the fields in
-        ##        ec2-describe-instances output appear.
+        # FIXME: Amazon's documentation doesn't say what order the fields in
+        #        ec2-describe-instances output appear.
         instance_line = ['INSTANCE']
         for key in ['instanceId', 'imageId', 'dnsName', 'privateDnsName']:
             instance_line.append(instance.get(key))
@@ -332,3 +326,29 @@ def _find_args_by_parg(arglike, parg):
         return matches
     else:
         raise TypeError('Unsearchable type ' + arglike.__class__.__name__)
+
+def _parse_shell_configfile(configfile_name):
+    # Should be able to drop this in 3.2
+    def sourcehook(filename):
+        filename = filename.strip('"\'')
+        filename = Template(filename).safe_substitute(config)
+        filename = os.path.expandvars(filename)
+        filename = os.path.expanduser(filename)
+        return (filename, open(filename))
+
+    config = {}
+    configfile_name = os.path.expandvars(configfile_name)
+    configfile_name = os.path.expanduser(configfile_name)
+    with open(configfile_name) as configfile:
+        ## TODO:  deal with $BASH_SOURCE
+        lexer = shlex.shlex(configfile)
+        lexer.whitespace_split = True
+        lexer.source = 'source'
+        lexer.sourcehook = sourcehook
+        for token in lexer:
+            if '=' in token:
+                (key, val) = token.split('=', 1)
+                val = val.strip('"\'')
+                if not config.get(key):
+                    config[key] = Template(val).safe_substitute(config)
+    return config
diff --git a/euca2ools/commands/euca/allocateaddress.py b/euca2ools/commands/euca/allocateaddress.py
index 3e12429..cd7a837 100644
--- a/euca2ools/commands/euca/allocateaddress.py
+++ b/euca2ools/commands/euca/allocateaddress.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -31,7 +31,7 @@
 import euca2ools.commands.euca
 
 class AllocateAddress(euca2ools.commands.euca.EucalyptusRequest):
-    Description = 'Allocate a public IP address'
+    DESCRIPTION = 'Allocate a public IP address'
 
     def print_result(self, result):
         print self.tabify(('ADDRESS', result.get('publicIp'),
diff --git a/euca2ools/commands/euca/associateaddress.py b/euca2ools/commands/euca/associateaddress.py
index 9a54a06..3aae1f2 100644
--- a/euca2ools/commands/euca/associateaddress.py
+++ b/euca2ools/commands/euca/associateaddress.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/attachvolume.py b/euca2ools/commands/euca/attachvolume.py
index 8a4e558..c28644c 100644
--- a/euca2ools/commands/euca/attachvolume.py
+++ b/euca2ools/commands/euca/attachvolume.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/authorize.py b/euca2ools/commands/euca/authorize.py
index 74d90bc..d4405cf 100644
--- a/euca2ools/commands/euca/authorize.py
+++ b/euca2ools/commands/euca/authorize.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -31,5 +31,5 @@
 from .modgroup import ModifySecurityGroupRequest
 
 class Authorize(ModifySecurityGroupRequest):
-    Description = 'Authorize a rule for a security group'
-    Action = 'AuthorizeSecurityGroupIngress'
+    NAME = 'AuthorizeSecurityGroupIngress'
+    DESCRIPTION = 'Authorize a rule for a security group'
diff --git a/euca2ools/commands/euca/bundleinstance.py b/euca2ools/commands/euca/bundleinstance.py
index 4813478..596ded0 100644
--- a/euca2ools/commands/euca/bundleinstance.py
+++ b/euca2ools/commands/euca/bundleinstance.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -76,8 +76,9 @@ class BundleInstance(EucalyptusRequest):
                                  ['starts-with', '$key',
                                   self.args.get('Storage.S3.Prefix')]],
                   'expiration': expire_time.isoformat()}
-        self.args['Storage.S3.UploadPolicy'] = \
-                base64.b64encode(json.dumps(policy))
+        policy_json = json.dumps(policy)
+        self.log.info('generated default policy: %s', policy_json)
+        self.args['Storage.S3.UploadPolicy'] = base64.b64encode(policy_json)
 
     def sign_policy(self):
         my_hmac = hmac.new(self.args['owner_sak'], digestmod=hashlib.sha1)
@@ -85,19 +86,22 @@ class BundleInstance(EucalyptusRequest):
         self.args['Storage.S3.UploadPolicySignature'] = \
                 base64.b64encode(my_hmac.digest())
 
-    def main(self):
+    def configure(self):
+        EucalyptusRequest.configure(self)
         if not self.args.get('Storage.S3.UploadPolicy'):
             if not self.args.get('owner_sak'):
                 self._cli_parser.error('argument -w/--owner-sak is required '
                                        'when -c/--policy is not used')
-            self.generate_default_policy()
-            self.sign_policy()
         elif not self.args.get('Storage.S3.UploadPolicySignature'):
             if not self.args.get('owner_sak'):
                 self._cli_parser.error('argument -w/--owner-sak is required '
                                        'when -c/--policy is not used')
+
+    def preprocess(self):
+        if not self.args.get('Storage.S3.UploadPolicy'):
+            self.generate_default_policy()
+        if not self.args.get('Storage.S3.UploadPplicySignature'):
             self.sign_policy()
-        return self.send()
 
     def print_result(self, result):
         self.print_bundle_task(result['bundleInstanceTask'])
diff --git a/euca2ools/commands/euca/cancelbundletask.py b/euca2ools/commands/euca/cancelbundletask.py
index e402f15..caf5d85 100644
--- a/euca2ools/commands/euca/cancelbundletask.py
+++ b/euca2ools/commands/euca/cancelbundletask.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/confirmproductinstance.py b/euca2ools/commands/euca/confirmproductinstance.py
index 55edbbb..68a8feb 100644
--- a/euca2ools/commands/euca/confirmproductinstance.py
+++ b/euca2ools/commands/euca/confirmproductinstance.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/createimage.py b/euca2ools/commands/euca/createimage.py
index 944bc9a..034b903 100644
--- a/euca2ools/commands/euca/createimage.py
+++ b/euca2ools/commands/euca/createimage.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/createkeypair.py b/euca2ools/commands/euca/createkeypair.py
index 0812ffb..03b2172 100644
--- a/euca2ools/commands/euca/createkeypair.py
+++ b/euca2ools/commands/euca/createkeypair.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/createsecuritygroup.py b/euca2ools/commands/euca/createsecuritygroup.py
index d027ace..cce7565 100644
--- a/euca2ools/commands/euca/createsecuritygroup.py
+++ b/euca2ools/commands/euca/createsecuritygroup.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/createsnapshot.py b/euca2ools/commands/euca/createsnapshot.py
index 5ad3cb5..b40d0fc 100644
--- a/euca2ools/commands/euca/createsnapshot.py
+++ b/euca2ools/commands/euca/createsnapshot.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/createtags.py b/euca2ools/commands/euca/createtags.py
index 2e04912..b68f910 100644
--- a/euca2ools/commands/euca/createtags.py
+++ b/euca2ools/commands/euca/createtags.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/createvolume.py b/euca2ools/commands/euca/createvolume.py
index 49df098..e1ec9da 100644
--- a/euca2ools/commands/euca/createvolume.py
+++ b/euca2ools/commands/euca/createvolume.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -42,11 +42,11 @@ class CreateVolume(EucalyptusRequest):
                 required=True,
                 help='availability zone in which to create the new volume')]
 
-    def main(self):
+    def configure(self):
+        EucalyptusRequest.configure(self)
         if not self.args.get('Size') and not self.args.get('SnapshotId'):
             self._cli_parser.error('at least one of -s/--size and --snapshot '
                                    'must be specified')
-        return self.send()
 
     def print_result(self, result):
         print self.tabify(['VOLUME', result.get('volumeId'),
diff --git a/euca2ools/commands/euca/deletekeypair.py b/euca2ools/commands/euca/deletekeypair.py
index 168aae9..4517bbf 100644
--- a/euca2ools/commands/euca/deletekeypair.py
+++ b/euca2ools/commands/euca/deletekeypair.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/deletesecuritygroup.py b/euca2ools/commands/euca/deletesecuritygroup.py
index c2ee5c1..9ce27aa 100644
--- a/euca2ools/commands/euca/deletesecuritygroup.py
+++ b/euca2ools/commands/euca/deletesecuritygroup.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/deletesnapshot.py b/euca2ools/commands/euca/deletesnapshot.py
index 63081bc..99d0b2f 100644
--- a/euca2ools/commands/euca/deletesnapshot.py
+++ b/euca2ools/commands/euca/deletesnapshot.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/deletetags.py b/euca2ools/commands/euca/deletetags.py
index 915c76d..1f96ae1 100644
--- a/euca2ools/commands/euca/deletetags.py
+++ b/euca2ools/commands/euca/deletetags.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/deletevolume.py b/euca2ools/commands/euca/deletevolume.py
index f9ff72d..2b7cbe1 100644
--- a/euca2ools/commands/euca/deletevolume.py
+++ b/euca2ools/commands/euca/deletevolume.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/deregisterimage.py b/euca2ools/commands/euca/deregisterimage.py
index c2dd5f2..c52a08e 100644
--- a/euca2ools/commands/euca/deregisterimage.py
+++ b/euca2ools/commands/euca/deregisterimage.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describeaddresses.py b/euca2ools/commands/euca/describeaddresses.py
index b96cede..546298a 100644
--- a/euca2ools/commands/euca/describeaddresses.py
+++ b/euca2ools/commands/euca/describeaddresses.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -46,7 +46,7 @@ class DescribeAddresses(EucalyptusRequest):
                Filter('public-ip', help='the elastic IP address')]
     LIST_MARKERS = ['addressesSet']
 
-    def main(self):
+    def preprocess(self):
         alloc_ids = set(addr for addr in self.args.get('address', [])
                         if addr.startswith('eipalloc-'))
         public_ips = set(self.args.get('address', [])) - alloc_ids
@@ -55,7 +55,6 @@ class DescribeAddresses(EucalyptusRequest):
             self.params['AllocationId'] = list(alloc_ids)
         if public_ips:
             self.params['PublicIp'] = list(public_ips)
-        return self.send()
 
     def print_result(self, result):
         for addr in result.get('addressesSet', []):
diff --git a/euca2ools/commands/euca/describeavailabilityzones.py b/euca2ools/commands/euca/describeavailabilityzones.py
index 03be5ce..024c927 100644
--- a/euca2ools/commands/euca/describeavailabilityzones.py
+++ b/euca2ools/commands/euca/describeavailabilityzones.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describebundletasks.py b/euca2ools/commands/euca/describebundletasks.py
index 3352c29..1763d8f 100644
--- a/euca2ools/commands/euca/describebundletasks.py
+++ b/euca2ools/commands/euca/describebundletasks.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describeimageattribute.py b/euca2ools/commands/euca/describeimageattribute.py
index ae7875f..6449ff7 100644
--- a/euca2ools/commands/euca/describeimageattribute.py
+++ b/euca2ools/commands/euca/describeimageattribute.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describeimages.py b/euca2ools/commands/euca/describeimages.py
index f7fcefc..b0bed63 100644
--- a/euca2ools/commands/euca/describeimages.py
+++ b/euca2ools/commands/euca/describeimages.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -98,6 +98,19 @@ class DescribeImages(EucalyptusRequest):
                       help='image\'s hypervisor type ("ovm" or "xen")')]
     LIST_MARKERS = ['imagesSet', 'blockDeviceMapping', 'tagSet']
 
+    def configure(self):
+        EucalyptusRequest.configure(self)
+        if self.args['all']:
+            if self.args.get('ImageId'):
+                self._cli_parser.error('argument -a/--all: not allowed with '
+                                       'a list of images')
+            if self.args.get('ExecutableBy'):
+                self._cli_parser.error('argument -a/--all: not allowed with '
+                                       'argument -x/--executable-by')
+            if self.args.get('Owner'):
+                self._cli_parser.error('argument -a/--all: not allowed with '
+                                       'argument -o/--owner')
+
     def main(self):
         if not any(self.args.get(item) for item in ('all', 'ImageId',
                                                     'ExecutableBy', 'Owner')):
@@ -110,17 +123,8 @@ class DescribeImages(EucalyptusRequest):
             owned['imagesSet'] = (owned.get(     'imagesSet', []) +
                                   executable.get('imagesSet', []))
             return owned
-        if self.args['all']:
-            if self.args.get('ImageId'):
-                self._cli_parser.error('argument -a/--all: not allowed with '
-                                       'a list of images')
-            if self.args.get('ExecutableBy'):
-                self._cli_parser.error('argument -a/--all: not allowed with '
-                                       'argument -x/--executable-by')
-            if self.args.get('Owner'):
-                self._cli_parser.error('argument -a/--all: not allowed with '
-                                       'argument -o/--owner')
-        return self.send()
+        else:
+            return self.send()
 
     def print_result(self, result):
         for image in result.get('imagesSet', []):
diff --git a/euca2ools/commands/euca/describeinstances.py b/euca2ools/commands/euca/describeinstances.py
index 8c230cb..f75c3ad 100644
--- a/euca2ools/commands/euca/describeinstances.py
+++ b/euca2ools/commands/euca/describeinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describekeypairs.py b/euca2ools/commands/euca/describekeypairs.py
index edf019a..7967608 100644
--- a/euca2ools/commands/euca/describekeypairs.py
+++ b/euca2ools/commands/euca/describekeypairs.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describeregions.py b/euca2ools/commands/euca/describeregions.py
index f48e358..6cb5d94 100644
--- a/euca2ools/commands/euca/describeregions.py
+++ b/euca2ools/commands/euca/describeregions.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describesecuritygroups.py b/euca2ools/commands/euca/describesecuritygroups.py
index f38a6e8..a6a8978 100644
--- a/euca2ools/commands/euca/describesecuritygroups.py
+++ b/euca2ools/commands/euca/describesecuritygroups.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -39,7 +39,7 @@ class DescribeSecurityGroups(EucalyptusRequest):
         "--filter ip-permission.from-port=22" will *not* match a group with a
         port range of 20 to 30.'''
 
-    API_VERSION = '2011-01-01'
+    API_VERSION = '2012-12-01'
     ARGS = [Arg('group', metavar='GROUP', nargs='*', route_to=None, default=[],
                 help='limit results to one or more security groups')]
     FILTERS = [Filter('description', help='group description'),
@@ -65,17 +65,14 @@ class DescribeSecurityGroups(EucalyptusRequest):
     LIST_MARKERS = ['securityGroupInfo', 'ipPermissions',
                     'ipPermissionsEgress', 'groups', 'ipRanges']
 
-    def main(self):
-        self.params = {}
+    def preprocess(self):
         for group in self.args['group']:
-            # Uncomment this during the next API version bump
-            #if group.startswith('sg-'):
-            #    self.params.setdefault('GroupId', [])
-            #    self.params['GroupId'].append(group)
-            #else:
+            if group.startswith('sg-'):
+                self.params.setdefault('GroupId', [])
+                self.params['GroupId'].append(group)
+            else:
                 self.params.setdefault('GroupName', [])
                 self.params['GroupName'].append(group)
-        return self.send()
 
     def print_result(self, result):
         for group in result.get('securityGroupInfo', []):
diff --git a/euca2ools/commands/euca/describesnapshots.py b/euca2ools/commands/euca/describesnapshots.py
index 3962116..404e80b 100644
--- a/euca2ools/commands/euca/describesnapshots.py
+++ b/euca2ools/commands/euca/describesnapshots.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -65,7 +65,8 @@ class DescribeSnapshots(EucalyptusRequest):
                Filter('volume-size', type=int)]
     LIST_MARKERS = ['snapshotSet', 'tagSet']
 
-    def main(self):
+    def configure(self):
+        EucalyptusRequest.configure(self)
         if not any(self.args.get(item) for item in ('all', 'Owner',
                                                     'RestorableBy')):
             # Default to restorable snapshots
@@ -77,7 +78,6 @@ class DescribeSnapshots(EucalyptusRequest):
             if self.args.get('RestorableBy'):
                 self._cli_parser.error('argument -a/--all: not allowed with '
                                        'argument -r/--restorable-by')
-        return self.send()
 
     def print_result(self, result):
         for snapshot in result.get('snapshotSet', []):
diff --git a/euca2ools/commands/euca/describetags.py b/euca2ools/commands/euca/describetags.py
index 5351727..786c31f 100644
--- a/euca2ools/commands/euca/describetags.py
+++ b/euca2ools/commands/euca/describetags.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/describevolumes.py b/euca2ools/commands/euca/describevolumes.py
index 8831b42..f792b01 100644
--- a/euca2ools/commands/euca/describevolumes.py
+++ b/euca2ools/commands/euca/describevolumes.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/detachvolume.py b/euca2ools/commands/euca/detachvolume.py
index 8931a23..909b6b8 100644
--- a/euca2ools/commands/euca/detachvolume.py
+++ b/euca2ools/commands/euca/detachvolume.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/disassociateaddress.py b/euca2ools/commands/euca/disassociateaddress.py
index 8b5a27f..ff65c10 100644
--- a/euca2ools/commands/euca/disassociateaddress.py
+++ b/euca2ools/commands/euca/disassociateaddress.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -36,12 +36,11 @@ class DisassociateAddress(EucalyptusRequest):
     ARGS = [Arg('address', route_to=None,
                 help='elastic IP address or association ID to disassociate')]
 
-    def main(self):
+    def preprocess(self):
         if self.args['address'].startswith('eipassoc'):
             self.params = {'AssociationId': self.args['address']}
         else:
             self.params = {'PublicIp':      self.args['address']}
-        return self.send()
 
     def print_result(self, result):
         print self.tabify(['ADDRESS', self.args['address']])
diff --git a/euca2ools/commands/euca/getconsoleoutput.py b/euca2ools/commands/euca/getconsoleoutput.py
index 3d4e5db..415e16d 100644
--- a/euca2ools/commands/euca/getconsoleoutput.py
+++ b/euca2ools/commands/euca/getconsoleoutput.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/getpassword.py b/euca2ools/commands/euca/getpassword.py
index 1a8e014..2d353f5 100644
--- a/euca2ools/commands/euca/getpassword.py
+++ b/euca2ools/commands/euca/getpassword.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/getpassworddata.py b/euca2ools/commands/euca/getpassworddata.py
index cbf2e90..ae60438 100644
--- a/euca2ools/commands/euca/getpassworddata.py
+++ b/euca2ools/commands/euca/getpassworddata.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/importkeypair.py b/euca2ools/commands/euca/importkeypair.py
index 7b38d50..b422f3b 100644
--- a/euca2ools/commands/euca/importkeypair.py
+++ b/euca2ools/commands/euca/importkeypair.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -42,10 +42,9 @@ class ImportKeyPair(EucalyptusRequest):
                 type=file_contents, required=True, route_to=None,
                 help='file name of the public key to import')]
 
-    def main(self):
+    def preprocess(self):
         self.params = {'PublicKeyMaterial':
                         base64.b64encode(self.args['pubkey'])}
-        return self.send()
 
     def print_result(self, result):
         print self.tabify(['KEYPAIR', result.get('keyName'),
diff --git a/euca2ools/commands/euca/modgroup.py b/euca2ools/commands/euca/modgroup.py
index 66ddbf4..c62d9a2 100644
--- a/euca2ools/commands/euca/modgroup.py
+++ b/euca2ools/commands/euca/modgroup.py
@@ -65,12 +65,9 @@ class ModifySecurityGroupRequest(EucalyptusRequest):
                         specified with -o''')]
                 # ^ required if -o is used
 
-    def __init__(self, **kwargs):
-        EucalyptusRequest.__init__(self, **kwargs)
-        self.icmp_opt = None
-        self.port_opt = None
+    def configure(self):
+        EucalyptusRequest.configure(self)
 
-    def parse_ports(self):
         from_port = None
         to_port   = None
         protocol = self.args.get('IpPermissions.1.IpProtocol')
@@ -126,15 +123,9 @@ class ModifySecurityGroupRequest(EucalyptusRequest):
                 self._cli_parser.error('argument -p/--port-range: port '
                                        'number(s) must be at least -1')
 
-        self.params = {'IpPermissions.1.FromPort': from_port,
-                       'IpPermissions.1.ToPort':   to_port}
+        self.params['IpPermissions.1.FromPort'] = from_port
+        self.params['IpPermissions.1.ToPort']   = to_port
 
-    def main(self):
-        if self.icmp_opt:
-            self.args['icmp_type_code'] = self.icmp_opt
-        if self.port_opt:
-            self.args['port_range'] = self.port_opt
-        self.parse_ports()
         if not self.args.get('IpPermissions.1.IpRanges.1.GroupName'):
             self.args.setdefault('IpPermissions.1.IpRanges.1.CidrIp',
                                  '0.0.0.0/0')
@@ -142,7 +133,6 @@ class ModifySecurityGroupRequest(EucalyptusRequest):
             not self.args.get('IpPermissions.1.Groups.1.UserId')):
             self._cli_parser.error('argument -u is required when -o is '
                                    'specified')
-        return self.send()
 
     def print_result(self, result):
         print self.tabify(['GROUP', self.args.get('GroupName')])
@@ -162,7 +152,7 @@ class ModifySecurityGroupRequest(EucalyptusRequest):
             perm_str.append(self.args.get('IpPermissions.1.IpRanges.1.CidrIp'))
         print self.tabify(perm_str)
 
-    def do_cli(self):
+    def process_cli_args(self):
         # We need to parse out -t and -p *before* argparse can see it because
         # of Python bug 9334, which prevents argparse from recognizing '-1:-1'
         # as an option value and not a (nonexistent) option name.
@@ -174,8 +164,12 @@ class ModifySecurityGroupRequest(EucalyptusRequest):
                     opt_val = sys.argv[index + 1]
                     del sys.argv[index:index + 2]
                     return opt_val
-        self.icmp_opt = parse_neg_one_value('-t') or self.icmp_opt
-        self.icmp_opt = parse_neg_one_value('--icmp-type-code') or self.icmp_opt
-        self.port_opt = parse_neg_one_value('-p') or self.port_opt
-        self.port_opt = parse_neg_one_value('--port-range') or self.port_opt
-        EucalyptusRequest.do_cli(self)
+        icmp_type_code = (parse_neg_one_value('-t') or
+                          parse_neg_one_value('--icmp-type-code'))
+        port_range = (parse_neg_one_value('-p') or
+                      parse_neg_one_value('--port-range'))
+        EucalyptusRequest._process_cli_args(self)
+        if icmp_type_code:
+            self.args['icmp_type_code'] = icmp_type_code
+        if port_range:
+            self.args['port_range'] = port_range
diff --git a/euca2ools/commands/euca/modifyimageattribute.py b/euca2ools/commands/euca/modifyimageattribute.py
index 9b854d3..a6265ab 100644
--- a/euca2ools/commands/euca/modifyimageattribute.py
+++ b/euca2ools/commands/euca/modifyimageattribute.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -50,7 +50,7 @@ class ModifyImageAttribute(EucalyptusRequest):
                 default=[], route_to=None, help='''account to remove launch
                 permission from , or "all" for all accounts''')]
 
-    def main(self):
+    def preprocess(self):
         if self.args.get('launch_permission'):
             lp = {}
             for entity in self.args.get('add', []):
@@ -68,7 +68,7 @@ class ModifyImageAttribute(EucalyptusRequest):
             if not lp:
                 self._cli_parser.error('at least one entity must be specified '
                                        'with -a/--add or -r/--remove')
-            self.params = {'LaunchPermission': lp}
+            self.params['LaunchPermission'] = lp
         else:
             if self.args.get('add'):
                 self._cli_parser.error('argument -a/--add may only be used '
@@ -76,7 +76,6 @@ class ModifyImageAttribute(EucalyptusRequest):
             if self.args.get('remove'):
                 self._cli_parser.error('argument -r/--remove may only be used '
                                        'with -l/--launch-permission')
-        return self.send()
 
     def print_result(self, result):
         if self.args.get('Description.Value'):
diff --git a/euca2ools/commands/euca/monitorinstances.py b/euca2ools/commands/euca/monitorinstances.py
index 7d5d208..b804d43 100644
--- a/euca2ools/commands/euca/monitorinstances.py
+++ b/euca2ools/commands/euca/monitorinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/rebootinstances.py b/euca2ools/commands/euca/rebootinstances.py
index e6b4622..5a8c134 100644
--- a/euca2ools/commands/euca/rebootinstances.py
+++ b/euca2ools/commands/euca/rebootinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/registerimage.py b/euca2ools/commands/euca/registerimage.py
index 390aa1a..fecba87 100644
--- a/euca2ools/commands/euca/registerimage.py
+++ b/euca2ools/commands/euca/registerimage.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -60,7 +60,7 @@ class RegisterImage(EucalyptusRequest):
                 form DEVICE=MAPPED, where "MAPPED" is "none", "ephemeral(0-3)",
                 or "[SNAP-ID]:[SIZE]:[true|false]"''')]
 
-    def main(self):
+    def preprocess(self):
         if self.args.get('ImageLocation'):
             # instance-store image
             if self.args.get('RootDeviceName'):
@@ -80,7 +80,7 @@ class RegisterImage(EucalyptusRequest):
                     if (snapshot and
                         snapshot != mapping.get('Ebs', {}).get('SnapshotId')):
                         # The mapping's snapshot differs or doesn't exist
-                        self._cli_parser.error('value supplied with '
+                        self._cli_parser.error('snapshot ID supplied with '
                                 '--snapshot conflicts with block device '
                                 'mapping for root device ' +
                                 mapping['DeviceName'])
@@ -96,7 +96,6 @@ class RegisterImage(EucalyptusRequest):
                 else:
                     self._cli_parser.error('either a manifest location or a '
                             'root device snapshot mapping must be specified')
-        return self.send()
 
     def print_result(self, result):
         print self.tabify(('IMAGE', result.get('imageId')))
diff --git a/euca2ools/commands/euca/releaseaddress.py b/euca2ools/commands/euca/releaseaddress.py
index 2b33980..646f099 100644
--- a/euca2ools/commands/euca/releaseaddress.py
+++ b/euca2ools/commands/euca/releaseaddress.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/resetimageattribute.py b/euca2ools/commands/euca/resetimageattribute.py
index 3cacbcb..474b300 100644
--- a/euca2ools/commands/euca/resetimageattribute.py
+++ b/euca2ools/commands/euca/resetimageattribute.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/revoke.py b/euca2ools/commands/euca/revoke.py
index e8981cd..c1630c6 100644
--- a/euca2ools/commands/euca/revoke.py
+++ b/euca2ools/commands/euca/revoke.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -31,5 +31,5 @@
 from .modgroup import ModifySecurityGroupRequest
 
 class Revoke(ModifySecurityGroupRequest):
-    Description = 'Revoke an existing rule from a security group'
-    Action = 'RevokeSecurityGroupIngress'
+    NAME = 'RevokeSecurityGroupIngress'
+    DESCRIPTION = 'Revoke an existing rule from a security group'
diff --git a/euca2ools/commands/euca/runinstances.py b/euca2ools/commands/euca/runinstances.py
index abb340e..a92c4f0 100644
--- a/euca2ools/commands/euca/runinstances.py
+++ b/euca2ools/commands/euca/runinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
@@ -46,8 +46,7 @@ class RunInstances(EucalyptusRequest):
                         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', dest='group', action='append', default=[],
-                route_to=None,
+            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'),
@@ -58,7 +57,7 @@ class RunInstances(EucalyptusRequest):
                             reservation'''),
                 Arg('--user-data-force', dest='UserData',
                     type=base64.b64encode, help=argparse.SUPPRESS),
-                    # ^ deprecated
+                    # ^ 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
@@ -81,7 +80,7 @@ class RunInstances(EucalyptusRequest):
                         "[SNAP-ID]:[SIZE]:[true|false]"'''),
             Arg('-m', '--monitor', dest='Monitoring.Enabled',
                 action='store_const', const='true',
-                help='enable monitoring for the instance(s)'),
+                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',
@@ -89,8 +88,7 @@ class RunInstances(EucalyptusRequest):
     LIST_MARKERS = ['reservationSet', 'instancesSet', 'groupSet', 'tagSet',
                     'blockDeviceMapping', 'productCodes']
 
-    def main(self):
-        self.params = {}
+    def preprocess(self):
         counts = self.args['count'].split('-')
         if len(counts) == 1:
             try:
@@ -98,7 +96,7 @@ class RunInstances(EucalyptusRequest):
                 self.params['MaxCount'] = int(counts[0])
             except ValueError:
                 self._cli_parser.error('argument -n/--instance-count: '
-                                       'instance count must be in integer')
+                                       'instance count must be an integer')
         elif len(counts) == 2:
             try:
                 self.params['MinCount'] = int(counts[0])
@@ -113,17 +111,18 @@ class RunInstances(EucalyptusRequest):
         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']})
 
         for group in self.args['group']:
-            # Uncomment this during the next API version bump
-            #if group.startswith('sg-'):
-            #    self.params.setdefault('SecurityGroupId', [])
-            #    self.params['SecurityGroupId'].append(group)
-            #else:
+            if group.startswith('sg-'):
+                self.params.setdefault('SecurityGroupId', [])
+                self.params['SecurityGroupId'].append(group)
+            else:
                 self.params.setdefault('SecurityGroup', [])
                 self.params['SecurityGroup'].append(group)
 
-        return self.send()
-
     def print_result(self, result):
         self.print_reservation(result)
diff --git a/euca2ools/commands/euca/startinstances.py b/euca2ools/commands/euca/startinstances.py
index 8471838..3ee91e0 100644
--- a/euca2ools/commands/euca/startinstances.py
+++ b/euca2ools/commands/euca/startinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/stopinstances.py b/euca2ools/commands/euca/stopinstances.py
index 13a96f2..301642f 100644
--- a/euca2ools/commands/euca/stopinstances.py
+++ b/euca2ools/commands/euca/stopinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/terminateinstances.py b/euca2ools/commands/euca/terminateinstances.py
index 50d3038..3a2401e 100644
--- a/euca2ools/commands/euca/terminateinstances.py
+++ b/euca2ools/commands/euca/terminateinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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
diff --git a/euca2ools/commands/euca/unmonitorinstances.py b/euca2ools/commands/euca/unmonitorinstances.py
index a822215..dee2e56 100644
--- a/euca2ools/commands/euca/unmonitorinstances.py
+++ b/euca2ools/commands/euca/unmonitorinstances.py
@@ -1,6 +1,6 @@
 # Software License Agreement (BSD License)
 #
-# Copyright (c) 2009-2012, 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

-- 
managing cloud instances for Eucalyptus



More information about the pkg-eucalyptus-commits mailing list