gotime_aws 2.5.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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