amazon-pricing 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -19,12 +19,12 @@ end
19
19
 
20
20
  desc "Publish the gem"
21
21
  task :publish do
22
- sh "gem push amazon-pricing-#{'0.1.4'}.gem"
22
+ sh "gem push amazon-pricing-#{AwsPricing::VERSION}.gem"
23
23
  end
24
24
 
25
25
  desc "Installs the gem"
26
26
  task :install => :gem do
27
- sh "#{SUDO} gem install amazon-pricing.gem --no-rdoc --no-ri"
27
+ sh "sudo gem install amazon-pricing-#{AwsPricing::VERSION}.gem --no-rdoc --no-ri"
28
28
  end
29
29
 
30
30
  task :test do
@@ -35,13 +35,29 @@ desc "Prints current EC2 pricing to console"
35
35
  task :print_price_list do
36
36
  require 'lib/amazon-pricing'
37
37
  pricing = AwsPricing::PriceList.new
38
- puts "Region,Instance Type,Prepay 1 Year,Prepay 3 Year,Linux PPH,Windows PPH,RHEL PPH,SLES PPH,MSWinSQL PPH,MsWinSQLWeb PPH,3 Year Linux PPH,3 Year Windows PPH,3 Year RHEL PPH,3 Year SLES PPH,3 Year MSWinSQL PPH,3 Year MsWinSQLWeb PPH"
39
- pricing.regions.each do |region|
40
- region.ec2_on_demand_instance_types.each do |t|
41
- puts "#{region.name},on-demand,#{t.api_name},0,0,#{t.linux_price_per_hour},#{t.windows_price_per_hour},#{t.rhel_price_per_hour},#{t.sles_price_per_hour},#{t.mswinSQL_price_per_hour},#{t.mswinSQLWeb_price_per_hour},N/A,N/A,N/A,N/A,N/A,N/A"
38
+ line = "Region,Instance Type,API Name,Memory (MB),Disk (MB),Compute Units, Virtual Cores,OD Linux PPH,OD Windows PPH,OD RHEL PPH,OD SLES PPH,OD MsWinSQL PPH,OD MsWinSQLWeb PPH,"
39
+ [:year1, :year3].each do |term|
40
+ [:light, :medium, :heavy].each do |res_type|
41
+ [:linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb].each do |os|
42
+ line += "#{term} #{res_type} #{os} Prepay,#{term} #{res_type} #{os} PPH,"
43
+ end
42
44
  end
43
- region.ec2_reserved_instance_types.each do |t|
44
- puts "#{region.name},#{t.usage_type},#{t.api_name},#{t.prepay_1_year},#{t.prepay_3_year},#{t.linux_price_per_hour},#{t.windows_price_per_hour},#{t.rhel_price_per_hour},#{t.sles_price_per_hour},#{t.mswinSQL_price_per_hour},#{t.mswinSQLWeb_price_per_hour},#{t.linux_price_per_hour_3_year},#{t.windows_price_per_hour_3_year},#{t.rhel_price_per_hour_3_year},#{t.sles_price_per_hour_3_year},#{t.mswinSQL_price_per_hour_3_year},#{t.mswinSQLWeb_price_per_hour_3_year}"
45
+ end
46
+ puts line.chop
47
+ pricing.regions.each do |region|
48
+ region.instance_types.each do |t|
49
+ line = "#{region.name},#{t.name},#{t.api_name},#{t.memory_in_mb},#{t.disk_in_mb},#{t.compute_units},#{t.virtual_cores},"
50
+ [:linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb].each do |os|
51
+ line += "#{t.price_per_hour(os, :ondemand)},"
52
+ end
53
+ [:year1, :year3].each do |term|
54
+ [:light, :medium, :heavy].each do |res_type|
55
+ [:linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb].each do |os|
56
+ line += "#{t.prepay(os, res_type, term)},#{t.price_per_hour(os, res_type, term)},"
57
+ end
58
+ end
59
+ end
60
+ puts line.chop
45
61
  end
46
62
  end
47
63
  end
@@ -19,8 +19,7 @@ module AwsPricing
19
19
  # information will be retrieved from Amazon via currently undocumented
20
20
  # json APIs.
21
21
  class PriceList
22
- attr_accessor :regions, :version_ec2_on_demand_instance,
23
- :version_ec2_reserved_instance
22
+ attr_accessor :regions
24
23
 
25
24
  def initialize
26
25
  @_regions = {}
@@ -37,12 +36,10 @@ module AwsPricing
37
36
  @_regions.values
38
37
  end
39
38
 
40
- # Type = :on_demand or :reserved
41
- # reserved_usage_type = :light, :medium, :heavy
42
- def get_instance_type(region_name, type, api_name, reserved_usage_type = :medium)
39
+ def get_instance_type(region_name, api_name)
43
40
  region = get_region(region_name)
44
41
  raise "Region #{region_name} not found" if region.nil?
45
- region.get_instance_type(type, api_name, reserved_usage_type)
42
+ region.get_instance_type(api_name)
46
43
  end
47
44
 
48
45
  protected
@@ -69,48 +66,33 @@ module AwsPricing
69
66
 
70
67
  def get_ec2_on_demand_instance_pricing
71
68
  @@OS_TYPES.each do |os|
72
- fetch_ec2_on_demand_instance_pricing(EC2_BASE_URL + "json/#{os}-od.json", os)
69
+ fetch_ec2_instance_pricing(EC2_BASE_URL + "json/#{os}-od.json", :ondemand, os)
73
70
  end
74
71
  end
75
72
 
76
73
  def get_ec2_reserved_instance_pricing
77
74
  @@OS_TYPES.each do |os|
78
75
  @@RES_TYPES.each do |res_type|
