diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index b3ced54398..29cb0aa346 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -19,6 +19,10 @@ Server action implementations """ +import logging + +from cliff.command import Command + from openstackclient.common import utils @@ -53,9 +57,41 @@ def _print_server(cs, server): utils.print_dict(info) -@utils.arg('server', metavar='<server>', help='Name or ID of server.') -def do_show_server(cs, args): - """Show details about the given server.""" - print "do_show_server(%s)" % args.server - #s = _find_server(cs, args.server) - #_print_server(cs, s) +class List_Server(Command): + "List server command." + + log = logging.getLogger(__name__) + + def get_parser(self, prog_name): + parser = super(List_Server, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help='Additional fields are listed in output') + return parser + + def run(self, parsed_args): + self.log.info('List_Server()') + self.log.info(' run(%s)' % parsed_args) + self.app.stdout.write('hi!\n') + +class Show_Server(Command): + "Show server command." + + log = logging.getLogger(__name__) + + def get_parser(self, prog_name): + parser = super(Show_Server, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='<server>', + help='Name or ID of server to display') + return parser + + def run(self, parsed_args): + self.log.info('Show_Server()') + self.log.info(' run(%s)' % parsed_args) + self.app.stdout.write('hi!\n') + #s = _find_server(cs, args.server) + #_print_server(cs, s) diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 29ff854f6b..0aac364639 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -16,17 +16,23 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 """ -Command-line interface to the OpenStack Identity, Compute and Storage APIs +Command-line interface to the OpenStack APIs """ -import argparse -import httplib2 +import logging +import optparse import os import sys +from cliff.app import App +from cliff.commandmanager import CommandManager + from openstackclient.common import utils +VERSION = '0.1' + + def env(*vars, **kwargs): """Search for the first defined of possibly many env vars @@ -41,265 +47,98 @@ def env(*vars, **kwargs): return kwargs.get('default', '') -class OpenStackShell(object): +class OpenStackShell(App): - def _find_actions(self, subparsers, actions_module): - if self.debug: - print "_find_actions(module: %s)" % actions_module - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - # I prefer to be hypen-separated instead of underscores. - command = attr[3:].replace('_', '-') - cmd = command.split('-', 1) - action = cmd[0] - if len(cmd) > 1: - subject = cmd[1] - else: - subject = '' - callback = getattr(actions_module, attr) - desc = callback.__doc__ or '' - help = desc.strip().split('\n')[0] - arguments = getattr(callback, 'arguments', []) + log = logging.getLogger(__name__) - if self.debug: - print " command: %s" % command - print " action: %s" % action - print " subject: %s" % subject - print " arguments: %s" % arguments - - subparser = subparsers.add_parser(command, - help=help, - description=desc, - add_help=False, - formatter_class=OpenStackHelpFormatter - ) - subparser.add_argument('-h', '--help', - action='help', - help=argparse.SUPPRESS, - ) - self.subcommands[command] = subparser - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - @utils.arg('command', metavar='<subcommand>', nargs='?', - help='Display help for <subcommand>') - def do_help(self, args): - """ - Display help about this program or one of its subcommands. - """ - if getattr(args, 'command', None): - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - raise exc.CommandError("'%s' is not a valid subcommand" % - args.command) - else: - self.parser.print_help() - - def get_base_parser(self): - parser = argparse.ArgumentParser( - prog='stack', + def __init__(self): + super(OpenStackShell, self).__init__( description=__doc__.strip(), - epilog='See "stack help COMMAND" ' - 'for help on a specific command.', - add_help=False, - formatter_class=OpenStackHelpFormatter, - ) + version=VERSION, + command_manager=CommandManager('openstack.cli'), + ) + + def build_option_parser(self, description, version): + parser = super(OpenStackShell, self).build_option_parser( + description, + version, + ) # Global arguments - parser.add_argument('-h', '--help', - action='store_true', - help=argparse.SUPPRESS, - ) - - parser.add_argument('--os-auth-url', metavar='<auth-url>', + parser.add_option('--os-auth-url', metavar='<auth-url>', default=env('OS_AUTH_URL'), help='Authentication URL (Env: OS_AUTH_URL)') - parser.add_argument('--os-tenant-name', metavar='<auth-tenant-name>', + parser.add_option('--os-tenant-name', metavar='<auth-tenant-name>', default=env('OS_TENANT_NAME'), help='Authentication tenant name (Env: OS_TENANT_NAME)') - parser.add_argument('--os-tenant-id', metavar='<auth-tenant-id>', + parser.add_option('--os-tenant-id', metavar='<auth-tenant-id>', default=env('OS_TENANT_ID'), help='Authentication tenant ID (Env: OS_TENANT_ID)') - parser.add_argument('--os-username', metavar='<auth-username>', + parser.add_option('--os-username', metavar='<auth-username>', default=utils.env('OS_USERNAME'), help='Authentication username (Env: OS_USERNAME)') - parser.add_argument('--os-password', metavar='<auth-password>', + parser.add_option('--os-password', metavar='<auth-password>', default=utils.env('OS_PASSWORD'), help='Authentication password (Env: OS_PASSWORD)') - parser.add_argument('--os-region-name', metavar='<auth-region-name>', + parser.add_option('--os-region-name', metavar='<auth-region-name>', default=env('OS_REGION_NAME'), help='Authentication region name (Env: OS_REGION_NAME)') - parser.add_argument('--debug', - default=False, - action='store_true', - help=argparse.SUPPRESS) - - parser.add_argument('--os-identity-api-version', + parser.add_option('--os-identity-api-version', metavar='<identity-api-version>', default=env('OS_IDENTITY_API_VERSION', default='2.0'), help='Identity API version, default=2.0 (Env: OS_IDENTITY_API_VERSION)') - parser.add_argument('--os-compute-api-version', + parser.add_option('--os-compute-api-version', metavar='<compute-api-version>', default=env('OS_COMPUTE_API_VERSION', default='2'), help='Compute API version, default=2.0 (Env: OS_COMPUTE_API_VERSION)') - parser.add_argument('--os-image-api-version', + parser.add_option('--os-image-api-version', metavar='<image-api-version>', default=env('OS_IMAGE_API_VERSION', default='1.0'), help='Image API version, default=1.0 (Env: OS_IMAGE_API_VERSION)') - parser.add_argument('--service-token', metavar='<service-token>', + parser.add_option('--service-token', metavar='<service-token>', default=env('SERVICE_TOKEN'), - help=argparse.SUPPRESS) + help='deprecated') - parser.add_argument('--service-endpoint', metavar='<service-endpoint>', + parser.add_option('--service-endpoint', metavar='<service-endpoint>', default=env('SERVICE_ENDPOINT'), - help=argparse.SUPPRESS) - - parser.add_argument('action', metavar='<action>', - default='help', - help=argparse.SUPPRESS) - - parser.add_argument('subject', metavar='<subject>', - default='', nargs='?', - help=argparse.SUPPRESS) + help='deprecated') return parser - def get_subcommand_parser(self, cmd_subject): - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='<subcommand>') - - if cmd_subject is None or cmd_subject == '': - # TODO(dtroyer): iterate over all known subjects to produce - # the complete help list - print "Get all subjects here - exit" - exit(1) - - (module, version) = self._map_subject(cmd_subject) - if module is None or cmd_subject is None: - print "Module %s not found - exit" % cmd_subject - exit(1) - if self.debug: - print "module: %s" % module - exec("from %s.v%s import %s as cmd" % (module, self.api_version[module], cmd_subject)) - self._find_actions(subparsers, cmd) - - self._find_actions(subparsers, self) - - return parser - - def _map_subject(self, cmd_subject): - '''Convert from subject to the module that implements it''' - COMPUTE = ['server'] - IDENTITY = ['key'] - IMAGE = ['image'] - if cmd_subject in COMPUTE: - version = self.api_version['compute'].replace('.', '_') - return ('compute', version) - elif cmd_subject in IDENTITY: - version = self.api_version['identity'].replace('.', '_') - return ('identity', version) - elif cmd_subject in IMAGE: - version = self.api_version['imade'].replace('.', '_') - return ('image', version) - else: - return None - - def main(self, argv): - ''' - - get api version - - get version command set - - import version-subject module - - is verb-subject supported? - ''' - # Parse global args to find version - parser = self.get_base_parser() - (options, args) = parser.parse_known_args(argv) + def prepare_to_run_command(self, cmd): + """Set up auth and API versions""" + self.log.debug('prepare_to_run_command %s', cmd.__class__.__name__) # stash selected API versions for later # TODO(dtroyer): how do extenstions add their version requirements? self.api_version = { - 'compute': options.os_compute_api_version, - 'identity': options.os_identity_api_version, - 'image': options.os_image_api_version, + 'compute': self.options.os_compute_api_version, + 'identity': self.options.os_identity_api_version, + 'image': self.options.os_image_api_version, } - # Setup debugging - if getattr(options, 'debug', None): - self.debug = 1 - else: - self.debug = 0 - - if self.debug: + if self.options.debug: print "API: Identity=%s Compute=%s Image=%s" % (self.api_version['identity'], self.api_version['compute'], self.api_version['image']) - print "Action: %s" % options.action - print "subject: %s" % getattr(options, 'subject', '') - print "args: %s" % args + print "cmd: %s" % cmd - # Handle top-level --help/-h before attempting to parse - # a command off the command line - if getattr(options, 'help', None) or getattr(options, 'action', None) == 'help': - print "top-level help" - # Build available subcommands - self.parser = self.get_subcommand_parser(options.subject) - self.do_help(options) - return 0 - - # Build selected subcommands - self.parser = self.get_subcommand_parser(options.subject) - - # Parse args again and call whatever callback was selected - args.insert(0, '%s-%s' % (options.action, options.subject)) - if self.debug: - print "args: %s" % args - args = self.parser.parse_args(args) - - if self.debug: - print "Testing command parsing" - print "Auth username: %s" % options.os_username - #print "Action: %s" % options.action - #print "Subject: %s" % options.subject - print "args: %s" % args - -class OpenStackHelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(OpenStackHelpFormatter, self).start_section(heading) + def clean_up(self, cmd, result, err): + self.log.debug('clean_up %s', cmd.__class__.__name__) + if err: + self.log.debug('got an error: %s', err) -def main(): - try: - OpenStackShell().main(sys.argv[1:]) +def main(argv=sys.argv[1:]): + return OpenStackShell().run(argv) - except Exception, e: - if httplib2.debuglevel == 1: - raise # dump stack. - else: - print >> sys.stderr, e - sys.exit(1) - -def test_main(argv): - # The argparse/optparse/cmd2 modules muck about with sys.argv - # so we save it and restore at the end to let the tests - # run repeatedly without concatenating the args on each run - save_argv = sys.argv - - main() - - # Put it back so the next test has a clean copy - sys.argv = save_argv if __name__ == "__main__": - main() + sys.exit(main(sys.argv[1:])) diff --git a/setup.py b/setup.py index bd8620af08..6dbf5e1ee6 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,8 @@ def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() requirements = [ + 'cliff', + 'distribute', 'httplib2', 'prettytable', "python-keystoneclient >= 2012.1", @@ -43,7 +45,11 @@ setup( test_suite = "nose.collector", entry_points = { - 'console_scripts': ['stack = openstackclient.shell:main'] + 'console_scripts': ['stack = openstackclient.shell:main'], + 'openstack.cli': [ + 'list_server = openstackclient.compute.v2.server:List_Server', + 'show_server = openstackclient.compute.v2.server:Show_Server', + ] } )