amazon-pricing 0.1.6 → 0.1.7

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.
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