79
- fetch_ec2_reserved_instance_pricing(EC2_BASE_URL + "json/#{os}-ri-#{res_type}.json", res_type, os)
76
+ fetch_ec2_instance_pricing(EC2_BASE_URL + "json/#{os}-ri-#{res_type}.json", res_type, os)
80
77
  end
81
78
  end
82
79
  end
83
80
 
84
81
  # Retrieves the EC2 on-demand instance pricing.
85
- def fetch_ec2_on_demand_instance_pricing(url, platform)
82
+ # type_of_instance = :ondemand, :light, :medium, :heavy
83
+ def fetch_ec2_instance_pricing(url, type_of_instance, operating_system)
86
84
  res = fetch_url(url)
87
- @version_ec2_on_demand_instance = res['vers']
88
85
  res['config']['regions'].each do |reg|
89
86
  region_name = reg['region']
90
87
  region = find_or_create_region(region_name)
88
+ # e.g. type = {"type"=>"hiCPUODI", "sizes"=>[{"size"=>"med", "valueColumns"=>[{"name"=>"mswinSQL", "prices"=>{"USD"=>"N/A"}}]}, {"size"=>"xl", "valueColumns"=>[{"name"=>"mswinSQL", "prices"=>{"USD"=>"2.427"}}]}]}
91
89
  reg['instanceTypes'].each do |type|
90
+ # e.g. size = {"size"=>"xl", "valueColumns"=>[{"name"=>"mswinSQL", "prices"=>{"USD"=>"2.427"}}]}
92
91
  type['sizes'].each do |size|
93
92
  begin
94
- region.add_or_update_instance_type(:on_demand, InstanceType.new(region, type['type'], size, platform))
95
- rescue UnknownTypeError
96
- $stderr.puts "WARNING: encountered #{$!.message}"
97
- end
98
- end
99
- end
100
- end
101
- end
102
- # Retrieves the EC2 on-demand instance pricing.
103
- # reserved_usage_type = :light, :medium, :heavy
104
- def fetch_ec2_reserved_instance_pricing(url, reserved_usage_type, platform)
105
- res = fetch_url(url)
106
- @version_ec2_reserved_instance = res['vers']
107
- res['config']['regions'].each do |reg|
108
- region_name = reg['region']
109
- region = find_or_create_region(region_name)
110
- reg['instanceTypes'].each do |type|
111
- type['sizes'].each do |size|
112
- begin
113
- region.add_or_update_instance_type(:reserved, ReservedInstanceType.new(region, type['type'], size, reserved_usage_type, platform), reserved_usage_type)
93
+ api_name, name = InstanceType.get_name(type["type"], size["size"], type_of_instance != :ondemand)
94
+
95
+ region.add_or_update_instance_type(api_name, name, operating_system, type_of_instance, size)
114
96
  rescue UnknownTypeError
115
97
  $stderr.puts "WARNING: encountered #{$!.message}"
116
98
  end
@@ -19,42 +19,17 @@ module AwsPricing
19
19
  # $0.48/hour for Windows.
20
20
  #
21
21
  class InstanceType
22
- attr_accessor :name, :api_name, :linux_price_per_hour, :windows_price_per_hour,
23
- :memory_in_mb, :disk_in_mb, :platform, :compute_units, :virtual_cores,
24
- :rhel_price_per_hour, :sles_price_per_hour, :mswinSQL_price_per_hour, :mswinSQLWeb_price_per_hour
22
+ attr_accessor :name, :api_name, :memory_in_mb, :disk_in_mb, :platform, :compute_units, :virtual_cores
25
23
 
26
24
  # Initializes and InstanceType object given a region, the internal
27
25
  # type (e.g. stdODI) and the json for the specific instance. The json is
28
26
  # based on the current undocumented AWS pricing API.
29
- def initialize(region, instance_type, json, platform)
30
- # e.g. json = {"size"=>"sm", "valueColumns"=>[{"name"=>"linux", "prices"=>{"USD"=>"0.060"}}]}
31
-
32
- # e.g. {"linux"=>"0.060"}
33
- values = InstanceType::get_values(json)
34
-
35
- @size = json['size']
36
-
37
- pph = values[platform.to_s]
38
- pph = nil if pph == "N/A"
39
-
40
- if platform == :mswin
41
- @windows_price_per_hour = pph
42
- elsif platform == :linux
43
- @linux_price_per_hour = pph
44
- elsif platform == :rhel
45
- @rhel_price_per_hour = pph
46
- elsif platform == :sles
47
- @sles_price_per_hour = pph
48
- elsif platform == :mswinSQL
49
- @mswinSQL_price_per_hour = pph
50
- elsif platform == :mswinSQLWeb
51
- @mswinSQLWeb_price_per_hour = pph
52
- end
53
-
54
- @instance_type = instance_type
27
+ def initialize(region, api_name, name)
28
+ @operating_systems = {}
55
29
 
56
- @api_name = self.class.get_api_name(@instance_type, @size)
57
- @name = self.class.get_name(@instance_type, @size)
30
+ @region = region
31
+ @name = name
32
+ @api_name = api_name
58
33
 
59
34
  @memory_in_mb = @@Memory_Lookup[@api_name]
60
35
  @disk_in_mb = @@Disk_Lookup[@api_name]
@@ -63,52 +38,99 @@ module AwsPricing
63
38
  @virtual_cores = @@Virtual_Cores_Lookup[@api_name]
64
39
  end
65
40
 
