1616# vim: tabstop=4 shiftwidth=4 softtabstop=4
1717
1818"""
19- Command-line interface to the OpenStack Identity, Compute and Storage APIs
19+ Command-line interface to the OpenStack APIs
2020"""
2121
22- import argparse
23- import httplib2
22+ import logging
23+ import optparse
2424import os
2525import sys
2626
27+ from cliff .app import App
28+ from cliff .commandmanager import CommandManager
29+
2730from openstackclient .common import utils
2831
2932
33+ VERSION = '0.1'
34+
35+
3036def env (* vars , ** kwargs ):
3137 """Search for the first defined of possibly many env vars
3238
@@ -41,265 +47,98 @@ def env(*vars, **kwargs):
4147 return kwargs .get ('default' , '' )
4248
4349
44- class OpenStackShell (object ):
45-
46- def _find_actions (self , subparsers , actions_module ):
47- if self .debug :
48- print "_find_actions(module: %s)" % actions_module
49- for attr in (a for a in dir (actions_module ) if a .startswith ('do_' )):
50- # I prefer to be hypen-separated instead of underscores.
51- command = attr [3 :].replace ('_' , '-' )
52- cmd = command .split ('-' , 1 )
53- action = cmd [0 ]
54- if len (cmd ) > 1 :
55- subject = cmd [1 ]
56- else :
57- subject = ''
58- callback = getattr (actions_module , attr )
59- desc = callback .__doc__ or ''
60- help = desc .strip ().split ('\n ' )[0 ]
61- arguments = getattr (callback , 'arguments' , [])
62-
63- if self .debug :
64- print " command: %s" % command
65- print " action: %s" % action
66- print " subject: %s" % subject
67- print " arguments: %s" % arguments
68-
69- subparser = subparsers .add_parser (command ,
70- help = help ,
71- description = desc ,
72- add_help = False ,
73- formatter_class = OpenStackHelpFormatter
50+ class OpenStackShell (App ):
51+
52+ log = logging .getLogger (__name__ )
53+
54+ def __init__ (self ):
55+ super (OpenStackShell , self ).__init__ (
56+ description = __doc__ .strip (),
57+ version = VERSION ,
58+ command_manager = CommandManager ('openstack.cli' ),
7459 )
75- subparser .add_argument ('-h' , '--help' ,
76- action = 'help' ,
77- help = argparse .SUPPRESS ,
60+
61+ def build_option_parser (self , description , version ):
62+ parser = super (OpenStackShell , self ).build_option_parser (
63+ description ,
64+ version ,
7865 )
79- self .subcommands [command ] = subparser
80- for (args , kwargs ) in arguments :
81- subparser .add_argument (* args , ** kwargs )
82- subparser .set_defaults (func = callback )
83-
84- @utils .arg ('command' , metavar = '<subcommand>' , nargs = '?' ,
85- help = 'Display help for <subcommand>' )
86- def do_help (self , args ):
87- """
88- Display help about this program or one of its subcommands.
89- """
90- if getattr (args , 'command' , None ):
91- if args .command in self .subcommands :
92- self .subcommands [args .command ].print_help ()
93- else :
94- raise exc .CommandError ("'%s' is not a valid subcommand" %
95- args .command )
96- else :
97- self .parser .print_help ()
98-
99- def get_base_parser (self ):
100- parser = argparse .ArgumentParser (
101- prog = 'stack' ,
102- description = __doc__ .strip (),
103- epilog = 'See "stack help COMMAND" '
104- 'for help on a specific command.' ,
105- add_help = False ,
106- formatter_class = OpenStackHelpFormatter ,
107- )
10866
10967 # Global arguments
110- parser .add_argument ('-h' , '--help' ,
111- action = 'store_true' ,
112- help = argparse .SUPPRESS ,
113- )
114-
115- parser .add_argument ('--os-auth-url' , metavar = '<auth-url>' ,
68+ parser .add_option ('--os-auth-url' , metavar = '<auth-url>' ,
11669 default = env ('OS_AUTH_URL' ),
11770 help = 'Authentication URL (Env: OS_AUTH_URL)' )
11871
119- parser .add_argument ('--os-tenant-name' , metavar = '<auth-tenant-name>' ,
72+ parser .add_option ('--os-tenant-name' , metavar = '<auth-tenant-name>' ,
12073 default = env ('OS_TENANT_NAME' ),
12174 help = 'Authentication tenant name (Env: OS_TENANT_NAME)' )
12275
123- parser .add_argument ('--os-tenant-id' , metavar = '<auth-tenant-id>' ,
76+ parser .add_option ('--os-tenant-id' , metavar = '<auth-tenant-id>' ,
12477 default = env ('OS_TENANT_ID' ),
12578 help = 'Authentication tenant ID (Env: OS_TENANT_ID)' )
12679
127- parser .add_argument ('--os-username' , metavar = '<auth-username>' ,
80+ parser .add_option ('--os-username' , metavar = '<auth-username>' ,
12881 default = utils .env ('OS_USERNAME' ),
12982 help = 'Authentication username (Env: OS_USERNAME)' )
13083
131- parser .add_argument ('--os-password' , metavar = '<auth-password>' ,
84+ parser .add_option ('--os-password' , metavar = '<auth-password>' ,
13285 default = utils .env ('OS_PASSWORD' ),
13386 help = 'Authentication password (Env: OS_PASSWORD)' )
13487
135- parser .add_argument ('--os-region-name' , metavar = '<auth-region-name>' ,
88+ parser .add_option ('--os-region-name' , metavar = '<auth-region-name>' ,
13689 default = env ('OS_REGION_NAME' ),
13790 help = 'Authentication region name (Env: OS_REGION_NAME)' )
13891
139- parser .add_argument ('--debug' ,
140- default = False ,
141- action = 'store_true' ,
142- help = argparse .SUPPRESS )
143-
144- parser .add_argument ('--os-identity-api-version' ,
92+ parser .add_option ('--os-identity-api-version' ,
14593 metavar = '<identity-api-version>' ,
14694 default = env ('OS_IDENTITY_API_VERSION' , default = '2.0' ),
14795 help = 'Identity API version, default=2.0 (Env: OS_IDENTITY_API_VERSION)' )
14896
149- parser .add_argument ('--os-compute-api-version' ,
97+ parser .add_option ('--os-compute-api-version' ,
15098 metavar = '<compute-api-version>' ,
15199 default = env ('OS_COMPUTE_API_VERSION' , default = '2' ),
152100 help = 'Compute API version, default=2.0 (Env: OS_COMPUTE_API_VERSION)' )
153101
154- parser .add_argument ('--os-image-api-version' ,
102+ parser .add_option ('--os-image-api-version' ,
155103 metavar = '<image-api-version>' ,
156104 default = env ('OS_IMAGE_API_VERSION' , default = '1.0' ),
157105 help = 'Image API version, default=1.0 (Env: OS_IMAGE_API_VERSION)' )
158106
159- parser .add_argument ('--service-token' , metavar = '<service-token>' ,
107+ parser .add_option ('--service-token' , metavar = '<service-token>' ,
160108 default = env ('SERVICE_TOKEN' ),
161- help = argparse . SUPPRESS )
109+ help = 'deprecated' )
162110
163- parser .add_argument ('--service-endpoint' , metavar = '<service-endpoint>' ,
111+ parser .add_option ('--service-endpoint' , metavar = '<service-endpoint>' ,
164112 default = env ('SERVICE_ENDPOINT' ),
165- help = argparse .SUPPRESS )
166-
167- parser .add_argument ('action' , metavar = '<action>' ,
168- default = 'help' ,
169- help = argparse .SUPPRESS )
170-
171- parser .add_argument ('subject' , metavar = '<subject>' ,
172- default = '' , nargs = '?' ,
173- help = argparse .SUPPRESS )
113+ help = 'deprecated' )
174114
175115 return parser
176116
177- def get_subcommand_parser (self , cmd_subject ):
178- parser = self .get_base_parser ()
179-
180- self .subcommands = {}
181- subparsers = parser .add_subparsers (metavar = '<subcommand>' )
182-
183- if cmd_subject is None or cmd_subject == '' :
184- # TODO(dtroyer): iterate over all known subjects to produce
185- # the complete help list
186- print "Get all subjects here - exit"
187- exit (1 )
188-
189- (module , version ) = self ._map_subject (cmd_subject )
190- if module is None or cmd_subject is None :
191- print "Module %s not found - exit" % cmd_subject
192- exit (1 )
193- if self .debug :
194- print "module: %s" % module
195- exec ("from %s.v%s import %s as cmd" % (module , self .api_version [module ], cmd_subject ))
196- self ._find_actions (subparsers , cmd )
197-
198- self ._find_actions (subparsers , self )
199-
200- return parser
201-
202- def _map_subject (self , cmd_subject ):
203- '''Convert from subject to the module that implements it'''
204- COMPUTE = ['server' ]
205- IDENTITY = ['key' ]
206- IMAGE = ['image' ]
207- if cmd_subject in COMPUTE :
208- version = self .api_version ['compute' ].replace ('.' , '_' )
209- return ('compute' , version )
210- elif cmd_subject in IDENTITY :
211- version = self .api_version ['identity' ].replace ('.' , '_' )
212- return ('identity' , version )
213- elif cmd_subject in IMAGE :
214- version = self .api_version ['imade' ].replace ('.' , '_' )
215- return ('image' , version )
216- else :
217- return None
218-
219- def main (self , argv ):
220- '''
221- - get api version
222- - get version command set
223- - import version-subject module
224- - is verb-subject supported?
225- '''
226- # Parse global args to find version
227- parser = self .get_base_parser ()
228- (options , args ) = parser .parse_known_args (argv )
117+ def prepare_to_run_command (self , cmd ):
118+ """Set up auth and API versions"""
119+ self .log .debug ('prepare_to_run_command %s' , cmd .__class__ .__name__ )
229120
230121 # stash selected API versions for later
231122 # TODO(dtroyer): how do extenstions add their version requirements?
232123 self .api_version = {
233- 'compute' : options .os_compute_api_version ,
234- 'identity' : options .os_identity_api_version ,
235- 'image' : options .os_image_api_version ,
124+ 'compute' : self . options .os_compute_api_version ,
125+ 'identity' : self . options .os_identity_api_version ,
126+ 'image' : self . options .os_image_api_version ,
236127 }
237128
238- # Setup debugging
239- if getattr (options , 'debug' , None ):
240- self .debug = 1
241- else :
242- self .debug = 0
243-
244- if self .debug :
129+ if self .options .debug :
245130 print "API: Identity=%s Compute=%s Image=%s" % (self .api_version ['identity' ], self .api_version ['compute' ], self .api_version ['image' ])
246- print "Action: %s" % options .action
247- print "subject: %s" % getattr (options , 'subject' , '' )
248- print "args: %s" % args
249-
250- # Handle top-level --help/-h before attempting to parse
251- # a command off the command line
252- if getattr (options , 'help' , None ) or getattr (options , 'action' , None ) == 'help' :
253- print "top-level help"
254- # Build available subcommands
255- self .parser = self .get_subcommand_parser (options .subject )
256- self .do_help (options )
257- return 0
258-
259- # Build selected subcommands
260- self .parser = self .get_subcommand_parser (options .subject )
261-
262- # Parse args again and call whatever callback was selected
263- args .insert (0 , '%s-%s' % (options .action , options .subject ))
264- if self .debug :
265- print "args: %s" % args
266- args = self .parser .parse_args (args )
267-
268- if self .debug :
269- print "Testing command parsing"
270- print "Auth username: %s" % options .os_username
271- #print "Action: %s" % options.action
272- #print "Subject: %s" % options.subject
273- print "args: %s" % args
274-
275- class OpenStackHelpFormatter (argparse .HelpFormatter ):
276- def start_section (self , heading ):
277- # Title-case the headings
278- heading = '%s%s' % (heading [0 ].upper (), heading [1 :])
279- super (OpenStackHelpFormatter , self ).start_section (heading )
280-
281-
282- def main ():
283- try :
284- OpenStackShell ().main (sys .argv [1 :])
285-
286- except Exception , e :
287- if httplib2 .debuglevel == 1 :
288- raise # dump stack.
289- else :
290- print >> sys .stderr , e
291- sys .exit (1 )
292-
293- def test_main (argv ):
294- # The argparse/optparse/cmd2 modules muck about with sys.argv
295- # so we save it and restore at the end to let the tests
296- # run repeatedly without concatenating the args on each run
297- save_argv = sys .argv
298-
299- main ()
300-
301- # Put it back so the next test has a clean copy
302- sys .argv = save_argv
131+ print "cmd: %s" % cmd
132+
133+ def clean_up (self , cmd , result , err ):
134+ self .log .debug ('clean_up %s' , cmd .__class__ .__name__ )
135+ if err :
136+ self .log .debug ('got an error: %s' , err )
137+
138+
139+ def main (argv = sys .argv [1 :]):
140+ return OpenStackShell ().run (argv )
141+
303142
304143if __name__ == "__main__" :
305- main ()
144+ sys . exit ( main (sys . argv [ 1 :]) )
0 commit comments