gotime_aws 2.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,611 @@
1
+ #
2
+ # Copyright (c) 2007-2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a 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,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ # Test
25
+ module Aws
26
+ require 'digest/md5'
27
+ require 'pp'
28
+ require 'cgi'
29
+ require 'uri'
30
+ require 'xmlsimple'
31
+ require 'net/http'
32
+
33
+ require_relative 'utils'
34
+ require_relative 'errors'
35
+ require_relative 'parsers'
36
+
37
+
38
+ class AwsBenchmarkingBlock #:nodoc:
39
+ attr_accessor :xml, :service
40
+
41
+ def initialize
42
+ # Benchmark::Tms instance for service (Ec2, S3, or SQS) access benchmarking.
43
+ @service = Benchmark::Tms.new()
44
+ # Benchmark::Tms instance for XML parsing benchmarking.
45
+ @xml = Benchmark::Tms.new()
46
+ end
47
+ end
48
+
49
+ class AwsNoChange < RuntimeError
50
+ end
51
+
52
+ class AwsBase
53
+
54
+ # Amazon HTTP Error handling
55
+
56
+ # Text, if found in an error message returned by AWS, indicates that this may be a transient
57
+ # error. Transient errors are automatically retried with exponential back-off.
58
+ AMAZON_PROBLEMS = ['internal service error',
59
+ 'is currently unavailable',
60
+ 'no response from',
61
+ 'Please try again',
62
+ 'InternalError',
63
+ 'ServiceUnavailable', #from SQS docs
64
+ 'Unavailable',
65
+ 'This application is not currently available',
66
+ 'InsufficientInstanceCapacity'
67
+ ]
68
+ @@amazon_problems = AMAZON_PROBLEMS
69
+ # Returns a list of Amazon service responses which are known to be transient problems.
70
+ # We have to re-request if we get any of them, because the problem will probably disappear.
71
+ # By default this method returns the same value as the AMAZON_PROBLEMS const.
72
+ def self.amazon_problems
73
+ @@amazon_problems
74
+ end
75
+
76
+ # Sets the list of Amazon side problems. Use in conjunction with the
77
+ # getter to append problems.
78
+ def self.amazon_problems=(problems_list)
79
+ @@amazon_problems = problems_list
80
+ end
81
+
82
+ end
83
+
84
+ module AwsBaseInterface
85
+
86
+ DEFAULT_SIGNATURE_VERSION = '2'
87
+
88
+ module ClassMethods
89
+
90
+ def self.bench
91
+ @@bench
92
+ end
93
+
94
+ def self.bench_xml
95
+ @@bench.xml
96
+ end
97
+
98
+ def self.bench_s3
99
+ @@bench.service
100
+ end
101
+ end
102
+
103
+ @@caching = false
104
+
105
+ def self.caching
106
+ @@caching
107
+ end
108
+
109
+ def self.caching=(caching)
110
+ @@caching = caching
111
+ end
112
+
113
+ # Current aws_access_key_id
114
+ attr_reader :aws_access_key_id
115
+ # Last HTTP request object
116
+ attr_reader :last_request
117
+ # Last HTTP response object
118
+ attr_reader :last_response
119
+ # Last AWS errors list (used by AWSErrorHandler)
120
+ attr_accessor :last_errors
121
+ # Last AWS request id (used by AWSErrorHandler)
122
+ attr_accessor :last_request_id
123
+ # Logger object
124
+ attr_accessor :logger
125
+ # Initial params hash
126
+ attr_accessor :params
127
+ # RightHttpConnection instance
128
+ # there's a method now to get this since it could be per thread or what have you
129
+ # attr_reader :connection
130
+ # Cache
131
+ attr_reader :cache
132
+ # Signature version (all services except s3)
133
+ attr_reader :signature_version
134
+
135
+ def init(service_info, aws_access_key_id, aws_secret_access_key, params={}) #:nodoc:
136
+ @params = params
137
+ if Aws::Utils.blank?(aws_access_key_id) || Aws::Utils.blank?(aws_secret_access_key)
138
+ raise AwsError.new("AWS access keys are required to operate on #{service_info[:name]}")
139
+ end
140
+ @aws_access_key_id = aws_access_key_id
141
+ @aws_secret_access_key = aws_secret_access_key
142
+ # if the endpoint was explicitly defined - then use it
143
+ if @params[:endpoint_url]
144
+ @params[:server] = URI.parse(@params[:endpoint_url]).host
145
+ @params[:port] = URI.parse(@params[:endpoint_url]).port
146
+ @params[:service] = URI.parse(@params[:endpoint_url]).path
147
+ @params[:protocol] = URI.parse(@params[:endpoint_url]).scheme
148
+ @params[:region] = nil
149
+ else
150
+ @params[:server] ||= service_info[:default_host]
151
+ @params[:server] = "#{@params[:region]}.#{@params[:server]}" if @params[:region]
152
+ @params[:port] ||= service_info[:default_port]
153
+ @params[:service] ||= service_info[:default_service]
154
+ @params[:protocol] ||= service_info[:default_protocol]
155
+ @params[:api_version] ||= service_info[:api_version]
156
+ end
157
+ if !@params[:multi_thread].nil? && @params[:connection_mode].nil? # user defined this
158
+ @params[:connection_mode] = @params[:multi_thread] ? :per_thread : :single
159
+ end
160
+ # @params[:multi_thread] ||= defined?(AWS_DAEMON)
161
+ @params[:connection_mode] ||= :default
162
+ @params[:connection_mode] = :per_request if @params[:connection_mode] == :default
163
+ @logger = @params[:logger]
164
+ @logger = Rails.logger if !@logger && defined?(Rails) && defined?(Rails.logger)
165
+ @logger = ::Rails.logger if !@logger && defined?(::Rails.logger)
166
+ @logger = Logger.new(STDOUT) if !@logger
167
+ @logger.info "New #{self.class.name} using #{@params[:connection_mode].to_s}-connection mode"
168
+ @error_handler = nil
169
+ @cache = {}
170
+ @signature_version = (params[:signature_version] || service_info[:signature_version] || DEFAULT_SIGNATURE_VERSION).to_s
171
+ end
172
+
173
+ def signed_service_params(aws_secret_access_key, service_hash, http_verb=nil, host=nil, service=nil)
174
+ case signature_version.to_s
175
+ when '0' then
176
+ Utils::sign_request_v0(aws_secret_access_key, service_hash)
177
+ when '1' then
178
+ Utils::sign_request_v1(aws_secret_access_key, service_hash)
179
+ when '2' then
180
+ Utils::sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, service)
181
+ else
182
+ raise AwsError.new("Unknown signature version (#{signature_version.to_s}) requested")
183
+ end
184
+ end
185
+
186
+ def generate_request(action, params={})
187
+ generate_request2(@aws_access_key_id, @aws_secret_access_key, action, @params[:api_version], @params, params)
188
+ end
189
+
190
+ # FROM SDB
191
+ def generate_request2(aws_access_key, aws_secret_key, action, api_version, lib_params, user_params={}, options={}) #:nodoc:
192
+ # remove empty params from request
193
+ user_params.delete_if { |key, value| value.nil? }
194
+ # user_params.each_pair do |k,v|
195
+ # user_params[k] = v.force_encoding("UTF-8")
196
+ # end
197
+ #params_string = params.to_a.collect{|key,val| key + "=#{CGI::escape(val.to_s)}" }.join("&")
198
+ # prepare service data
199
+ service = lib_params[:service]
200
+
201
+ now = Time.now.getutc
202
+ service_hash = {"Action" => action,
203
+ "AWSAccessKeyId" => aws_access_key}
204
+ service_hash.update("Version" => api_version) if api_version
205
+ service_hash.update(user_params)
206
+ headers = {}
207
+ if signature_version == '3'
208
+ service_hash["Timestamp"] = now.iso8601
209
+ service_params = escape_params(service_hash)
210
+ signature, algorithm = Aws::Utils.signature_version3(aws_secret_key, now)
211
+ headers['X-Amzn-Authorization'] = "AWS3-HTTPS AWSAccessKeyId=#{aws_access_key}, Algorithm=#{algorithm.upcase}, Signature=#{signature}"
212
+ headers['Date'] = now.httpdate
213
+ else
214
+ # puts 'service=' + service.to_s
215
+ service_params = signed_service_params(aws_secret_key, service_hash, :get, lib_params[:server], lib_params[:service])
216
+ end
217
+
218
+ #
219
+ # use POST method if the length of the query string is too large
220
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/MakingRESTRequests.html
221
+ if service_params.size > 2000
222
+ if signature_version == '2'
223
+ # resign the request because HTTP verb is included into signature
224
+ service_params = signed_service_params(aws_secret_key, service_hash, :post, lib_params[:server], service)
225
+ end
226
+ request = Net::HTTP::Post.new(service)
227
+ request.body = service_params
228
+ request['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
229
+ else
230
+ request = Net::HTTP::Get.new("#{service}?#{service_params}")
231
+ end
232
+ headers.each_pair do |k, v|
233
+ request[k] = v
234
+ end
235
+ # puts "header=" + request['X-Amzn-Authorization']
236
+
237
+ #puts "\n\n --------------- QUERY REQUEST TO AWS -------------- \n\n"
238
+ #puts "#{@params[:service]}?#{service_params}\n\n"
239
+
240
+ # prepare output hash
241
+ {:request => request,
242
+ :server => lib_params[:server],
243
+ :port => lib_params[:port],
244
+ :protocol => lib_params[:protocol]}
245
+ end
246
+
247
+ def escape_params(service_hash)
248
+ canonical_string = service_hash.keys.sort.map do |key|
249
+ "#{Aws::Utils.amz_escape(key)}=#{Aws::Utils.amz_escape(service_hash[key])}"
250
+ end.join('&')
251
+ canonical_string
252
+ end
253
+
254
+ def get_conn(connection_name, lib_params, logger)
255
+ # thread = lib_params[:multi_thread] ? Thread.current : Thread.main
256
+ # thread[connection_name] ||= Rightscale::HttpConnection.new(:exception => Aws::AwsError, :logger => logger)
257
+ # conn = thread[connection_name]
258
+ # return conn
259
+ http_conn = nil
260
+ conn_mode = lib_params[:connection_mode]
261
+
262
+ params = { :exception => AwsError, :logger => logger }
263
+
264
+ # Adds all parameters accepted by Rightscale::HttpConnection#new
265
+ [ :user_agent, :ca_file, :http_connection_retry_count,
266
+ :http_connection_open_timeout, :http_connection_read_timeout,
267
+ :http_connection_retry_delay
268
+ ].each do |key|
269
+ params[key] = lib_params[key] if lib_params.has_key?(key)
270
+ end
271
+
272
+ if conn_mode == :per_request
273
+ http_conn = Rightscale::HttpConnection.new(params)
274
+
275
+ elsif conn_mode == :per_thread || conn_mode == :single
276
+ thread = conn_mode == :per_thread ? Thread.current : Thread.main
277
+ thread[connection_name] ||= Rightscale::HttpConnection.new(params)
278
+ http_conn = thread[connection_name]
279
+ # ret = request_info_impl(http_conn, bench, request, parser, &block)
280
+ end
281
+ return http_conn
282
+
283
+ end
284
+
285
+ def close_conn(conn_name)
286
+ conn_mode = @params[:connection_mode]
287
+ if conn_mode == :per_thread || conn_mode == :single
288
+ thread = conn_mode == :per_thread ? Thread.current : Thread.main
289
+ if !thread[conn_name].nil?
290
+ thread[conn_name].finish
291
+ thread[conn_name] = nil
292
+ end
293
+ end
294
+ end
295
+
296
+ def connection
297
+ get_conn(self.class.connection_name, self.params, self.logger)
298
+ end
299
+
300
+ def close_connection
301
+ close_conn(self.class.connection_name)
302
+ end
303
+
304
+
305
+ def request_info2(request, parser, lib_params, connection_name, logger, bench, options={}, &block) #:nodoc:
306
+ ret = nil
307
+ # puts 'OPTIONS=' + options.inspect
308
+ http_conn = get_conn(connection_name, lib_params, logger)
309
+ begin
310
+ # todo: this QueryTimeout retry should go into a SimpleDbErrorHandler, not here
311
+ retry_count = 1
312
+ count = 0
313
+ while count <= retry_count
314
+ puts 'RETRYING QUERY due to QueryTimeout...' if count > 0
315
+ begin
316
+ ret = request_info_impl(http_conn, bench, request, parser, options, &block)
317
+ rescue Aws::AwsError => ex
318
+ if !ex.include?(/QueryTimeout/) || count == retry_count
319
+ raise ex
320
+ end
321
+ end
322
+ break if ret
323
+ count += 1
324
+ end
325
+ ensure
326
+ http_conn.finish if http_conn && lib_params[:connection_mode] == :per_request
327
+ end
328
+ ret
329
+ end
330
+
331
+ # This is the latest and greatest now. Service must have connection_name defined.
332
+ def request_info3(service_interface, request, parser, options, &block)
333
+ request_info2(request, parser,
334
+ service_interface.params,
335
+ service_interface.class.connection_name,
336
+ service_interface.logger,
337
+ service_interface.class.bench,
338
+ options, &block)
339
+ end
340
+
341
+
342
+ # This is the direction we should head instead of writing our own parsers for everything, much simpler
343
+ # params:
344
+ # - :group_tags => hash of indirection to eliminate, see: http://xml-simple.rubyforge.org/
345
+ # - :force_array => true for all or an array of tag names to force
346
+ # - :pull_out_array => an array of levels to dig into when generating return value (see rds.rb for example)
347
+ def request_info_xml_simple(connection_name, lib_params, request, logger, params = {})
348
+
349
+ connection = get_conn(connection_name, lib_params, logger)
350
+ begin
351
+ @last_request = request[:request]
352
+ @last_response = nil
353
+
354
+ response = connection.request(request)
355
+ # puts "response=" + response.body
356
+ # benchblock.service.add!{ response = connection.request(request) }
357
+ # check response for errors...
358
+ @last_response = response
359
+ if response.is_a?(Net::HTTPSuccess)
360
+ @error_handler = nil
361
+ # benchblock.xml.add! { parser.parse(response) }
362
+ # return parser.result
363
+ force_array = params[:force_array] || false
364
+ # Force_array and group_tags don't work nice together so going to force array manually
365
+ xml_simple_options = {"KeyToSymbol"=>false, 'ForceArray' => false}
366
+ xml_simple_options["GroupTags"] = params[:group_tags] if params[:group_tags]
367
+
368
+ # { 'GroupTags' => { 'searchpath' => 'dir' }
369
+ # 'ForceArray' => %r(_list$)
370
+ parsed = XmlSimple.xml_in(response.body, xml_simple_options)
371
+ # todo: we may want to consider stripping off a couple of layers when doing this, for instance:
372
+ # <DescribeDBInstancesResponse xmlns="http://rds.amazonaws.com/admin/2009-10-16/">
373
+ # <DescribeDBInstancesResult>
374
+ # <DBInstances>
375
+ # <DBInstance>....
376
+ # Strip it off and only return an array or hash of <DBInstance>'s (hash by identifier).
377
+ # would have to be able to make the RequestId available somehow though, perhaps some special array subclass which included that?
378
+ unless force_array.is_a? Array
379
+ force_array = []
380
+ end
381
+ parsed = symbolize(parsed, force_array)
382
+ # puts 'parsed=' + parsed.inspect
383
+ if params[:pull_out_array]
384
+ ret = Aws::AwsResponseArray.new(parsed[:response_metadata])
385
+ level_hash = parsed
386
+ params[:pull_out_array].each do |x|
387
+ level_hash = level_hash[x]
388
+ end
389
+ if level_hash.is_a? Hash # When there's only one
390
+ ret << level_hash
391
+ else # should be array
392
+ # puts 'level_hash=' + level_hash.inspect
393
+ level_hash.each do |x|
394
+ ret << x
395
+ end
396
+ end
397
+ elsif params[:pull_out_single]
398
+ # returns a single object
399
+ ret = AwsResponseObjectHash.new(parsed[:response_metadata])
400
+ level_hash = parsed
401
+ params[:pull_out_single].each do |x|
402
+ level_hash = level_hash[x]
403
+ end
404
+ ret.merge!(level_hash)
405
+ else
406
+ ret = parsed
407
+ end
408
+ return ret
409
+
410
+ else
411
+ @error_handler = AWSErrorHandler.new(self, nil, :errors_list => self.class.amazon_problems) unless @error_handler
412
+ check_result = @error_handler.check(request)
413
+ if check_result
414
+ @error_handler = nil
415
+ return check_result
416
+ end
417
+ request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
418
+ raise AwsError2.new(@last_response.code, @last_request_id, request_text_data, @last_response.body)
419
+ end
420
+ ensure
421
+ connection.finish if connection && lib_params[:connection_mode] == :per_request
422
+ end
423
+
424
+ end
425
+
426
+ # This is the latest and greatest now. Service must have connection_name defined.
427
+ def request_info_xml_simple3(service_interface, request, options)
428
+ request_info_xml_simple(service_interface.class.connection_name,
429
+ service_interface.params,
430
+ request,
431
+ service_interface.logger,
432
+ options)
433
+ end
434
+
435
+ def symbolize(hash, force_array)
436
+ ret = {}
437
+ hash.keys.each do |key|
438
+ val = hash[key]
439
+ if val.is_a? Hash
440
+ val = symbolize(val, force_array)
441
+ if force_array.include? key
442
+ val = [val]
443
+ end
444
+ elsif val.is_a? Array
445
+ val = val.collect { |x| symbolize(x, force_array) }
446
+ end
447
+ ret[Aws::Utils.underscore(key).to_sym] = val
448
+ end
449
+ ret
450
+ end
451
+
452
+ # Returns +true+ if the describe_xxx responses are being cached
453
+ def caching?
454
+ @params.key?(:cache) ? @params[:cache] : @@caching
455
+ end
456
+
457
+ # Check if the aws function response hits the cache or not.
458
+ # If the cache hits:
459
+ # - raises an +AwsNoChange+ exception if +do_raise+ == +:raise+.
460
+ # - returnes parsed response from the cache if it exists or +true+ otherwise.
461
+ # If the cache miss or the caching is off then returns +false+.
462
+ def cache_hits?(function, response, do_raise=:raise)
463
+ result = false
464
+ if caching?
465
+ function = function.to_sym
466
+ # get rid of requestId (this bad boy was added for API 2008-08-08+ and it is uniq for every response)
467
+ response = response.sub(%r{<requestId>.+?</requestId>}, '')
468
+ response_md5 =Digest::MD5.hexdigest(response).to_s
469
+ # check for changes
470
+ unless @cache[function] && @cache[function][:response_md5] == response_md5
471
+ # well, the response is new, reset cache data
472
+ update_cache(function, {:response_md5 => response_md5,
473
+ :timestamp => Time.now,
474
+ :hits => 0,
475
+ :parsed => nil})
476
+ else
477
+ # aha, cache hits, update the data and throw an exception if needed
478
+ @cache[function][:hits] += 1
479
+ if do_raise == :raise
480
+ raise(AwsNoChange, "Cache hit: #{function} response has not changed since "+
481
+ "#{@cache[function][:timestamp].strftime('%Y-%m-%d %H:%M:%S')}, "+
482
+ "hits: #{@cache[function][:hits]}.")
483
+ else
484
+ result = @cache[function][:parsed] || true
485
+ end
486
+ end
487
+ end
488
+ result
489
+ end
490
+
491
+ def update_cache(function, hash)
492
+ (@cache[function.to_sym] ||= {}).merge!(hash) if caching?
493
+ end
494
+
495
+ def on_exception(options={:raise=>true, :log=>true}) # :nodoc:
496
+ raise if $!.is_a?(AwsNoChange)
497
+ AwsError::on_aws_exception(self, options)
498
+ end
499
+
500
+ # Return +true+ if this instance works in multi_thread mode and +false+ otherwise.
501
+ def multi_thread
502
+ @params[:multi_thread]
503
+ end
504
+
505
+
506
+ def request_info_impl(connection, benchblock, request, parser, options={}, &block) #:nodoc:
507
+ connection = connection
508
+ @last_request = request[:request]
509
+ @last_response = nil
510
+ response =nil
511
+ blockexception = nil
512
+
513
+ # puts 'OPTIONS2=' + options.inspect
514
+
515
+ if (block != nil)
516
+ # TRB 9/17/07 Careful - because we are passing in blocks, we get a situation where
517
+ # an exception may get thrown in the block body (which is high-level
518
+ # code either here or in the application) but gets caught in the
519
+ # low-level code of HttpConnection. The solution is not to let any
520
+ # exception escape the block that we pass to HttpConnection::request.
521
+ # Exceptions can originate from code directly in the block, or from user
522
+ # code called in the other block which is passed to response.read_body.
523
+ benchblock.service.add! do
524
+ responsehdr = connection.request(request) do |response|
525
+ #########
526
+ begin
527
+ @last_response = response
528
+ if response.is_a?(Net::HTTPSuccess)
529
+ @error_handler = nil
530
+ response.read_body(&block)
531
+ else
532
+ @error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
533
+ check_result = @error_handler.check(request, options)
534
+ if check_result
535
+ @error_handler = nil
536
+ return check_result
537
+ end
538
+ request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
539
+ raise AwsError.new(@last_errors, @last_response.code, @last_request_id, request_text_data)
540
+ end
541
+ rescue Exception => e
542
+ blockexception = e
543
+ end
544
+ end
545
+ #########
546
+
547
+ #OK, now we are out of the block passed to the lower level
548
+ if (blockexception)
549
+ raise blockexception
550
+ end
551
+ benchblock.xml.add! do
552
+ parser.parse(responsehdr)
553
+ end
554
+ return parser.result
555
+ end
556
+ else
557
+ benchblock.service.add! { response = connection.request(request) }
558
+ # check response for errors...
559
+ @last_response = response
560
+ if response.is_a?(Net::HTTPSuccess)
561
+ @error_handler = nil
562
+ benchblock.xml.add! { parser.parse(response) }
563
+ return parser.result
564
+ else
565
+ @error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
566
+ check_result = @error_handler.check(request, options)
567
+ if check_result
568
+ @error_handler = nil
569
+ return check_result
570
+ end
571
+ request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
572
+ raise AwsError.new(@last_errors, @last_response.code, @last_request_id, request_text_data)
573
+ end
574
+ end
575
+ rescue
576
+ @error_handler = nil
577
+ raise
578
+ end
579
+
580
+ def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true) #:nodoc:
581
+ # We do not want to break the logic of parsing hence will use a dummy parser to process all the standard
582
+ # steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
583
+ # If the caching is enabled and hit then throw AwsNoChange.
584
+ # P.S. caching works for the whole images list only! (when the list param is blank)
585
+ # check cache
586
+ response, params = request_info(link, RightDummyParser.new)
587
+ cache_hits?(method.to_sym, response.body) if use_cache
588
+ parser = parser_class.new(:logger => @logger)
589
+ benchblock.xml.add! { parser.parse(response, params) }
590
+ result = block_given? ? yield(parser) : parser.result
591
+ # update parsed data
592
+ update_cache(method.to_sym, :parsed => result) if use_cache
593
+ result
594
+ end
595
+
596
+ # Returns Amazons request ID for the latest request
597
+ def last_request_id
598
+ @last_response && @last_response.body.to_s[%r{<requestId>(.+?)</requestId>}] && $1
599
+ end
600
+
601
+ def hash_params(prefix, list) #:nodoc:
602
+ groups = {}
603
+ list.each_index { |i| groups.update("#{prefix}.#{i+1}"=>list[i]) } if list
604
+ return groups
605
+ end
606
+
607
+ end
608
+
609
+
610
+ end
611
+
@@ -0,0 +1,39 @@
1
+ #
2
+ # Copyright (c) 2007-2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a 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,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ #
24
+
25
+
26
+ # A hack because there's a bug in add! in Benchmark::Tms
27
+ module Benchmark #:nodoc:
28
+ class Tms #:nodoc:
29
+ def add!(&blk)
30
+ t = Benchmark::measure(&blk)
31
+ @utime = utime + t.utime
32
+ @stime = stime + t.stime
33
+ @cutime = cutime + t.cutime
34
+ @cstime = cstime + t.cstime
35
+ @real = real + t.real
36
+ self
37
+ end
38
+ end
39
+ end