66
- # Returns whether an instance_type is available.
67
- # Optionally can specify the specific platform (:linix or :windows).
68
- def available?(platform = nil)
69
- return @linux_price_per_hour != nil if platform == :linux
70
- return @windows_price_per_hour != nil if platform == :windows
71
- return @rhel_price_per_hour != nil if platform == :rhel
72
- return @sles_price_per_hour != nil if platform == :sles
73
- return @mswinSQL_price_per_hour != nil if platform == :mswinSQL
74
- return @mswinSQLWeb_price_per_hour != nil if platform == :mswinSQLWeb
75
- return @linux_price_per_hour != nil || @windows_price_per_hour != nil || @rhel_price_per_hour ||
76
- @sles_price_per_hour != nil || @mswinSQL_price_per_hour != nil || @mswinSQLWeb_price_per_hour
41
+ def operating_systems
42
+ @operating_systems.values
43
+ end
44
+
45
+ def get_operating_system(name)
46
+ @operating_systems[name]
47
+ end
48
+
49
+ # type_of_instance = :ondemand, :light, :medium, :heavy
50
+ # term = :year_1, :year_3, nil
51
+ def price_per_hour(operating_system, type_of_instance, term = nil)
52
+ os = get_operating_system(operating_system)
53
+ os.price_per_hour(type_of_instance, term)
77
54
  end
78
55
 
79
- def is_reserved?
80
- false
56
+ # type_of_instance = :ondemand, :light, :medium, :heavy
57
+ # term = :year_1, :year_3, nil
58
+ def prepay(operating_system, type_of_instance, term = nil)
59
+ os = get_operating_system(operating_system)
60
+ os.prepay(type_of_instance, term)
81
61
  end
82
62
 
83
- def update(instance_type)
84
- # Due to new AWS json we have to make two passes through to populate an instance
85
- @windows_price_per_hour ||= instance_type.windows_price_per_hour
86
- @linux_price_per_hour ||= instance_type.linux_price_per_hour
87
- @rhel_price_per_hour ||= instance_type.rhel_price_per_hour
88
- @sles_price_per_hour ||= instance_type.sles_price_per_hour
89
- @mswinSQL_price_per_hour ||= instance_type.mswinSQL_price_per_hour
90
- @mswinSQLWeb_price_per_hour ||= instance_type.mswinSQLWeb_price_per_hour
63
+ # operating_system = :linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb
64
+ # type_of_instance = :ondemand, :light, :medium, :heavy
65
+ def update_pricing(operating_system, type_of_instance, json)
66
+ os = get_operating_system(operating_system)
67
+ if os.nil?
68
+ os = OperatingSystem.new(self, operating_system)
69
+ @operating_systems[operating_system] = os
70
+ end
71
+
72
+ if type_of_instance == :ondemand
73
+ # e.g. {"size"=>"sm", "valueColumns"=>[{"name"=>"linux", "prices"=>{"USD"=>"0.060"}}]}
74
+ values = InstanceType::get_values(json)
75
+ price = coerce_price(values[operating_system.to_s])
76
+
77
+ os.set_price_per_hour(type_of_instance, nil, price)
78
+ else
79
+ json['valueColumns'].each do |val|
80
+ price = coerce_price(val['prices']['USD'])
81
+
82
+ case val["name"]
83
+ when "yrTerm1"
84
+ os.set_prepay(type_of_instance, :year1, price)
85
+ when "yrTerm3"
86
+ os.set_prepay(type_of_instance, :year3, price)
87
+ when "yrTerm1Hourly"
88
+ os.set_price_per_hour(type_of_instance, :year1, price)
89
+ when "yrTerm3Hourly"
90
+ os.set_price_per_hour(type_of_instance, :year3, price)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ # type_of_instance = :ondemand, :light, :medium, :heavy
97
+ # term = :year_1, :year_3, nil
98
+ def get_breakeven_month(operating_system, type_of_instance, term)
99
+ os = get_operating_system(operating_system)
100
+ os.get_breakeven_month(type_of_instance, term)
91
101
  end
92
102
 
93
103
  protected
94
104
 
95
- attr_accessor :size, :instance_type
105
+ def coerce_price(price)
106
+ return nil if price.nil? || price == "N/A"
107
+ price.to_f
108
+ end
109
+
110
+ #attr_accessor :size, :instance_type
111
+
112
+ # Returns [api_name, name]
113
+ def self.get_name(instance_type, size, is_reserved = false)
114
+ lookup = @@Api_Name_Lookup
115
+ lookup = @@Api_Name_Lookup_Reserved if is_reserved
96
116
 
97
- def self.get_api_name(instance_type, size)
98
117
  # Let's handle new instances more gracefully
99
- unless @@Api_Name_Lookup.has_key? instance_type
118
+ unless lookup.has_key? instance_type
100
119
  raise UnknownTypeError, "Unknown instance type #{instance_type}", caller
101
120
  else
102
- @@Api_Name_Lookup[instance_type][size]
121
+ api_name = lookup[instance_type][size]
103
122
  end
104
- end
105
123
 
106
- def self.get_name(instance_type, size)
107
- @@Name_Lookup[instance_type][size]
124
+ lookup = @@Name_Lookup
125
+ lookup = @@Name_Lookup_Reserved if is_reserved
126
+ name = lookup[instance_type][size]
127
+
128
+ [api_name, name]
108
129
  end
109
130
 
110
131
  # Turn json into hash table for parsing
111
132
  def self.get_values(json)
133
+ # e.g. json = {"size"=>"xl", "valueColumns"=>[{"name"=>"mswinSQL", "prices"=>{"USD"=>"2.427"}}]}
112
134
  values = {}
113
135
  json['valueColumns'].each do |val|
114
136
  values[val['name']] = val['prices']['USD']
@@ -116,7 +138,6 @@ module AwsPricing
116
138
  values
117
139
  end
118
140
 
