Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(266)

Side by Side Diff: boto/roboto/awsqueryrequest.py

Issue 8386013: Merging in latest boto. (Closed) Base URL: svn://svn.chromium.org/boto
Patch Set: Redoing vendor drop by deleting and then merging. Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « boto/roboto/__init__.py ('k') | boto/roboto/awsqueryservice.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2010 Mitch Garnaat http://garnaat.org/
2 # Copyright (c) 2010, Eucalyptus Systems, Inc.
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining a
5 # copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish, dis-
8 # tribute, sublicense, and/or sell copies of the Software, and to permit
9 # persons to whom the Software is furnished to do so, subject to the fol-
10 # lowing conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 # IN THE SOFTWARE.
22
23 import sys
24 import os
25 import boto
26 import optparse
27 import copy
28 import boto.exception
29 import boto.roboto.awsqueryservice
30
31 import bdb
32 import traceback
33 try:
34 import epdb as debugger
35 except ImportError:
36 import pdb as debugger
37
38 def boto_except_hook(debugger_flag, debug_flag):
39 def excepthook(typ, value, tb):
40 if typ is bdb.BdbQuit:
41 sys.exit(1)
42 sys.excepthook = sys.__excepthook__
43
44 if debugger_flag and sys.stdout.isatty() and sys.stdin.isatty():
45 if debugger.__name__ == 'epdb':
46 debugger.post_mortem(tb, typ, value)
47 else:
48 debugger.post_mortem(tb)
49 elif debug_flag:
50 print traceback.print_tb(tb)
51 sys.exit(1)
52 else:
53 print value
54 sys.exit(1)
55
56 return excepthook
57
58 class Line(object):
59
60 def __init__(self, fmt, data, label):
61 self.fmt = fmt
62 self.data = data
63 self.label = label
64 self.line = '%s\t' % label
65 self.printed = False
66
67 def append(self, datum):
68 self.line += '%s\t' % datum
69
70 def print_it(self):
71 if not self.printed:
72 print self.line
73 self.printed = True
74
75 class RequiredParamError(boto.exception.BotoClientError):
76
77 def __init__(self, required):
78 self.required = required
79 s = 'Required parameters are missing: %s' % self.required
80 boto.exception.BotoClientError.__init__(self, s)
81
82 class EncoderError(boto.exception.BotoClientError):
83
84 def __init__(self, error_msg):
85 s = 'Error encoding value (%s)' % error_msg
86 boto.exception.BotoClientError.__init__(self, s)
87
88 class FilterError(boto.exception.BotoClientError):
89
90 def __init__(self, filters):
91 self.filters = filters
92 s = 'Unknown filters: %s' % self.filters
93 boto.exception.BotoClientError.__init__(self, s)
94
95 class Encoder:
96
97 @classmethod
98 def encode(cls, p, rp, v, label=None):
99 if p.name.startswith('_'):
100 return
101 try:
102 mthd = getattr(cls, 'encode_'+p.ptype)
103 mthd(p, rp, v, label)
104 except AttributeError:
105 raise EncoderError('Unknown type: %s' % p.ptype)
106
107 @classmethod
108 def encode_string(cls, p, rp, v, l):
109 if l:
110 label = l
111 else:
112 label = p.name
113 rp[label] = v
114
115 encode_file = encode_string
116 encode_enum = encode_string
117
118 @classmethod
119 def encode_integer(cls, p, rp, v, l):
120 if l:
121 label = l
122 else:
123 label = p.name
124 rp[label] = '%d' % v
125
126 @classmethod
127 def encode_boolean(cls, p, rp, v, l):
128 if l:
129 label = l
130 else:
131 label = p.name
132 if v:
133 v = 'true'
134 else:
135 v = 'false'
136 rp[label] = v
137
138 @classmethod
139 def encode_datetime(cls, p, rp, v, l):
140 if l:
141 label = l
142 else:
143 label = p.name
144 rp[label] = v
145
146 @classmethod
147 def encode_array(cls, p, rp, v, l):
148 v = boto.utils.mklist(v)
149 if l:
150 label = l
151 else:
152 label = p.name
153 label = label + '.%d'
154 for i, value in enumerate(v):
155 rp[label%(i+1)] = value
156
157 class AWSQueryRequest(object):
158
159 ServiceClass = None
160
161 Description = ''
162 Params = []
163 Args = []
164 Filters = []
165 Response = {}
166
167 CLITypeMap = {'string' : 'string',
168 'integer' : 'int',
169 'int' : 'int',
170 'enum' : 'choice',
171 'datetime' : 'string',
172 'dateTime' : 'string',
173 'file' : 'string',
174 'boolean' : None}
175
176 @classmethod
177 def name(cls):
178 return cls.__name__
179
180 def __init__(self, **args):
181 self.args = args
182 self.parser = None
183 self.cli_options = None
184 self.cli_args = None
185 self.cli_output_format = None
186 self.connection = None
187 self.list_markers = []
188 self.item_markers = []
189 self.request_params = {}
190 self.connection_args = None
191
192 def __repr__(self):
193 return self.name()
194
195 def get_connection(self, **args):
196 if self.connection is None:
197 self.connection = self.ServiceClass(**args)
198 return self.connection
199
200 @property
201 def status(self):
202 retval = None
203 if self.http_response is not None:
204 retval = self.http_response.status
205 return retval
206
207 @property
208 def reason(self):
209 retval = None
210 if self.http_response is not None:
211 retval = self.http_response.reason
212 return retval
213
214 @property
215 def request_id(self):
216 retval = None
217 if self.aws_response is not None:
218 retval = getattr(self.aws_response, 'requestId')
219 return retval
220
221 def process_filters(self):
222 filters = self.args.get('filters', [])
223 filter_names = [f['name'] for f in self.Filters]
224 unknown_filters = [f for f in filters if f not in filter_names]
225 if unknown_filters:
226 raise FilterError, 'Unknown filters: %s' % unknown_filters
227 for i, filter in enumerate(self.Filters):
228 name = filter['name']
229 if name in filters:
230 self.request_params['Filter.%d.Name' % (i+1)] = name
231 for j, value in enumerate(boto.utils.mklist(filters[name])):
232 Encoder.encode(filter, self.request_params, value,
233 'Filter.%d.Value.%d' % (i+1,j+1))
234
235 def process_args(self, **args):
236 """
237 Responsible for walking through Params defined for the request and:
238
239 * Matching them with keyword parameters passed to the request
240 constructor or via the command line.
241 * Checking to see if all required parameters have been specified
242 and raising an exception, if not.
243 * Encoding each value into the set of request parameters that will
244 be sent in the request to the AWS service.
245 """
246 self.args.update(args)
247 self.connection_args = copy.copy(self.args)
248 if 'debug' in self.args and self.args['debug'] >= 2:
249 boto.set_stream_logger(self.name())
250 required = [p.name for p in self.Params+self.Args if not p.optional]
251 for param in self.Params+self.Args:
252 if param.long_name:
253 python_name = param.long_name.replace('-', '_')
254 else:
255 python_name = boto.utils.pythonize_name(param.name, '_')
256 value = None
257 if python_name in self.args:
258 value = self.args[python_name]
259 if value is None:
260 value = param.default
261 if value is not None:
262 if param.name in required:
263 required.remove(param.name)
264 if param.request_param:
265 if param.encoder:
266 param.encoder(param, self.request_params, value)
267 else:
268 Encoder.encode(param, self.request_params, value)
269 if python_name in self.args:
270 del self.connection_args[python_name]
271 if required:
272 l = []
273 for p in self.Params+self.Args:
274 if p.name in required:
275 if p.short_name and p.long_name:
276 l.append('(%s, %s)' % (p.optparse_short_name,
277 p.optparse_long_name))
278 elif p.short_name:
279 l.append('(%s)' % p.optparse_short_name)
280 else:
281 l.append('(%s)' % p.optparse_long_name)
282 raise RequiredParamError(','.join(l))
283 boto.log.debug('request_params: %s' % self.request_params)
284 self.process_markers(self.Response)
285
286 def process_markers(self, fmt, prev_name=None):
287 if fmt and fmt['type'] == 'object':
288 for prop in fmt['properties']:
289 self.process_markers(prop, fmt['name'])
290 elif fmt and fmt['type'] == 'array':
291 self.list_markers.append(prev_name)
292 self.item_markers.append(fmt['name'])
293
294 def send(self, verb='GET', **args):
295 self.process_args(**args)
296 self.process_filters()
297 conn = self.get_connection(**self.connection_args)
298 self.http_response = conn.make_request(self.name(),
299 self.request_params,
300 verb=verb)
301 self.body = self.http_response.read()
302 boto.log.debug(self.body)
303 if self.http_response.status == 200:
304 self.aws_response = boto.jsonresponse.Element(list_marker=self.list_ markers,
305 item_marker=self.item_ markers)
306 h = boto.jsonresponse.XmlHandler(self.aws_response, self)
307 h.parse(self.body)
308 return self.aws_response
309 else:
310 boto.log.error('%s %s' % (self.http_response.status,
311 self.http_response.reason))
312 boto.log.error('%s' % self.body)
313 raise conn.ResponseError(self.http_response.status,
314 self.http_response.reason,
315 self.body)
316
317 def add_standard_options(self):
318 group = optparse.OptionGroup(self.parser, 'Standard Options')
319 # add standard options that all commands get
320 group.add_option('-D', '--debug', action='store_true',
321 help='Turn on all debugging output')
322 group.add_option('--debugger', action='store_true',
323 default=False,
324 help='Enable interactive debugger on error')
325 group.add_option('-U', '--url', action='store',
326 help='Override service URL with value provided')
327 group.add_option('--region', action='store',
328 help='Name of the region to connect to')
329 group.add_option('-I', '--access-key-id', action='store',
330 help='Override access key value')
331 group.add_option('-S', '--secret-key', action='store',
332 help='Override secret key value')
333 group.add_option('--version', action='store_true',
334 help='Display version string')
335 if self.Filters:
336 self.group.add_option('--help-filters', action='store_true',
337 help='Display list of available filters')
338 self.group.add_option('--filter', action='append',
339 metavar=' name=value',
340 help='A filter for limiting the results')
341 self.parser.add_option_group(group)
342
343 def process_standard_options(self, options, args, d):
344 if hasattr(options, 'help_filters') and options.help_filters:
345 print 'Available filters:'
346 for filter in self.Filters:
347 print '%s\t%s' % (filter.name, filter.doc)
348 sys.exit(0)
349 if options.debug:
350 self.args['debug'] = 2
351 if options.url:
352 self.args['url'] = options.url
353 if options.region:
354 self.args['region'] = options.region
355 if options.access_key_id:
356 self.args['aws_access_key_id'] = options.access_key_id
357 if options.secret_key:
358 self.args['aws_secret_access_key'] = options.secret_key
359 if options.version:
360 # TODO - Where should the version # come from?
361 print 'version x.xx'
362 exit(0)
363 sys.excepthook = boto_except_hook(options.debugger,
364 options.debug)
365
366 def get_usage(self):
367 s = 'usage: %prog [options] '
368 l = [ a.long_name for a in self.Args ]
369 s += ' '.join(l)
370 for a in self.Args:
371 if a.doc:
372 s += '\n\n\t%s - %s' % (a.long_name, a.doc)
373 return s
374
375 def build_cli_parser(self):
376 self.parser = optparse.OptionParser(description=self.Description,
377 usage=self.get_usage())
378 self.add_standard_options()
379 for param in self.Params:
380 ptype = action = choices = None
381 if param.ptype in self.CLITypeMap:
382 ptype = self.CLITypeMap[param.ptype]
383 action = 'store'
384 if param.ptype == 'boolean':
385 action = 'store_true'
386 elif param.ptype == 'array':
387 if len(param.items) == 1:
388 ptype = param.items[0]['type']
389 action = 'append'
390 elif param.cardinality != 1:
391 action = 'append'
392 if ptype or action == 'store_true':
393 if param.short_name:
394 self.parser.add_option(param.optparse_short_name,
395 param.optparse_long_name,
396 action=action, type=ptype,
397 choices=param.choices,
398 help=param.doc)
399 elif param.long_name:
400 self.parser.add_option(param.optparse_long_name,
401 action=action, type=ptype,
402 choices=param.choices,
403 help=param.doc)
404
405 def do_cli(self):
406 if not self.parser:
407 self.build_cli_parser()
408 self.cli_options, self.cli_args = self.parser.parse_args()
409 d = {}
410 self.process_standard_options(self.cli_options, self.cli_args, d)
411 for param in self.Params:
412 if param.long_name:
413 p_name = param.long_name.replace('-', '_')
414 else:
415 p_name = boto.utils.pythonize_name(param.name)
416 value = getattr(self.cli_options, p_name)
417 if param.ptype == 'file' and value:
418 if value == '-':
419 value = sys.stdin.read()
420 else:
421 path = os.path.expanduser(value)
422 path = os.path.expandvars(path)
423 if os.path.isfile(path):
424 fp = open(path)
425 value = fp.read()
426 fp.close()
427 else:
428 self.parser.error('Unable to read file: %s' % path)
429 d[p_name] = value
430 for arg in self.Args:
431 if arg.long_name:
432 p_name = arg.long_name.replace('-', '_')
433 else:
434 p_name = boto.utils.pythonize_name(arg.name)
435 value = None
436 if arg.cardinality == 1:
437 if len(self.cli_args) >= 1:
438 value = self.cli_args[0]
439 else:
440 value = self.cli_args
441 d[p_name] = value
442 self.args.update(d)
443 if hasattr(self.cli_options, 'filter') and self.cli_options.filter:
444 d = {}
445 for filter in self.cli_options.filter:
446 name, value = filter.split('=')
447 d[name] = value
448 if 'filters' in self.args:
449 self.args['filters'].update(d)
450 else:
451 self.args['filters'] = d
452 try:
453 response = self.main()
454 self.cli_formatter(response)
455 except RequiredParamError, e:
456 print e
457 sys.exit(1)
458 except self.ServiceClass.ResponseError, err:
459 print 'Error(%s): %s' % (err.error_code, err.error_message)
460 sys.exit(1)
461 except boto.roboto.awsqueryservice.NoCredentialsError, err:
462 print 'Unable to find credentials.'
463 sys.exit(1)
464 except Exception, e:
465 print e
466 sys.exit(1)
467
468 def _generic_cli_formatter(self, fmt, data, label=''):
469 if fmt['type'] == 'object':
470 for prop in fmt['properties']:
471 if 'name' in fmt:
472 if fmt['name'] in data:
473 data = data[fmt['name']]
474 if fmt['name'] in self.list_markers:
475 label = fmt['name']
476 if label[-1] == 's':
477 label = label[0:-1]
478 label = label.upper()
479 self._generic_cli_formatter(prop, data, label)
480 elif fmt['type'] == 'array':
481 for item in data:
482 line = Line(fmt, item, label)
483 if isinstance(item, dict):
484 for field_name in item:
485 line.append(item[field_name])
486 elif isinstance(item, basestring):
487 line.append(item)
488 line.print_it()
489
490 def cli_formatter(self, data):
491 """
492 This method is responsible for formatting the output for the
493 command line interface. The default behavior is to call the
494 generic CLI formatter which attempts to print something
495 reasonable. If you want specific formatting, you should
496 override this method and do your own thing.
497
498 :type data: dict
499 :param data: The data returned by AWS.
500 """
501 if data:
502 self._generic_cli_formatter(self.Response, data)
503
504
OLDNEW
« no previous file with comments | « boto/roboto/__init__.py ('k') | boto/roboto/awsqueryservice.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698