119
-
120
141
  @@Api_Name_Lookup = {
121
142
  'stdODI' => {'sm' => 'm1.small', 'med' => 'm1.medium', 'lg' => 'm1.large', 'xl' => 'm1.xlarge'},
122
143
  'hiMemODI' => {'xl' => 'm2.xlarge', 'xxl' => 'm2.2xlarge', 'xxxxl' => 'm2.4xlarge'},
@@ -141,6 +162,31 @@ module AwsPricing
141
162
  'clusterHiMemODI' => {'xxxxxxxxl' => 'High-Memory Cluster Eight Extra Large'},
142
163
  'hiStoreODI' => {'xxxxxxxxl' => 'High-Storage Eight Extra Large'},
143
164
  }
165
+ @@Api_Name_Lookup_Reserved = {
166
+ 'stdResI' => {'sm' => 'm1.small', 'med' => 'm1.medium', 'lg' => 'm1.large', 'xl' => 'm1.xlarge'},
167
+ 'hiMemResI' => {'xl' => 'm2.xlarge', 'xxl' => 'm2.2xlarge', 'xxxxl' => 'm2.4xlarge'},
168
+ 'hiCPUResI' => {'med' => 'c1.medium', 'xl' => 'c1.xlarge'},
169
+ 'clusterGPUResI' => {'xxxxl' => 'cg1.4xlarge'},
170
+ 'clusterCompResI' => {'xxxxl' => 'cc1.4xlarge', 'xxxxxxxxl' => 'cc2.8xlarge'},
171
+ 'uResI' => {'u' => 't1.micro'},
172
+ 'hiIoResI' => {'xxxxl' => 'hi1.4xlarge'},
173
+ 'secgenstdResI' => {'xl' => 'm3.xlarge', 'xxl' => 'm3.2xlarge'},
174
+ 'clusterHiMemResI' => {'xxxxxxxxl' => 'cr1.8xlarge'},
175
+ 'hiStoreResI' => {'xxxxxxxxl' => 'hs1.8xlarge'},
176
+ }
177
+ @@Name_Lookup_Reserved = {
178
+ 'stdResI' => {'sm' => 'Standard Small', 'med' => 'Standard Medium', 'lg' => 'Standard Large', 'xl' => 'Standard Extra Large'},
179
+ 'hiMemResI' => {'xl' => 'Hi-Memory Extra Large', 'xxl' => 'Hi-Memory Double Extra Large', 'xxxxl' => 'Hi-Memory Quadruple Extra Large'},
180
+ 'hiCPUResI' => {'med' => 'High-CPU Medium', 'xl' => 'High-CPU Extra Large'},
181
+ 'clusterGPUResI' => {'xxxxl' => 'Cluster GPU Quadruple Extra Large'},
182
+ 'clusterCompResI' => {'xxxxl' => 'Cluster Compute Quadruple Extra Large', 'xxxxxxxxl' => 'Cluster Compute Eight Extra Large'},
183
+ 'uResI' => {'u' => 'Micro'},
184
+ 'hiIoResI' => {'xxxxl' => 'High I/O Quadruple Extra Large Instance'},
185
+ 'secgenstdResI' => {'xl' => 'M3 Extra Large Instance', 'xxl' => 'M3 Double Extra Large Instance'},
186
+ 'clusterHiMemResI' => {'xxxxxxxxl' => 'High-Memory Cluster Eight Extra Large'},
187
+ 'hiStoreResI' => {'xxxxxxxxl' => 'High-Storage Eight Extra Large'},
188
+ }
189
+
144
190
  @@Memory_Lookup = {
145
191
  'm1.small' => 1700, 'm1.medium' => 3750, 'm1.large' => 7500, 'm1.xlarge' => 15000,
146
192
  'm2.xlarge' => 17100, 'm2.2xlarge' => 34200, 'm2.4xlarge' => 68400,
@@ -0,0 +1,150 @@
1
+ #--
2
+ # Amazon Web Services Pricing Ruby library
3
+ #
4
+ # Ruby Gem Name:: amazon-pricing
5
+ # Author:: Joe Kinsella (mailto:joe.kinsella@gmail.com)
6
+ # Copyright:: Copyright (c) 2011-2012 Sonian
7
+ # License:: Distributes under the same terms as Ruby
8
+ # Home:: http://github.com/sonian/amazon-pricing
9
+ #++
10
+ module AwsPricing
11
+
12
+ class OperatingSystem
13
+ attr_accessor :instance_type, :name,
14
+ :ondemand_price_per_hour, :light_price_per_hour_1_year, :medium_price_per_hour_1_year, :heavy_price_per_hour_1_year,
15
+ :light_price_per_hour_3_year, :medium_price_per_hour_3_year, :heavy_price_per_hour_3_year,
16
+ :light_prepay_1_year, :light_prepay_3_year, :medium_prepay_1_year, :medium_prepay_3_year, :heavy_prepay_1_year, :heavy_prepay_3_year
17
+
18
+
19
+ def initialize(instance_type, name)
20
+ @instance_type = instance_type
21
+ @name = name
22
+ end
23
+
24
+ # type_of_instance = :ondemand, :light, :medium, :heavy
25
+ # term = :year_1, :year_3, nil
26
+ def prepay(type_of_instance = :ondemand, term = nil)
27
+ case type_of_instance
28
+ when :ondemand
29
+ 0
30
+ when :light
31
+ if term == :year1
32
+ @light_prepay_1_year
33
+ elsif term == :year3
34
+ @light_prepay_3_year
35
+ end
36
+ when :medium
37
+ if term == :year1
38
+ @medium_prepay_1_year
39
+ elsif term == :year3
40
+ @medium_prepay_3_year
41
+ end
42
+ when :heavy
43
+ if term == :year1
44
+ @heavy_prepay_1_year
45
+ elsif term == :year3
46
+ @heavy_prepay_3_year
47
+ end
48
+ end
49
+ end
50
+
51
+ # type_of_instance = :ondemand, :light, :medium, :heavy
52
+ # term = :year_1, :year_3, nil
53
+ def set_prepay(type_of_instance, term, price)
54
+ case type_of_instance
55
+ when :light
56
+ if term == :year1
57
+ @light_prepay_1_year = price
58
+ elsif term == :year3
59
+ @light_prepay_3_year = price
60
+ end
61
+ when :medium
62
+ if term == :year1
63
+ @medium_prepay_1_year = price
64
+ elsif term == :year3
65
+ @medium_prepay_3_year = price
66
+ end
67
+ when :heavy
68
+ if term == :year1
69
+ @heavy_prepay_1_year = price
70
+ elsif term == :year3
71
+ @heavy_prepay_3_year = price
72
+ end
73
+ end
74
+ end
75
+
76
+ # type_of_instance = :ondemand, :light, :medium, :heavy
77
+ # term = :year_1, :year_3, nil
78
+ def price_per_hour(type_of_instance = :ondemand, term = nil)
79
+ case type_of_instance
80
+ when :ondemand
81
+ @ondemand_price_per_hour
82
+ when :light
83
+ if term == :year1
84
+ @light_price_per_hour_1_year
85
+ elsif term == :year3
86
+ @light_price_per_hour_3_year
87
+ end
88
+ when :medium
89
+ if term == :year1
90
+ @medium_price_per_hour_1_year
91
+ elsif term == :year3
92
+ @medium_price_per_hour_3_year
93
+ end
94
+ when :heavy
95
+ if term == :year1
96
+ @heavy_price_per_hour_1_year
97
+ elsif term == :year3
98
+ @heavy_price_per_hour_3_year
99
+ end
100
+ end
101
+ end
102
+
103
+ # type_of_instance = :ondemand, :light, :medium, :heavy
104
+ # term = :year_1, :year_3, nil
105
+ def set_price_per_hour(type_of_instance, term, price_per_hour)
106
+ case type_of_instance
107
+ when :ondemand
108
+ @ondemand_price_per_hour = price_per_hour
109
+ when :light
110
+ if term == :year1
111
+ @light_price_per_hour_1_year = price_per_hour
112
+ elsif term == :year3
113
+ @light_price_per_hour_3_year = price_per_hour
114
+ end
115
+ when :medium
116
+ if term == :year1
117
+ @medium_price_per_hour_1_year = price_per_hour
118
+ elsif term == :year3
119
+ @medium_price_per_hour_3_year = price_per_hour
120
+ end
121
+ when :heavy
122
+ if term == :year1
123
+ @heavy_price_per_hour_1_year = price_per_hour
124
+ elsif term == :year3
125
+ @heavy_price_per_hour_3_year = price_per_hour
126
+ end
127
+ end
128
+ end
129
+
130
+ # type_of_instance = :ondemand, :light, :medium, :heavy
131
+ # term = :year_1, :year_3, nil
132
+ def get_breakeven_month(type_of_instance, term)
133
+ # Some regions and types do not have reserved available
134
+ ondemand_pph = price_per_hour(:ondemand)
135
+ reserved_pph = price_per_hour(type_of_instance, term)
136
+ return nil if ondemand_pph.nil? || reserved_pph.nil?
137
+
138
+ on_demand = 0
139
+ reserved = prepay(type_of_instance, term)
140
+ for i in 1..36 do
141
+ on_demand += ondemand_pph * 24 * 30.4
142
+ reserved += reserved_pph * 24 * 30.4
143
+ return i if reserved < on_demand
144
+ end
145
+ nil
146
+ end
147
+
148
+ end
149
+
150
+ end
@@ -19,82 +19,40 @@ module AwsPricing
19
19
 
20
20
  def initialize(name)
21
21
  @name = name
22
- @_ec2_on_demand_instance_types = {}
23
- @_ec2_reserved_instance_types_light = {}
24
- @_ec2_reserved_instance_types_medium = {}
25
- @_ec2_reserved_instance_types_heavy = {}
22
+ @instance_types = {}
26
23
  end
27
24
 
28
- def ec2_on_demand_instance_types
29
- @_ec2_on_demand_instance_types.values
25
+ def instance_types
26
+ @instance_types.values
30
27
  end
31
28
 
32
- # reserved_usage_type = :light, :medium, :heavy
33
- def ec2_reserved_instance_types(reserved_usage_type = nil)
34
- case reserved_usage_type
35
- when :light
36
- @_ec2_reserved_instance_types_light.values
37
- when :medium
38
- @_ec2_reserved_instance_types_medium.values
39
- when :heavy
40
- @_ec2_reserved_instance_types_heavy.values
41
- else
42
- @_ec2_reserved_instance_types_light.values + @_ec2_reserved_instance_types_medium.values + @_ec2_reserved_instance_types_heavy.values
43
- end
44
- end
45
-
46
- # Returns whether an instance_type is available. Must specify the type
47
- # (:on_demand or :reserved) and instance type (m1.large). Optionally can
48
- # specify the specific platform (:linix or :windows).
49
- def instance_type_available?(type, api_name, platform = nil)
50
- get_instance_type(type, api_name).available?(platform)
29
+ # Returns whether an instance_type is available.
30
+ # operating_system = :linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb
31
+ # type_of_instance = :ondemand, :light, :medium, :heavy
32
+ def instance_type_available?(api_name, type_of_instance = :ondemand, operating_system = :linux)
33
+ instance = @instance_types[api_name]
34
+ return false if instance.nil?
35
+ os = instance.get_operating_system(operating_system)
36
+ return false if os.nil?
37
+ pph = os.price_per_hour(type_of_instance)
38
+ not pph.nil?
51
39
  end
52
40
 
53
- # instance_type = :on_demand or :reserved
54
- # reserved_usage_type = :light, :medium, :heavy
55
- def add_or_update_instance_type(type, instance_type, reserved_usage_type = :medium)
56
- current = get_instance_type(type, instance_type.api_name, reserved_usage_type)
41
+ # type_of_instance = :ondemand, :light, :medium, :heavy
42
+ def add_or_update_instance_type(api_name, name, operating_system, type_of_instance, json)
43
+ current = get_instance_type(api_name)
57
44
  if current.nil?
58
- if type == :on_demand
59
- @_ec2_on_demand_instance_types[instance_type.api_name] = instance_type
60
- elsif type == :reserved
61
- case reserved_usage_type
62
- when :light
63
- @_ec2_reserved_instance_types_light[instance_type.api_name] = instance_type
64
- when :medium
65
- @_ec2_reserved_instance_types_medium[instance_type.api_name] = instance_type
66
- when :heavy
67
- @_ec2_reserved_instance_types_heavy[instance_type.api_name] = instance_type
68
- end
69
- end
70
- else
71
- current.update(instance_type)
45
+ current = InstanceType.new(self, api_name, name)
46
+ @instance_types[api_name] = current
72
47
  end
48
+ current.update_pricing(operating_system, type_of_instance, json)
49
+ current
73
50
  end
74
51
 
75
- # Type = :on_demand or :reserved
76
- # reserved_usage_type = :light, :medium, :heavy
77
- def get_instance_type(type, api_name, reserved_usage_type = :medium)
78
- if type == :on_demand
79
- @_ec2_on_demand_instance_types[api_name]
80
- elsif type == :reserved
81
- case reserved_usage_type
82
- when :light
83
- @_ec2_reserved_instance_types_light[api_name]
84
- when :medium
85
- @_ec2_reserved_instance_types_medium[api_name]
86
- when :heavy
87
- @_ec2_reserved_instance_types_heavy[api_name]
88
- end
89
- else
90
- nil
91
- end
52
+ def get_instance_type(api_name)
53
+ @instance_types[api_name]
92
54
  end
93
55
 
94
- protected
95
-
96
- attr_accessor :_ec2_on_demand_instance_types, :_ec2_reserved_instance_types_light, :_ec2_reserved_instance_types_medium, :_ec2_reserved_instance_types_heavy
97
-
98
56
  end
99
57
 
100
58
  end
@@ -8,5 +8,5 @@
8
8
  # Home:: http://github.com/sonian/amazon-pricing
9
9
  #++
10
10
  module AwsPricing
11
- VERSION = '0.1.6'
11
+ VERSION = '0.1.7'
12
12
  end
@@ -4,7 +4,7 @@ describe AwsPricing::InstanceType do
4
4
  describe '::get_api_name' do
5
5
  it "raises an UnknownTypeError on an unexpected instance type" do
6
6
  expect {
7
- AwsPricing::InstanceType::get_api_name 'QuantumODI', 'huge'
7
+ AwsPricing::InstanceType::get_name 'QuantumODI', 'huge'
8
8
  }.to raise_error(AwsPricing::UnknownTypeError)
9
9
  end
10
10
  end
@@ -1,6 +1,9 @@
1
1
  require 'amazon-pricing'
2
2
 
3
3
  describe AwsPricing::PriceList do
4
+
5
+ # Need to update below based on 2013-07 changes
6
+ =begin
4
7
  describe "#get_ec2_on_demand_instance_pricing" do
5
8
  let(:price_list) {
6
9
  {
@@ -36,4 +39,5 @@ describe AwsPricing::PriceList do
36
39
  }.to_not raise_error
37
40
  end
38
41
  end
42
+ =end
39
43
  end
@@ -17,7 +17,7 @@ require 'test/unit'
17
17
  class TestEc2InstanceTypes < Test::Unit::TestCase
18
18
  def test_cc8xlarge_issue
19
19
  pricing = AwsPricing::PriceList.new
20
- obj = pricing.get_instance_type('us-east', :reserved, 'cc2.8xlarge', :medium)
20
+ obj = pricing.get_instance_type('us-east', 'cc2.8xlarge')
21
21
  assert obj.api_name == 'cc2.8xlarge'
22
22
  end
23
23
 
@@ -25,25 +25,10 @@ class TestEc2InstanceTypes < Test::Unit::TestCase
25
25
  pricing = AwsPricing::PriceList.new
26
26
  pricing.regions.each do |region|
27
27
  assert_not_nil region.name
28
- region.ec2_on_demand_instance_types.each do |instance_type|
29
- assert_not_nil instance_type.api_name
30
- assert_not_nil instance_type.name
31
- assert instance_type.api_name != instance_type.name
32
- end
33
- region.ec2_reserved_instance_types(:light).each do |instance_type|
34
- assert_not_nil instance_type.api_name
35
- assert_not_nil instance_type.name
36
- assert instance_type.api_name != instance_type.name
37
- end
38
- region.ec2_reserved_instance_types(:medium).each do |instance_type|
39
- assert_not_nil instance_type.api_name
40
- assert_not_nil instance_type.name
41
- assert instance_type.api_name != instance_type.name
42
- end
43
- region.ec2_reserved_instance_types(:heavy).each do |instance_type|
44
- assert_not_nil instance_type.api_name
45
- assert_not_nil instance_type.name
46
- assert instance_type.api_name != instance_type.name
28
+
29
+ region.instance_types.each do |instance|
30
+ assert_not_nil(instance.api_name)
31
+ assert_not_nil(instance.name)
47
32
  end
48
33
  end
49
34
  end
@@ -60,21 +45,29 @@ class TestEc2InstanceTypes < Test::Unit::TestCase
60
45
  # Validate instance types in specific regions are available
61
46
  pricing = AwsPricing::PriceList.new
62
47
  region = pricing.get_region('us-east')
63
- assert region.instance_type_available?(:on_demand, 'm1.large')
48
+ assert region.instance_type_available?('m1.large')
49
+ end
50
+
51
+ def test_breakeven_month
52
+ pricing = AwsPricing::PriceList.new
53
+ region = pricing.get_region('us-east')
54
+ instance = region.get_instance_type('m1.large')
55
+ bem = instance.get_breakeven_month(:linux, :heavy, :year1)
56
+ assert bem == 6
64
57
  end
65
58
 
66
59
  def test_memory
67
60
  # Validate instance types in specific regions are available
68
61
  pricing = AwsPricing::PriceList.new
69
62
  region = pricing.get_region('us-east')
70
- instance = region.get_instance_type(:on_demand, 'm1.large')
63
+ instance = region.get_instance_type('m1.large')
71
64
  assert instance.memory_in_mb == 7500
72
65
  end
73
66
 
74
67
  def test_non_standard_region_name
75
68
  pricing = AwsPricing::PriceList.new
76
69
  region = pricing.get_region('eu-west-1')
77
- instance = region.get_instance_type(:on_demand, 'm1.large')
70
+ instance = region.get_instance_type('m1.large')
78
71
  assert instance.memory_in_mb == 7500
79
72
  end
80
73
 
@@ -101,7 +94,7 @@ class TestEc2InstanceTypes < Test::Unit::TestCase
101
94
  def test_virtual_cores
102
95
  pricing = AwsPricing::PriceList.new
103
96
  region = pricing.get_region('us-east')
104
- instance = region.get_instance_type(:on_demand, 'm1.large')
97
+ instance = region.get_instance_type('m1.large')
105
98
  assert instance.virtual_cores == 2
106
99
  end
107
100
 
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amazon-pricing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
5
- prerelease:
4
+ prerelease:
5
+ version: 0.1.7
6
6
  platform: ruby
7
7
  authors:
8
8
  - Joe Kinsella
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2013-07-19 00:00:00.000000000 Z
@@ -29,8 +29,8 @@ files:
29
29
  - lib/amazon-pricing.rb
30
30
  - lib/amazon-pricing/ebs-price.rb
31
31
  - lib/amazon-pricing/instance-type.rb
32
+ - lib/amazon-pricing/operating-system.rb
32
33
  - lib/amazon-pricing/region.rb
33
- - lib/amazon-pricing/reserved-instance-type.rb
34
34
  - lib/amazon-pricing/version.rb
35
35
  - spec/instance_type_spec.rb
36
36
  - spec/price_list_spec.rb
@@ -38,31 +38,33 @@ files:
38
38
  - test/test-ec2-instance-types.rb
39
39
  homepage: http://github.com/sonian/amazon-pricing
40
40
  licenses: []
41
- post_install_message:
41
+ post_install_message:
42
42
  rdoc_options:
43
- - --title
43
+ - "--title"
44
44
  - amazon-pricing documentation
45
- - --line-numbers
46
- - --main
45
+ - "--line-numbers"
46
+ - "--main"
47
47
  - README
48
48
  require_paths:
49
49
  - lib
50
50
  required_ruby_version: !ruby/object:Gem::Requirement
51
- none: false
52
51
  requirements:
53
- - - ! '>='
52
+ - - ">="
54
53
  - !ruby/object:Gem::Version
55
- version: '0'
56
- required_rubygems_version: !ruby/object:Gem::Requirement
54
+ version: !binary |-
55
+ MA==
57
56
  none: false
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ! '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: !binary |-
62
+ MA==
63
+ none: false
62
64
  requirements: []
63
65
  rubyforge_project: amazon-pricing
64
- rubygems_version: 1.8.25
65
- signing_key:
66
+ rubygems_version: 1.8.24
67
+ signing_key:
66
68
  specification_version: 3
67
69
  summary: Amazon Web Services Pricing Ruby gem
68
70
  test_files:
@@ -70,4 +72,3 @@ test_files:
70
72
  - spec/price_list_spec.rb
71
73
  - test/helper.rb
72
74
  - test/test-ec2-instance-types.rb
73
- has_rdoc:
@@ -1,131 +0,0 @@
1
- #--
2
- # Amazon Web Services Pricing Ruby library
3
- #
4
- # Ruby Gem Name:: amazon-pricing
5
- # Author:: Joe Kinsella (mailto:joe.kinsella@gmail.com)
6
- # Copyright:: Copyright (c) 2011-2012 Sonian
7
- # License:: Distributes under the same terms as Ruby
8
- # Home:: http://github.com/sonian/amazon-pricing
9
- #++
10
- module AwsPricing
11
-
12
- # ReservedInstanceType is a specific type of instance reservation in a region
13
- # with a defined price per hour, and a reservation term of 1 or 3 years. The
14
- # price will vary by platform (Linux, Windows), and as of December 2012,
15
- # reserved instances have three usage types: light, medium and heavy.
16
- #
17
- class ReservedInstanceType < InstanceType
18
- attr_accessor :prepay_1_year, :prepay_3_year, :usage_type,
19
- :linux_price_per_hour_3_year, :windows_price_per_hour_3_year, :rhel_price_per_hour_3_year, :sles_price_per_hour_3_year, :mswinSQL_price_per_hour_3_year, :mswinSQLWeb_price_per_hour_3_year
20
-
21
- # Initializes and InstanceType object given a region, the internal
22
- # type (e.g. stdODI) and the json for the specific instance. The json is
23
- # based on the current undocumented AWS pricing API.
24
- def initialize(region, instance_type, json, usage_type, platform)
25
- super(region, instance_type, json, platform)
26
-
27
- # Fixme: calling twice, fix later
28
- json['valueColumns'].each do |val|
29
- price = val['prices']['USD']
30
- price = nil if price == "N/A"
31
-
32
- case val["name"]
33
- when "yrTerm1"
34
- @prepay_1_year ||= price
35
- when "yrTerm1Hourly"
36
- if platform == :mswin
37
- @windows_price_per_hour = price
38
- elsif platform == :linux
39
- @linux_price_per_hour = price
40
- elsif platform == :rhel
41
- @rhel_price_per_hour = price
42
- elsif platform == :sles
43
- @sles_price_per_hour = price
44
- elsif platform == :mswinSQL
45
- @mswinSQL_price_per_hour = price
46
- elsif platform == :mswinSQLWeb
47
- @mswinSQLWeb_price_per_hour = price
48
- end
49
- when "yrTerm3"
50
- @prepay_3_year ||= price
51
- when "yrTerm3Hourly"
52
- if platform == :mswin
53
- @windows_price_per_hour_3_year = price
54
- elsif platform == :linux
55
- @linux_price_per_hour_3_year = price
56
- elsif platform == :rhel
57
- @rhel_price_per_hour_3_year = price
58
- elsif platform == :sles
59
- @sles_price_per_hour_3_year = price
60
- elsif platform == :mswinSQL
61
- @mswinSQL_price_per_hour_3_year = price
62
- elsif platform == :mswinSQLWeb
63
- @mswinSQLWeb_price_per_hour_3_year = price
64
- end
65
- end
66
- end
67
- @usage_type = usage_type
68
- end
69
-
70
- def to_s
71
- "Reserved Instance Type: #{@region.name} #{@api_name}, 1 Year Prepay=#{@prepay_1_year}, 3 Year Prepay=#{@prepay_3_year}, Linux=$#{@linux_price_per_hour}/hour, Windows=$#{@windows_price_per_hour}/hour"
72
- end
73
-
74
- def is_reserved?
75
- true
76
- end
77
-
78
- def update(instance_type)
79
- super
80
- # Due to new AWS json we have to make two passes through to populate an instance
81
- @linux_price_per_hour_3_year ||= instance_type.linux_price_per_hour_3_year
82
- @windows_price_per_hour_3_year ||= instance_type.windows_price_per_hour_3_year
83
- @rhel_price_per_hour_3_year ||= instance_type.rhel_price_per_hour_3_year
84
- @sles_price_per_hour_3_year ||= instance_type.sles_price_per_hour_3_year
85
- @mswinSQL_price_per_hour_3_year ||= instance_type.mswinSQL_price_per_hour_3_year
86
- @mswinSQLWeb_price_per_hour_3_year ||= instance_type.mswinSQLWeb_price_per_hour_3_year
87
- end
88
-
89
- protected
90
- attr_accessor :size, :instance_type
91
-
92
- def self.get_api_name(instance_type, size)
93
- # Let's handle new instances more gracefully
94
- unless @@Api_Name_Lookup_Reserved.has_key? instance_type
95
- raise UnknownTypeError, "Unknown instance type #{instance_type}", caller
96
- else
97
- @@Api_Name_Lookup_Reserved[instance_type][size]
98
- end
99
- end
100
-
101
- def self.get_name(instance_type, size)
102
- @@Name_Lookup_Reserved[instance_type][size]
103
- end
104
-
105
- @@Api_Name_Lookup_Reserved = {
106
- 'stdResI' => {'sm' => 'm1.small', 'med' => 'm1.medium', 'lg' => 'm1.large', 'xl' => 'm1.xlarge'},
107
- 'hiMemResI' => {'xl' => 'm2.xlarge', 'xxl' => 'm2.2xlarge', 'xxxxl' => 'm2.4xlarge'},
108
- 'hiCPUResI' => {'med' => 'c1.medium', 'xl' => 'c1.xlarge'},
109
- 'clusterGPUResI' => {'xxxxl' => 'cg1.4xlarge'},
110
- 'clusterCompResI' => {'xxxxl' => 'cc1.4xlarge', 'xxxxxxxxl' => 'cc2.8xlarge'},
111
- 'uResI' => {'u' => 't1.micro'},
112
- 'hiIoResI' => {'xxxxl' => 'hi1.4xlarge'},
113
- 'secgenstdResI' => {'xl' => 'm3.xlarge', 'xxl' => 'm3.2xlarge'},
114
- 'clusterHiMemResI' => {'xxxxxxxxl' => 'cr1.8xlarge'},
115
- 'hiStoreResI' => {'xxxxxxxxl' => 'hs1.8xlarge'},
116
- }
117
- @@Name_Lookup_Reserved = {
118
- 'stdResI' => {'sm' => 'Standard Small', 'med' => 'Standard Medium', 'lg' => 'Standard Large', 'xl' => 'Standard Extra Large'},
119
- 'hiMemResI' => {'xl' => 'Hi-Memory Extra Large', 'xxl' => 'Hi-Memory Double Extra Large', 'xxxxl' => 'Hi-Memory Quadruple Extra Large'},
120
- 'hiCPUResI' => {'med' => 'High-CPU Medium', 'xl' => 'High-CPU Extra Large'},
121
- 'clusterGPUResI' => {'xxxxl' => 'Cluster GPU Quadruple Extra Large'},
122
- 'clusterCompResI' => {'xxxxl' => 'Cluster Compute Quadruple Extra Large', 'xxxxxxxxl' => 'Cluster Compute Eight Extra Large'},
123
- 'uResI' => {'u' => 'Micro'},
124
- 'hiIoResI' => {'xxxxl' => 'High I/O Quadruple Extra Large Instance'},
125
- 'secgenstdResI' => {'xl' => 'M3 Extra Large Instance', 'xxl' => 'M3 Double Extra Large Instance'},
126
- 'clusterHiMemResI' => {'xxxxxxxxl' => 'High-Memory Cluster Eight Extra Large'},
127
- 'hiStoreResI' => {'xxxxxxxxl' => 'High-Storage Eight Extra Large'},
128
- }
129
- end
130
-
131
- end