amazon-pricing 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +75 -7
- data/amazon-pricing.gemspec +1 -1
- data/lib/amazon-pricing.rb +147 -27
- data/lib/amazon-pricing/category-type.rb +154 -0
- data/lib/amazon-pricing/database-type.rb +13 -0
- data/lib/amazon-pricing/ec2-instance-type.rb +79 -0
- data/lib/amazon-pricing/instance-type.rb +61 -91
- data/lib/amazon-pricing/operating-system.rb +1 -144
- data/lib/amazon-pricing/rds-instance-type.rb +89 -0
- data/lib/amazon-pricing/region.rb +33 -9
- data/lib/amazon-pricing/version.rb +1 -1
- data/spec/rds-amazon-pricing-spec.rb +34 -0
- data/test/test-ec2-instance-types.rb +16 -16
- metadata +7 -1
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'rake/testtask'
|
|
3
3
|
|
4
4
|
$: << File.expand_path(File.dirname(__FILE__), 'lib')
|
5
5
|
|
6
|
-
require 'amazon-pricing
|
6
|
+
require File.join('amazon-pricing','version')
|
7
7
|
|
8
8
|
Rake::TestTask.new(:test) do |test|
|
9
9
|
test.libs << 'lib' << 'test'
|
@@ -32,10 +32,10 @@ task :test do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
desc "Prints current EC2 pricing in CSV format"
|
35
|
-
task :
|
36
|
-
require '
|
37
|
-
pricing = AwsPricing::
|
38
|
-
line = "Region,Instance Type,API Name,Memory (MB),Disk (
|
35
|
+
task :print_ec2_price_list do
|
36
|
+
require 'amazon-pricing'
|
37
|
+
pricing = AwsPricing::Ec2PriceList.new
|
38
|
+
line = "Region,Instance Type,API Name,Memory (MB),Disk (GB),Compute Units,Virtual Cores,Disk Type,OD Linux PPH,OD Windows PPH,OD RHEL PPH,OD SLES PPH,OD MsWinSQL PPH,OD MsWinSQLWeb PPH,"
|
39
39
|
[:year1, :year3].each do |term|
|
40
40
|
[:light, :medium, :heavy].each do |res_type|
|
41
41
|
[:linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb].each do |os|
|
@@ -45,8 +45,8 @@ task :print_price_list do
|
|
45
45
|
end
|
46
46
|
puts line.chop
|
47
47
|
pricing.regions.each do |region|
|
48
|
-
region.
|
49
|
-
line = "#{region.name},#{t.name},#{t.api_name},#{t.memory_in_mb},#{t.
|
48
|
+
region.ec2_instance_types.each do |t|
|
49
|
+
line = "#{region.name},#{t.name},#{t.api_name},#{t.memory_in_mb},#{t.disk_in_gb},#{t.compute_units},#{t.virtual_cores},#{t.disk_type},"
|
50
50
|
[:linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb].each do |os|
|
51
51
|
line += "#{t.price_per_hour(os, :ondemand)},"
|
52
52
|
end
|
@@ -62,4 +62,72 @@ task :print_price_list do
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
|
66
|
+
desc "Prints current RDS pricing in CSV format"
|
67
|
+
task :print_rds_price_list do
|
68
|
+
require 'amazon-pricing'
|
69
|
+
pricing = AwsPricing::RdsPriceList.new
|
70
|
+
|
71
|
+
line = "Region,Instance Type,API Name,Memory (MB),Disk (GB),Compute Units,Virtual Cores,Disk Type,"
|
72
|
+
|
73
|
+
|
74
|
+
[:mysql, :oracle, :oracle_byol, :sqlserver, :sqlserver_express, :sqlserver_web, :sqlserver_byol].each do |db|
|
75
|
+
if [:mysql, :oracle, :oracle_byol].include? db
|
76
|
+
[:standard,:multiAZ].each do |deploy_type|
|
77
|
+
line += "OD #{db} #{deploy_type} PPH,"
|
78
|
+
end
|
79
|
+
else
|
80
|
+
line += "OD #{db} PPH,"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
[:year1, :year3].each do |term|
|
86
|
+
[:light, :medium, :heavy].each do |res_type|
|
87
|
+
[:mysql, :oracle, :oracle_byol, :sqlserver, :sqlserver_express, :sqlserver_web, :sqlserver_byol].each do |db|
|
88
|
+
if [:mysql, :oracle, :oracle_byol].include? db
|
89
|
+
[:standard,:multiAZ].each do |deploy_type|
|
90
|
+
line += "#{term} #{res_type} #{deploy_type} #{db} Prepay,#{term} #{res_type} #{deploy_type} #{db} PPH,"
|
91
|
+
end
|
92
|
+
else
|
93
|
+
line += "#{term} #{res_type} #{db} Prepay,#{term} #{res_type} #{db} PPH,"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
puts line.chop
|
101
|
+
|
102
|
+
pricing.regions.each do |region|
|
103
|
+
region.rds_instance_types.each do |t|
|
104
|
+
line = "#{region.name},#{t.name},#{t.api_name},#{t.memory_in_mb},#{t.disk_in_gb},#{t.compute_units},#{t.virtual_cores},#{t.disk_type},"
|
105
|
+
[:mysql, :oracle, :oracle_byol, :sqlserver, :sqlserver_express, :sqlserver_web, :sqlserver_byol].each do |db|
|
106
|
+
if [:mysql, :oracle, :oracle_byol].include? db
|
107
|
+
[:standard,:multiAZ].each do |deploy_type|
|
108
|
+
line += "#{t.price_per_hour(db, :ondemand, nil, deploy_type == :multiAZ)},"
|
109
|
+
end
|
110
|
+
else
|
111
|
+
line += "#{t.price_per_hour(db, :ondemand, nil, false)},"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
[:year1, :year3].each do |term|
|
116
|
+
[:light, :medium, :heavy].each do |res_type|
|
117
|
+
[:mysql, :oracle, :oracle_byol, :sqlserver, :sqlserver_express, :sqlserver_web, :sqlserver_byol].each do |db|
|
118
|
+
if [:mysql, :oracle, :oracle_byol].include? db
|
119
|
+
[:standard,:multiAZ].each do |deploy_type|
|
120
|
+
line += "#{t.prepay(db, res_type, term, deploy_type == :multiAZ)},#{t.price_per_hour(db, res_type, term, deploy_type == :multiAZ)},"
|
121
|
+
end
|
122
|
+
else
|
123
|
+
line += "#{t.prepay(db, res_type, term, false)},#{t.price_per_hour(db, res_type, term, false)},"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
puts line.chop
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
65
133
|
task :default => [:test]
|
data/amazon-pricing.gemspec
CHANGED
data/lib/amazon-pricing.rb
CHANGED
@@ -18,16 +18,9 @@ module AwsPricing
|
|
18
18
|
# Upon instantiating a PriceList object, all the corresponding pricing
|
19
19
|
# information will be retrieved from Amazon via currently undocumented
|
20
20
|
# json APIs.
|
21
|
-
|
21
|
+
class PriceList
|
22
22
|
attr_accessor :regions
|
23
23
|
|
24
|
-
def initialize
|
25
|
-
@_regions = {}
|
26
|
-
get_ec2_on_demand_instance_pricing
|
27
|
-
get_ec2_reserved_instance_pricing
|
28
|
-
fetch_ec2_ebs_pricing
|
29
|
-
end
|
30
|
-
|
31
24
|
def get_region(name)
|
32
25
|
@_regions[@@Region_Lookup[name] || name]
|
33
26
|
end
|
@@ -42,6 +35,12 @@ module AwsPricing
|
|
42
35
|
region.get_instance_type(api_name)
|
43
36
|
end
|
44
37
|
|
38
|
+
def fetch_url(url)
|
39
|
+
uri = URI.parse(url)
|
40
|
+
page = Net::HTTP.get_response(uri)
|
41
|
+
JSON.parse(page.body)
|
42
|
+
end
|
43
|
+
|
45
44
|
protected
|
46
45
|
|
47
46
|
attr_accessor :_regions
|
@@ -59,6 +58,33 @@ module AwsPricing
|
|
59
58
|
region
|
60
59
|
end
|
61
60
|
|
61
|
+
EC2_BASE_URL = "http://aws.amazon.com/ec2/pricing/"
|
62
|
+
RDS_BASE_URL = "http://aws.amazon.com/rds/pricing/"
|
63
|
+
|
64
|
+
# Lookup allows us to map to AWS API region names
|
65
|
+
@@Region_Lookup = {
|
66
|
+
'us-east-1' => 'us-east',
|
67
|
+
'us-west-1' => 'us-west',
|
68
|
+
'us-west-2' => 'us-west-2',
|
69
|
+
'eu-west-1' => 'eu-ireland',
|
70
|
+
'ap-southeast-1' => 'apac-sin',
|
71
|
+
'ap-southeast-2' => 'apac-syd',
|
72
|
+
'ap-northeast-1' => 'apac-tokyo',
|
73
|
+
'sa-east-1' => 'sa-east-1'
|
74
|
+
}
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
class Ec2PriceList < PriceList
|
80
|
+
|
81
|
+
def initialize
|
82
|
+
@_regions = {}
|
83
|
+
get_ec2_on_demand_instance_pricing
|
84
|
+
get_ec2_reserved_instance_pricing
|
85
|
+
fetch_ec2_ebs_pricing
|
86
|
+
end
|
87
|
+
|
62
88
|
protected
|
63
89
|
|
64
90
|
@@OS_TYPES = [:linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb]
|
@@ -90,9 +116,9 @@ module AwsPricing
|
|
90
116
|
# e.g. size = {"size"=>"xl", "valueColumns"=>[{"name"=>"mswinSQL", "prices"=>{"USD"=>"2.427"}}]}
|
91
117
|
type['sizes'].each do |size|
|
92
118
|
begin
|
93
|
-
api_name, name =
|
94
|
-
|
95
|
-
region.
|
119
|
+
api_name, name = Ec2InstanceType.get_name(type["type"], size["size"], type_of_instance != :ondemand)
|
120
|
+
|
121
|
+
region.add_or_update_ec2_instance_type(api_name, name, operating_system, type_of_instance, size)
|
96
122
|
rescue UnknownTypeError
|
97
123
|
$stderr.puts "WARNING: encountered #{$!.message}"
|
98
124
|
end
|
@@ -109,24 +135,118 @@ module AwsPricing
|
|
109
135
|
end
|
110
136
|
end
|
111
137
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
|
142
|
+
class RdsPriceList < PriceList
|
143
|
+
|
144
|
+
def initialize
|
145
|
+
@_regions = {}
|
146
|
+
get_rds_on_demand_instance_pricing
|
147
|
+
get_rds_reserved_instance_pricing
|
116
148
|
end
|
117
149
|
|
118
|
-
|
150
|
+
protected
|
119
151
|
|
120
|
-
|
121
|
-
@@
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
152
|
+
@@DB_TYPE = [:mysql, :oracle, :sqlserver]
|
153
|
+
@@RES_TYPES = [:light, :medium, :heavy]
|
154
|
+
|
155
|
+
@@OD_DB_DEPLOY_TYPE = {
|
156
|
+
:mysql=> {:mysql=>["standard","multiAZ"]},
|
157
|
+
:oracle=> {:oracle=>["li-standard","li-multiAZ"], :oracle_byol=>["byol-standard","byol-multiAZ"]},
|
158
|
+
:sqlserver=> {:sqlserver=>["li-se"], :sqlserver_express=>["li-ex"], :sqlserver_web=>["li-web"], :sqlserver_byol=>["byol"]}
|
159
|
+
}
|
160
|
+
|
161
|
+
|
162
|
+
@@RESERVED_DB_DEPLOY_TYPE = {
|
163
|
+
:oracle=> {:oracle=>"li", :oracle_byol=>"byol"},
|
164
|
+
:sqlserver=> {:sqlserver=>"li-se", :sqlserver_express=>"li-ex", :sqlserver_web=>"li-web", :sqlserver_byol=>"byol"}
|
165
|
+
}
|
166
|
+
|
167
|
+
|
168
|
+
def is_multi_az?(type)
|
169
|
+
return true if type.match("multiAZ")
|
170
|
+
false
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_rds_on_demand_instance_pricing
|
174
|
+
@@DB_TYPE.each do |db|
|
175
|
+
@@OD_DB_DEPLOY_TYPE[db].each {|db_type, db_instances|
|
176
|
+
db_instances.each do |dp_type|
|
177
|
+
if db == :mysql or db == :oracle
|
178
|
+
fetch_on_demand_rds_instance_pricing(RDS_BASE_URL+"#{db}/pricing-#{dp_type}-deployments.json",:ondemand, db_type)
|
179
|
+
elsif db == :sqlserver
|
180
|
+
fetch_on_demand_rds_instance_pricing(RDS_BASE_URL+"#{db}/sqlserver-#{dp_type}-ondemand.json",:ondemand, db_type)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
}
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_rds_reserved_instance_pricing
|
188
|
+
@@DB_TYPE.each do |db|
|
189
|
+
if db == :mysql
|
190
|
+
@@RES_TYPES.each do |res_type|
|
191
|
+
fetch_reserved_rds_instance_pricing(RDS_BASE_URL+"#{db}/pricing-#{res_type}-utilization-reserved-instances.json", res_type, db)
|
192
|
+
end
|
193
|
+
else
|
194
|
+
@@RESERVED_DB_DEPLOY_TYPE[db].each {|db_type, db_instance|
|
195
|
+
@@RES_TYPES.each do |res_type|
|
196
|
+
fetch_reserved_rds_instance_pricing(RDS_BASE_URL+"#{db}/pricing-#{db_instance}-#{res_type}-utilization-reserved-instances.json", res_type, db_type) if db == :oracle
|
197
|
+
fetch_reserved_rds_instance_pricing(RDS_BASE_URL+"#{db}/sqlserver-#{db_instance}-#{res_type}-ri.json", res_type, db_type) if db == :sqlserver
|
198
|
+
end
|
199
|
+
}
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def fetch_on_demand_rds_instance_pricing(url, type_of_rds_instance, db_type)
|
205
|
+
res = fetch_url(url)
|
206
|
+
res['config']['regions'].each do |reg|
|
207
|
+
region_name = reg['region']
|
208
|
+
region = find_or_create_region(region_name)
|
209
|
+
reg['types'].each do |type|
|
210
|
+
type['tiers'].each do |tier|
|
211
|
+
begin
|
212
|
+
#
|
213
|
+
# this is special case URL, it is oracle - multiAZ type of deployment but it doesn't have mutliAZ attributes in json.
|
214
|
+
if url == "http://aws.amazon.com/rds/pricing/oracle/pricing-li-multiAZ-deployments.json"
|
215
|
+
isMultiAz = true
|
216
|
+
else
|
217
|
+
isMultiAz = is_multi_az? type["name"]
|
218
|
+
end
|
219
|
+
api_name, name = RdsInstanceType.get_name(type["name"], tier["name"], type_of_rds_instance != :ondemand)
|
220
|
+
|
221
|
+
region.add_or_update_rds_instance_type(api_name, name, db_type, type_of_rds_instance, tier, isMultiAz)
|
222
|
+
rescue UnknownTypeError
|
223
|
+
$stderr.puts "WARNING: encountered #{$!.message}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def fetch_reserved_rds_instance_pricing(url, type_of_rds_instance, db_type)
|
231
|
+
res = fetch_url(url)
|
232
|
+
res['config']['regions'].each do |reg|
|
233
|
+
region_name = reg['region']
|
234
|
+
region = find_or_create_region(region_name)
|
235
|
+
reg['instanceTypes'].each do |type|
|
236
|
+
type['tiers'].each do |tier|
|
237
|
+
begin
|
238
|
+
isMultiAz = is_multi_az? type["type"]
|
239
|
+
api_name, name = RdsInstanceType.get_name(type["type"], tier["size"], true)
|
240
|
+
|
241
|
+
region.add_or_update_rds_instance_type(api_name, name, db_type, type_of_rds_instance, tier, isMultiAz)
|
242
|
+
rescue UnknownTypeError
|
243
|
+
$stderr.puts "WARNING: encountered #{$!.message}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
131
249
|
end
|
250
|
+
|
251
|
+
|
132
252
|
end
|
@@ -0,0 +1,154 @@
|
|
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-2013 CloudHealth
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
8
|
+
# Home:: http://github.com/CloudHealth/amazon-pricing
|
9
|
+
#++
|
10
|
+
module AwsPricing
|
11
|
+
|
12
|
+
class CategoryType
|
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
|
+
# Returns whether an instance_type is available.
|
25
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
26
|
+
def available?(type_of_instance = :ondemand)
|
27
|
+
not price_per_hour(type_of_instance).nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
31
|
+
# term = :year_1, :year_3, nil
|
32
|
+
def prepay(type_of_instance = :ondemand, term = nil)
|
33
|
+
case type_of_instance
|
34
|
+
when :ondemand
|
35
|
+
0
|
36
|
+
when :light
|
37
|
+
if term == :year1
|
38
|
+
@light_prepay_1_year
|
39
|
+
elsif term == :year3
|
40
|
+
@light_prepay_3_year
|
41
|
+
end
|
42
|
+
when :medium
|
43
|
+
if term == :year1
|
44
|
+
@medium_prepay_1_year
|
45
|
+
elsif term == :year3
|
46
|
+
@medium_prepay_3_year
|
47
|
+
end
|
48
|
+
when :heavy
|
49
|
+
if term == :year1
|
50
|
+
@heavy_prepay_1_year
|
51
|
+
elsif term == :year3
|
52
|
+
@heavy_prepay_3_year
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
58
|
+
# term = :year_1, :year_3, nil
|
59
|
+
def set_prepay(type_of_instance, term, price)
|
60
|
+
case type_of_instance
|
61
|
+
when :light
|
62
|
+
if term == :year1
|
63
|
+
@light_prepay_1_year = price
|
64
|
+
elsif term == :year3
|
65
|
+
@light_prepay_3_year = price
|
66
|
+
end
|
67
|
+
when :medium
|
68
|
+
if term == :year1
|
69
|
+
@medium_prepay_1_year = price
|
70
|
+
elsif term == :year3
|
71
|
+
@medium_prepay_3_year = price
|
72
|
+
end
|
73
|
+
when :heavy
|
74
|
+
if term == :year1
|
75
|
+
@heavy_prepay_1_year = price
|
76
|
+
elsif term == :year3
|
77
|
+
@heavy_prepay_3_year = price
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
83
|
+
# term = :year_1, :year_3, nil
|
84
|
+
def price_per_hour(type_of_instance = :ondemand, term = nil)
|
85
|
+
case type_of_instance
|
86
|
+
when :ondemand
|
87
|
+
@ondemand_price_per_hour
|
88
|
+
when :light
|
89
|
+
if term == :year1
|
90
|
+
@light_price_per_hour_1_year
|
91
|
+
elsif term == :year3
|
92
|
+
@light_price_per_hour_3_year
|
93
|
+
end
|
94
|
+
when :medium
|
95
|
+
if term == :year1
|
96
|
+
@medium_price_per_hour_1_year
|
97
|
+
elsif term == :year3
|
98
|
+
@medium_price_per_hour_3_year
|
99
|
+
end
|
100
|
+
when :heavy
|
101
|
+
if term == :year1
|
102
|
+
@heavy_price_per_hour_1_year
|
103
|
+
elsif term == :year3
|
104
|
+
@heavy_price_per_hour_3_year
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
110
|
+
# term = :year_1, :year_3, nil
|
111
|
+
def set_price_per_hour(type_of_instance, term, price_per_hour)
|
112
|
+
case type_of_instance
|
113
|
+
when :ondemand
|
114
|
+
@ondemand_price_per_hour = price_per_hour
|
115
|
+
when :light
|
116
|
+
if term == :year1
|
117
|
+
@light_price_per_hour_1_year = price_per_hour
|
118
|
+
elsif term == :year3
|
119
|
+
@light_price_per_hour_3_year = price_per_hour
|
120
|
+
end
|
121
|
+
when :medium
|
122
|
+
if term == :year1
|
123
|
+
@medium_price_per_hour_1_year = price_per_hour
|
124
|
+
elsif term == :year3
|
125
|
+
@medium_price_per_hour_3_year = price_per_hour
|
126
|
+
end
|
127
|
+
when :heavy
|
128
|
+
if term == :year1
|
129
|
+
@heavy_price_per_hour_1_year = price_per_hour
|
130
|
+
elsif term == :year3
|
131
|
+
@heavy_price_per_hour_3_year = price_per_hour
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
137
|
+
# term = :year_1, :year_3, nil
|
138
|
+
def get_breakeven_month(type_of_instance, term)
|
139
|
+
# Some regions and types do not have reserved available
|
140
|
+
ondemand_pph = price_per_hour(:ondemand)
|
141
|
+
reserved_pph = price_per_hour(type_of_instance, term)
|
142
|
+
return nil if ondemand_pph.nil? || reserved_pph.nil?
|
143
|
+
|
144
|
+
on_demand = 0
|
145
|
+
reserved = prepay(type_of_instance, term)
|
146
|
+
for i in 1..36 do
|
147
|
+
on_demand += ondemand_pph * 24 * 30.4
|
148
|
+
reserved += reserved_pph * 24 * 30.4
|
149
|
+
return i if reserved < on_demand
|
150
|
+
end
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,13 @@
|
|
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-2013 CloudHealth
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
8
|
+
# Home:: http://github.com/CloudHealth/amazon-pricing
|
9
|
+
#++
|
10
|
+
module AwsPricing
|
11
|
+
class DatabaseType < CategoryType
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'amazon-pricing/instance-type'
|
2
|
+
module AwsPricing
|
3
|
+
class Ec2InstanceType < InstanceType
|
4
|
+
|
5
|
+
# Returns whether an instance_type is available.
|
6
|
+
# operating_system = :linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb
|
7
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
8
|
+
def available?(type_of_instance = :ondemand, operating_system = :linux)
|
9
|
+
os = get_category_type(operating_system)
|
10
|
+
return false if os.nil?
|
11
|
+
os.available?(type_of_instance)
|
12
|
+
end
|
13
|
+
|
14
|
+
# operating_system = :linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb
|
15
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
16
|
+
def update_pricing(operating_system, type_of_instance, json)
|
17
|
+
os = get_category_type(operating_system)
|
18
|
+
if os.nil?
|
19
|
+
os = OperatingSystem.new(self, operating_system)
|
20
|
+
@category_types[operating_system] = os
|
21
|
+
end
|
22
|
+
|
23
|
+
if type_of_instance == :ondemand
|
24
|
+
# e.g. {"size"=>"sm", "valueColumns"=>[{"name"=>"linux", "prices"=>{"USD"=>"0.060"}}]}
|
25
|
+
values = Ec2InstanceType::get_values(json, operating_system)
|
26
|
+
price = coerce_price(values[operating_system.to_s])
|
27
|
+
os.set_price_per_hour(type_of_instance, nil, price)
|
28
|
+
else
|
29
|
+
json['valueColumns'].each do |val|
|
30
|
+
price = coerce_price(val['prices']['USD'])
|
31
|
+
|
32
|
+
case val["name"]
|
33
|
+
when "yrTerm1"
|
34
|
+
os.set_prepay(type_of_instance, :year1, price)
|
35
|
+
when "yrTerm3"
|
36
|
+
os.set_prepay(type_of_instance, :year3, price)
|
37
|
+
when "yrTerm1Hourly"
|
38
|
+
os.set_price_per_hour(type_of_instance, :year1, price)
|
39
|
+
when "yrTerm3Hourly"
|
40
|
+
os.set_price_per_hour(type_of_instance, :year3, price)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Maintained for backward compatibility reasons
|
47
|
+
def operating_systems
|
48
|
+
category_types
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
# Returns [api_name, name]
|
53
|
+
def self.get_name(instance_type, api_name, is_reserved = false)
|
54
|
+
# Let's handle new instances more gracefully
|
55
|
+
unless @@Memory_Lookup.has_key? api_name
|
56
|
+
raise UnknownTypeError, "Unknown instance type #{instance_type} #{api_name}", caller
|
57
|
+
end
|
58
|
+
|
59
|
+
name = @@Name_Lookup[api_name]
|
60
|
+
|
61
|
+
[api_name, name]
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
@@Name_Lookup = {
|
66
|
+
'm1.small' => 'Standard Small', 'm1.medium' => 'Standard Medium', 'm1.large' => 'Standard Large', 'm1.xlarge' => 'Standard Extra Large',
|
67
|
+
'm2.xlarge' => 'Hi-Memory Extra Large', 'm2.2xlarge' => 'Hi-Memory Double Extra Large', 'm2.4xlarge' => 'Hi-Memory Quadruple Extra Large',
|
68
|
+
'm3.xlarge' => 'M3 Extra Large Instance', 'm3.2xlarge' => 'M3 Double Extra Large Instance',
|
69
|
+
'c1.medium' => 'High-CPU Medium', 'c1.xlarge' => 'High-CPU Extra Large',
|
70
|
+
'hi1.4xlarge' => 'High I/O Quadruple Extra Large',
|
71
|
+
'cg1.4xlarge' => 'Cluster GPU Quadruple Extra Large',
|
72
|
+
'cc1.4xlarge' => 'Cluster Compute Quadruple Extra Large', 'cc2.8xlarge' => 'Cluster Compute Eight Extra Large',
|
73
|
+
't1.micro' => 'Micro',
|
74
|
+
'cr1.8xlarge' => 'High-Memory Cluster Eight Extra Large',
|
75
|
+
'hs1.8xlarge' => 'High-Storage Eight Extra Large',
|
76
|
+
'g2.2xlarge' => 'Cluster GPU Double Extra Large',
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
@@ -19,93 +19,62 @@ module AwsPricing
|
|
19
19
|
# $0.48/hour for Windows.
|
20
20
|
#
|
21
21
|
class InstanceType
|
22
|
-
attr_accessor :name, :api_name, :memory_in_mb, :
|
23
|
-
|
24
|
-
# Initializes and InstanceType object given a region, the internal
|
25
|
-
# type (e.g. stdODI) and the json for the specific instance. The json is
|
26
|
-
# based on the current undocumented AWS pricing API.
|
22
|
+
attr_accessor :name, :api_name, :memory_in_mb, :platform, :compute_units, :virtual_cores, :disk_type, :disk_in_gb
|
23
|
+
|
27
24
|
def initialize(region, api_name, name)
|
28
|
-
@
|
25
|
+
@category_types = {}
|
29
26
|
|
30
27
|
@region = region
|
31
28
|
@name = name
|
32
29
|
@api_name = api_name
|
33
30
|
|
34
31
|
@memory_in_mb = @@Memory_Lookup[@api_name]
|
35
|
-
@
|
32
|
+
@disk_in_gb = @@Disk_Lookup[@api_name]
|
36
33
|
@platform = @@Platform_Lookup[@api_name]
|
37
34
|
@compute_units = @@Compute_Units_Lookup[@api_name]
|
38
35
|
@virtual_cores = @@Virtual_Cores_Lookup[@api_name]
|
36
|
+
@disk_type = @@Disk_Type_Lookup[@api_name]
|
39
37
|
end
|
40
38
|
|
41
|
-
|
42
|
-
|
39
|
+
# Keep this in for backwards compatibility within current major version of gem
|
40
|
+
def disk_in_mb
|
41
|
+
@disk_in_gb * 1000
|
43
42
|
end
|
44
43
|
|
45
|
-
def
|
46
|
-
@
|
44
|
+
def memory_in_gb
|
45
|
+
@memory_in_mb / 1000
|
47
46
|
end
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
# type_of_instance = :ondemand, :light, :medium, :heavy
|
52
|
-
def available?(type_of_instance = :ondemand, operating_system = :linux)
|
53
|
-
os = get_operating_system(operating_system)
|
54
|
-
return false if os.nil?
|
55
|
-
os.available?(type_of_instance)
|
48
|
+
def category_types
|
49
|
+
@category_types.values
|
56
50
|
end
|
57
51
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
52
|
+
def get_category_type(name, multiAz = false)
|
53
|
+
if multiAz
|
54
|
+
db = @category_types["#{name}_multiAz"]
|
55
|
+
else
|
56
|
+
db = @category_types[name]
|
57
|
+
end
|
63
58
|
end
|
64
59
|
|
65
60
|
# type_of_instance = :ondemand, :light, :medium, :heavy
|
66
61
|
# term = :year_1, :year_3, nil
|
67
|
-
def
|
68
|
-
|
69
|
-
|
62
|
+
def price_per_hour(category_type, type_of_instance, term = nil, isMultiAz = false)
|
63
|
+
cat = get_category_type(category_type, isMultiAz)
|
64
|
+
cat.price_per_hour(type_of_instance, term) unless cat.nil?
|
70
65
|
end
|
71
66
|
|
72
|
-
# operating_system = :linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb
|
73
67
|
# type_of_instance = :ondemand, :light, :medium, :heavy
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
@operating_systems[operating_system] = os
|
79
|
-
end
|
80
|
-
|
81
|
-
if type_of_instance == :ondemand
|
82
|
-
# e.g. {"size"=>"sm", "valueColumns"=>[{"name"=>"linux", "prices"=>{"USD"=>"0.060"}}]}
|
83
|
-
values = InstanceType::get_values(json)
|
84
|
-
price = coerce_price(values[operating_system.to_s])
|
85
|
-
|
86
|
-
os.set_price_per_hour(type_of_instance, nil, price)
|
87
|
-
else
|
88
|
-
json['valueColumns'].each do |val|
|
89
|
-
price = coerce_price(val['prices']['USD'])
|
90
|
-
|
91
|
-
case val["name"]
|
92
|
-
when "yrTerm1"
|
93
|
-
os.set_prepay(type_of_instance, :year1, price)
|
94
|
-
when "yrTerm3"
|
95
|
-
os.set_prepay(type_of_instance, :year3, price)
|
96
|
-
when "yrTerm1Hourly"
|
97
|
-
os.set_price_per_hour(type_of_instance, :year1, price)
|
98
|
-
when "yrTerm3Hourly"
|
99
|
-
os.set_price_per_hour(type_of_instance, :year3, price)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
68
|
+
# term = :year_1, :year_3, nil
|
69
|
+
def prepay(category_type, type_of_instance, term = nil, isMultiAz = false)
|
70
|
+
cat = get_category_type(category_type, isMultiAz)
|
71
|
+
cat.prepay(type_of_instance, term) unless cat.nil?
|
103
72
|
end
|
104
73
|
|
105
74
|
# type_of_instance = :ondemand, :light, :medium, :heavy
|
106
75
|
# term = :year_1, :year_3, nil
|
107
|
-
def get_breakeven_month(
|
108
|
-
os =
|
76
|
+
def get_breakeven_month(category_types, type_of_instance, term)
|
77
|
+
os = get_category_type(category_types)
|
109
78
|
os.get_breakeven_month(type_of_instance, term)
|
110
79
|
end
|
111
80
|
|
@@ -113,46 +82,21 @@ module AwsPricing
|
|
113
82
|
|
114
83
|
def coerce_price(price)
|
115
84
|
return nil if price.nil? || price == "N/A"
|
116
|
-
price.to_f
|
117
|
-
end
|
118
|
-
|
119
|
-
#attr_accessor :size, :instance_type
|
120
|
-
|
121
|
-
# Returns [api_name, name]
|
122
|
-
def self.get_name(instance_type, api_name, is_reserved = false)
|
123
|
-
# Let's handle new instances more gracefully
|
124
|
-
unless @@Memory_Lookup.has_key? api_name
|
125
|
-
raise UnknownTypeError, "Unknown instance type #{instance_type} #{api_name}", caller
|
126
|
-
end
|
127
|
-
|
128
|
-
name = @@Name_Lookup[api_name]
|
129
|
-
|
130
|
-
[api_name, name]
|
85
|
+
price.gsub(",","").to_f
|
131
86
|
end
|
132
87
|
|
133
|
-
|
134
|
-
def self.get_values(json)
|
135
|
-
# e.g. json = {"size"=>"xl", "valueColumns"=>[{"name"=>"mswinSQL", "prices"=>{"USD"=>"2.427"}}]}
|
88
|
+
def self.get_values(json, category_type)
|
136
89
|
values = {}
|
137
|
-
json['valueColumns'].
|
138
|
-
|
90
|
+
unless json['valueColumns'].nil?
|
91
|
+
json['valueColumns'].each do |val|
|
92
|
+
values[val['name']] = val['prices']['USD']
|
93
|
+
end
|
94
|
+
else
|
95
|
+
values[category_type.to_s] = json['prices']['USD']
|
139
96
|
end
|
140
97
|
values
|
141
98
|
end
|
142
99
|
|
143
|
-
@@Name_Lookup = {
|
144
|
-
'm1.small' => 'Standard Small', 'm1.medium' => 'Standard Medium', 'm1.large' => 'Standard Large', 'm1.xlarge' => 'Standard Extra Large',
|
145
|
-
'm2.xlarge' => 'Hi-Memory Extra Large', 'm2.2xlarge' => 'Hi-Memory Double Extra Large', 'm2.4xlarge' => 'Hi-Memory Quadruple Extra Large',
|
146
|
-
'm3.xlarge' => 'M3 Extra Large Instance', 'm3.2xlarge' => 'M3 Double Extra Large Instance',
|
147
|
-
'c1.medium' => 'High-CPU Medium', 'c1.xlarge' => 'High-CPU Extra Large',
|
148
|
-
'hi1.4xlarge' => 'High I/O Quadruple Extra Large',
|
149
|
-
'cg1.4xlarge' => 'Cluster GPU Quadruple Extra Large',
|
150
|
-
'cc1.4xlarge' => 'Cluster Compute Quadruple Extra Large', 'cc2.8xlarge' => 'Cluster Compute Eight Extra Large',
|
151
|
-
't1.micro' => 'Micro',
|
152
|
-
'cr1.8xlarge' => 'High-Memory Cluster Eight Extra Large',
|
153
|
-
'hs1.8xlarge' => 'High-Storage Eight Extra Large',
|
154
|
-
'g2.2xlarge' => 'Cluster GPU Double Extra Large',
|
155
|
-
}
|
156
100
|
@@Memory_Lookup = {
|
157
101
|
'm1.small' => 1700, 'm1.medium' => 3750, 'm1.large' => 7500, 'm1.xlarge' => 15000,
|
158
102
|
'm2.xlarge' => 17100, 'm2.2xlarge' => 34200, 'm2.4xlarge' => 68400,
|
@@ -166,6 +110,8 @@ module AwsPricing
|
|
166
110
|
'cr1.8xlarge' => 244000,
|
167
111
|
'hs1.8xlarge' => 117000,
|
168
112
|
'g2.2xlarge' => 15000,
|
113
|
+
'db.m1.small' => 1700, 'db.m1.medium' => 3750, 'db.m1.large' => 7500, 'db.m1.xlarge' => 15000,
|
114
|
+
'db.m2.xlarge' => 17100, 'db.m2.2xlarge' => 34200, 'db.m2.4xlarge' => 68400, 'db.m2.8xlarge' => 136800
|
169
115
|
}
|
170
116
|
@@Disk_Lookup = {
|
171
117
|
'm1.small' => 160, 'm1.medium' => 410, 'm1.large' =>850, 'm1.xlarge' => 1690,
|
@@ -180,6 +126,8 @@ module AwsPricing
|
|
180
126
|
'cr1.8xlarge' => 240,
|
181
127
|
'hs1.8xlarge' => 48000,
|
182
128
|
'g2.2xlarge' => 60,
|
129
|
+
'db.m1.small' => 160, 'db.m1.medium' => 410, 'db.m1.large' =>850, 'db.m1.xlarge' => 1690,
|
130
|
+
'db.m2.xlarge' => 420, 'db.m2.2xlarge' => 850, 'db.m2.4xlarge' => 1690, 'db.m2.8xlarge' => 0
|
183
131
|
}
|
184
132
|
@@Platform_Lookup = {
|
185
133
|
'm1.small' => 32, 'm1.medium' => 32, 'm1.large' => 64, 'm1.xlarge' => 64,
|
@@ -194,6 +142,8 @@ module AwsPricing
|
|
194
142
|
'cr1.8xlarge' => 64,
|
195
143
|
'hs1.8xlarge' => 64,
|
196
144
|
'g2.2xlarge' => 64,
|
145
|
+
'db.m1.small' => 32, 'db.m1.medium' => 32, 'db.m1.large' => 64, 'db.m1.xlarge' => 64,
|
146
|
+
'db.m2.xlarge' => 64, 'db.m2.2xlarge' => 64, 'db.m2.4xlarge' => 64, 'db.m2.8xlarge' => 64
|
197
147
|
}
|
198
148
|
@@Compute_Units_Lookup = {
|
199
149
|
'm1.small' => 1, 'm1.medium' => 2, 'm1.large' => 4, 'm1.xlarge' => 8,
|
@@ -208,6 +158,8 @@ module AwsPricing
|
|
208
158
|
'hs1.8xlarge' => 35,
|
209
159
|
'g2.2xlarge' => 26,
|
210
160
|
'unknown' => 0,
|
161
|
+
'db.m1.small' => 1, 'db.m1.medium' => 2, 'db.m1.large' => 4, 'db.m1.xlarge' => 8,
|
162
|
+
'db.m2.xlarge' => 6, 'db.m2.2xlarge' => 13, 'db.m2.4xlarge' => 26, 'db.m2.8xlarge' => 52
|
211
163
|
}
|
212
164
|
@@Virtual_Cores_Lookup = {
|
213
165
|
'm1.small' => 1, 'm1.medium' => 1, 'm1.large' => 2, 'm1.xlarge' => 4,
|
@@ -222,6 +174,24 @@ module AwsPricing
|
|
222
174
|
'hs1.8xlarge' => 16,
|
223
175
|
'g2.2xlarge' => 8,
|
224
176
|
'unknown' => 0,
|
177
|
+
'db.m1.small' => 1, 'db.m1.medium' => 1, 'db.m1.large' => 2, 'db.m1.xlarge' => 4,
|
178
|
+
'db.m2.xlarge' => 2, 'db.m2.2xlarge' => 4, 'db.m2.4xlarge' => 8, 'db.m2.8xlarge' => 16
|
179
|
+
}
|
180
|
+
@@Disk_Type_Lookup = {
|
181
|
+
'm1.small' => :ephemeral, 'm1.medium' => :ephemeral, 'm1.large' => :ephemeral, 'm1.xlarge' => :ephemeral,
|
182
|
+
'm2.xlarge' => :ephemeral, 'm2.2xlarge' => :ephemeral, 'm2.4xlarge' => :ephemeral,
|
183
|
+
'm3.xlarge' => :ephemeral, 'm3.2xlarge' => :ephemeral,
|
184
|
+
'c1.medium' => :ephemeral, 'c1.xlarge' => :ephemeral,
|
185
|
+
'hi1.4xlarge' => :ssd,
|
186
|
+
'cg1.4xlarge' => :ephemeral,
|
187
|
+
'cc1.4xlarge' => :ephemeral, 'cc2.8xlarge' => :ephemeral,
|
188
|
+
't1.micro' => :ebs,
|
189
|
+
'cr1.8xlarge' => :ssd,
|
190
|
+
'hs1.8xlarge' => :ephemeral,
|
191
|
+
'g2.2xlarge' => :ssd,
|
192
|
+
'unknown' => :ephemeral,
|
193
|
+
'db.m1.small' => :ephemeral, 'db.m1.medium' => :ephemeral, 'db.m1.large' => :ephemeral, 'db.m1.xlarge' => :ephemeral,
|
194
|
+
'db.m2.xlarge' => :ephemeral, 'db.m2.2xlarge' => :ephemeral, 'db.m2.4xlarge' => :ephemeral, 'db.m2.8xlarge' => :ephemeral
|
225
195
|
}
|
226
196
|
end
|
227
197
|
|
@@ -8,149 +8,6 @@
|
|
8
8
|
# Home:: http://github.com/CloudHealth/amazon-pricing
|
9
9
|
#++
|
10
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
|
-
# Returns whether an instance_type is available.
|
25
|
-
# type_of_instance = :ondemand, :light, :medium, :heavy
|
26
|
-
def available?(type_of_instance = :ondemand)
|
27
|
-
not price_per_hour(type_of_instance).nil?
|
28
|
-
end
|
29
|
-
|
30
|
-
# type_of_instance = :ondemand, :light, :medium, :heavy
|
31
|
-
# term = :year_1, :year_3, nil
|
32
|
-
def prepay(type_of_instance = :ondemand, term = nil)
|
33
|
-
case type_of_instance
|
34
|
-
when :ondemand
|
35
|
-
0
|
36
|
-
when :light
|
37
|
-
if term == :year1
|
38
|
-
@light_prepay_1_year
|
39
|
-
elsif term == :year3
|
40
|
-
@light_prepay_3_year
|
41
|
-
end
|
42
|
-
when :medium
|
43
|
-
if term == :year1
|
44
|
-
@medium_prepay_1_year
|
45
|
-
elsif term == :year3
|
46
|
-
@medium_prepay_3_year
|
47
|
-
end
|
48
|
-
when :heavy
|
49
|
-
if term == :year1
|
50
|
-
@heavy_prepay_1_year
|
51
|
-
elsif term == :year3
|
52
|
-
@heavy_prepay_3_year
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# type_of_instance = :ondemand, :light, :medium, :heavy
|
58
|
-
# term = :year_1, :year_3, nil
|
59
|
-
def set_prepay(type_of_instance, term, price)
|
60
|
-
case type_of_instance
|
61
|
-
when :light
|
62
|
-
if term == :year1
|
63
|
-
@light_prepay_1_year = price
|
64
|
-
elsif term == :year3
|
65
|
-
@light_prepay_3_year = price
|
66
|
-
end
|
67
|
-
when :medium
|
68
|
-
if term == :year1
|
69
|
-
@medium_prepay_1_year = price
|
70
|
-
elsif term == :year3
|
71
|
-
@medium_prepay_3_year = price
|
72
|
-
end
|
73
|
-
when :heavy
|
74
|
-
if term == :year1
|
75
|
-
@heavy_prepay_1_year = price
|
76
|
-
elsif term == :year3
|
77
|
-
@heavy_prepay_3_year = price
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# type_of_instance = :ondemand, :light, :medium, :heavy
|
83
|
-
# term = :year_1, :year_3, nil
|
84
|
-
def price_per_hour(type_of_instance = :ondemand, term = nil)
|
85
|
-
case type_of_instance
|
86
|
-
when :ondemand
|
87
|
-
@ondemand_price_per_hour
|
88
|
-
when :light
|
89
|
-
if term == :year1
|
90
|
-
@light_price_per_hour_1_year
|
91
|
-
elsif term == :year3
|
92
|
-
@light_price_per_hour_3_year
|
93
|
-
end
|
94
|
-
when :medium
|
95
|
-
if term == :year1
|
96
|
-
@medium_price_per_hour_1_year
|
97
|
-
elsif term == :year3
|
98
|
-
@medium_price_per_hour_3_year
|
99
|
-
end
|
100
|
-
when :heavy
|
101
|
-
if term == :year1
|
102
|
-
@heavy_price_per_hour_1_year
|
103
|
-
elsif term == :year3
|
104
|
-
@heavy_price_per_hour_3_year
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# type_of_instance = :ondemand, :light, :medium, :heavy
|
110
|
-
# term = :year_1, :year_3, nil
|
111
|
-
def set_price_per_hour(type_of_instance, term, price_per_hour)
|
112
|
-
case type_of_instance
|
113
|
-
when :ondemand
|
114
|
-
@ondemand_price_per_hour = price_per_hour
|
115
|
-
when :light
|
116
|
-
if term == :year1
|
117
|
-
@light_price_per_hour_1_year = price_per_hour
|
118
|
-
elsif term == :year3
|
119
|
-
@light_price_per_hour_3_year = price_per_hour
|
120
|
-
end
|
121
|
-
when :medium
|
122
|
-
if term == :year1
|
123
|
-
@medium_price_per_hour_1_year = price_per_hour
|
124
|
-
elsif term == :year3
|
125
|
-
@medium_price_per_hour_3_year = price_per_hour
|
126
|
-
end
|
127
|
-
when :heavy
|
128
|
-
if term == :year1
|
129
|
-
@heavy_price_per_hour_1_year = price_per_hour
|
130
|
-
elsif term == :year3
|
131
|
-
@heavy_price_per_hour_3_year = price_per_hour
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# type_of_instance = :ondemand, :light, :medium, :heavy
|
137
|
-
# term = :year_1, :year_3, nil
|
138
|
-
def get_breakeven_month(type_of_instance, term)
|
139
|
-
# Some regions and types do not have reserved available
|
140
|
-
ondemand_pph = price_per_hour(:ondemand)
|
141
|
-
reserved_pph = price_per_hour(type_of_instance, term)
|
142
|
-
return nil if ondemand_pph.nil? || reserved_pph.nil?
|
143
|
-
|
144
|
-
on_demand = 0
|
145
|
-
reserved = prepay(type_of_instance, term)
|
146
|
-
for i in 1..36 do
|
147
|
-
on_demand += ondemand_pph * 24 * 30.4
|
148
|
-
reserved += reserved_pph * 24 * 30.4
|
149
|
-
return i if reserved < on_demand
|
150
|
-
end
|
151
|
-
nil
|
152
|
-
end
|
153
|
-
|
11
|
+
class OperatingSystem < CategoryType
|
154
12
|
end
|
155
|
-
|
156
13
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'amazon-pricing/instance-type'
|
2
|
+
module AwsPricing
|
3
|
+
class RdsInstanceType < InstanceType
|
4
|
+
# database_type = :mysql, :oracle, :sqlserver
|
5
|
+
# type_of_instance = :ondemand, :light, :medium, :heavy
|
6
|
+
def update_pricing(database_type, type_of_instance, json, isMultiAz)
|
7
|
+
db = get_category_type(database_type, isMultiAz)
|
8
|
+
if db.nil?
|
9
|
+
db = DatabaseType.new(self, database_type)
|
10
|
+
if isMultiAz
|
11
|
+
@category_types["#{database_type}_multiAz"] = db
|
12
|
+
else
|
13
|
+
@category_types[database_type] = db
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if type_of_instance == :ondemand
|
18
|
+
values = RdsInstanceType::get_values(json, database_type)
|
19
|
+
price = coerce_price(values[database_type.to_s])
|
20
|
+
db.set_price_per_hour(type_of_instance, nil, price)
|
21
|
+
else
|
22
|
+
json['valueColumns'].each do |val|
|
23
|
+
price = coerce_price(val['prices']['USD'])
|
24
|
+
|
25
|
+
case val["name"]
|
26
|
+
when "yrTerm1"
|
27
|
+
db.set_prepay(type_of_instance, :year1, price)
|
28
|
+
when "yrTerm3"
|
29
|
+
db.set_prepay(type_of_instance, :year3, price)
|
30
|
+
when "yrTerm1Hourly"
|
31
|
+
db.set_price_per_hour(type_of_instance, :year1, price)
|
32
|
+
when "yrTerm3Hourly"
|
33
|
+
db.set_price_per_hour(type_of_instance, :year3, price)
|
34
|
+
when "yearTerm1Hourly"
|
35
|
+
db.set_price_per_hour(type_of_instance, :year1, price)
|
36
|
+
when "yearTerm3Hourly"
|
37
|
+
db.set_price_per_hour(type_of_instance, :year3, price)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
# Returns [api_name, name]
|
46
|
+
def self.get_name(instance_type, size, is_reserved = false)
|
47
|
+
lookup = @@Api_Name_Lookup
|
48
|
+
lookup = @@Api_Name_Lookup_Reserved if is_reserved
|
49
|
+
|
50
|
+
# Let's handle new instances more gracefully
|
51
|
+
unless lookup.has_key? instance_type
|
52
|
+
raise UnknownTypeError, "Unknown instance type #{instance_type}", caller
|
53
|
+
else
|
54
|
+
api_name = lookup[instance_type][size]
|
55
|
+
end
|
56
|
+
|
57
|
+
lookup = @@Name_Lookup
|
58
|
+
lookup = @@Name_Lookup_Reserved if is_reserved
|
59
|
+
name = lookup[instance_type][size]
|
60
|
+
|
61
|
+
[api_name, name]
|
62
|
+
end
|
63
|
+
|
64
|
+
@@Api_Name_Lookup = {
|
65
|
+
'udbInstClass' => {'uDBInst'=>'db.t1.micro'},
|
66
|
+
'dbInstClass'=> {'uDBInst' => 'db.t1.micro', 'smDBInst' => 'db.m1.small', 'medDBInst' => 'db.m1.medium', 'lgDBInst' => 'db.m1.large', 'xlDBInst' => 'db.m1.xlarge'},
|
67
|
+
'hiMemDBInstClass'=> {'xlDBInst' => 'db.m2.xlarge', 'xxlDBInst' => 'db.m2.2xlarge', 'xxxxDBInst' => 'db.m2.4xlarge'},
|
68
|
+
'clusterHiMemDB' => {'xxxxxxxxl' => 'db.m2.8xlarge'},
|
69
|
+
'multiAZDBInstClass'=> {'uDBInst' => 'db.t1.micro', 'smDBInst' => 'db.m1.small', 'medDBInst' => 'db.m1.medium', 'lgDBInst' => 'db.m1.large', 'xlDBInst' => 'db.m1.xlarge'},
|
70
|
+
'multiAZHiMemInstClass'=> {'xlDBInst' => 'db.m2.xlarge', 'xxlDBInst' => 'db.m2.2xlarge', 'xxxxDBInst' => 'db.m2.4xlarge'},
|
71
|
+
}
|
72
|
+
@@Name_Lookup = {
|
73
|
+
'udbInstClass' => {'uDBInst'=>'Standard Micro'},
|
74
|
+
'dbInstClass'=> {'uDBInst' => 'Standard Micro', 'smDBInst' => 'Standard Small', 'medDBInst' => 'Standard Medium', 'lgDBInst' => 'Standard Large', 'xlDBInst' => 'Standard Extra Large'},
|
75
|
+
'hiMemDBInstClass'=> {'xlDBInst' => 'Standard High-Memory Extra Large', 'xxlDBInst' => 'Standard High-Memory Double Extra Large', 'xxxxDBInst' => 'Standard High-Memory Quadruple Extra Large'},
|
76
|
+
'clusterHiMemDB' => {'xxxxxxxxl' => 'Standard High-Memory Cluster Eight Extra Large'},
|
77
|
+
'multiAZDBInstClass'=> {'uDBInst' => 'Multi-AZ Micro', 'smDBInst' => 'Multi-AZ Small', 'medDBInst' => 'Multi-AZ Medium', 'lgDBInst' => 'Multi-AZ Large', 'xlDBInst' => 'Multi-AZ Extra Large'},
|
78
|
+
'multiAZHiMemInstClass'=> {'xlDBInst' => 'Multi-AZ High-Memory Extra Large', 'xxlDBInst' => 'Multi-AZ High-Memory Double Extra Large', 'xxxxDBInst' => 'Multi-AZ High-Memory Quadruple Extra Large'},
|
79
|
+
}
|
80
|
+
@@Api_Name_Lookup_Reserved = {
|
81
|
+
'stdDeployRes' => {'u' => 'db.t1.micro', 'micro' => 'db.t1.micro', 'sm' => 'db.m1.small', 'med' => 'db.m1.medium', 'lg' => 'db.m1.large', 'xl' => 'db.m1.xlarge', 'xlHiMem' => 'db.m2.xlarge', 'xxlHiMem' => 'db.m2.2xlarge', 'xxxxlHiMem' => 'db.m2.4xlarge', 'xxxxxxxxl' => 'db.m2.8xlarge'},
|
82
|
+
'multiAZdeployRes' => {'u' => 'db.t1.micro', 'micro' => 'db.t1.micro', 'sm' => 'db.m1.small', 'med' => 'db.m1.medium', 'lg' => 'db.m1.large', 'xl' => 'db.m1.xlarge', 'xlHiMem' => 'db.m2.xlarge', 'xxlHiMem' => 'db.m2.2xlarge', 'xxxxlHiMem' => 'db.m2.4xlarge', 'xxxxxxxxl' => 'db.m2.8xlarge'},
|
83
|
+
}
|
84
|
+
@@Name_Lookup_Reserved = {
|
85
|
+
'stdDeployRes' => {'u' => 'Standard Micro', 'micro' => 'Standard Micro', 'sm' => 'Standard Small', 'med' => 'Standard Medium', 'lg' => 'Standard Large', 'xl' => 'Standard Extra Large', 'xlHiMem' => 'Standard Extra Large High-Memory', 'xxlHiMem' => 'Standard Double Extra Large High-Memory', 'xxxxlHiMem' => 'Standard Quadruple Extra Large High-Memory', 'xxxxxxxxl' => 'Standard Eight Extra Large'} ,
|
86
|
+
'multiAZdeployRes' => {'u' => 'Multi-AZ Micro', 'micro' => 'Multi-AZ Micro', 'sm' => 'Multi-AZ Small', 'med' => 'Multi-AZ Medium', 'lg' => 'Multi-AZ Large', 'xl' => 'Multi-AZ Extra Large', 'xlHiMem' => 'Multi-AZ Extra Large High-Memory', 'xxlHiMem' => 'Multi-AZ Double Extra Large High-Memory', 'xxxxlHiMem' => 'Multi-AZ Quadruple Extra Large High-Memory', 'xxxxxxxxl' => 'Multi-AZ Eight Extra Large'},
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
@@ -19,35 +19,59 @@ module AwsPricing
|
|
19
19
|
|
20
20
|
def initialize(name)
|
21
21
|
@name = name
|
22
|
-
@
|
22
|
+
@ec2_instance_types = {}
|
23
|
+
@rds_instance_types = {}
|
23
24
|
end
|
24
25
|
|
25
|
-
def
|
26
|
-
@
|
26
|
+
def ec2_instance_types
|
27
|
+
@ec2_instance_types.values
|
28
|
+
end
|
29
|
+
|
30
|
+
def rds_instance_types
|
31
|
+
@rds_instance_types.values
|
27
32
|
end
|
28
33
|
|
29
34
|
# Returns whether an instance_type is available.
|
30
35
|
# operating_system = :linux, :mswin, :rhel, :sles, :mswinSQL, :mswinSQLWeb
|
31
36
|
# type_of_instance = :ondemand, :light, :medium, :heavy
|
32
37
|
def instance_type_available?(api_name, type_of_instance = :ondemand, operating_system = :linux)
|
33
|
-
instance = @
|
38
|
+
instance = @ec2_instance_types[api_name]
|
34
39
|
return false if instance.nil?
|
35
40
|
instance.available?(type_of_instance, operating_system)
|
36
41
|
end
|
37
42
|
|
38
43
|
# type_of_instance = :ondemand, :light, :medium, :heavy
|
39
|
-
def
|
40
|
-
current =
|
44
|
+
def add_or_update_ec2_instance_type(api_name, name, operating_system, type_of_instance, json)
|
45
|
+
current = get_ec2_instance_type(api_name)
|
41
46
|
if current.nil?
|
42
|
-
current =
|
43
|
-
@
|
47
|
+
current = Ec2InstanceType.new(self, api_name, name)
|
48
|
+
@ec2_instance_types[api_name] = current
|
44
49
|
end
|
45
50
|
current.update_pricing(operating_system, type_of_instance, json)
|
46
51
|
current
|
47
52
|
end
|
48
53
|
|
54
|
+
def add_or_update_rds_instance_type(api_name, name, db_type, type_of_instance, json, isMultiAz)
|
55
|
+
current = get_rds_instance_type(api_name)
|
56
|
+
if current.nil?
|
57
|
+
current = RdsInstanceType.new(self, api_name, name)
|
58
|
+
@rds_instance_types[api_name] = current
|
59
|
+
end
|
60
|
+
current.update_pricing(db_type, type_of_instance, json, isMultiAz)
|
61
|
+
current
|
62
|
+
end
|
63
|
+
|
64
|
+
# Maintained for backward compatibility reasons (retrieves EC2 instance type)
|
49
65
|
def get_instance_type(api_name)
|
50
|
-
|
66
|
+
get_ec2_instance_type(api_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_ec2_instance_type(api_name)
|
70
|
+
@ec2_instance_types[api_name]
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_rds_instance_type(api_name)
|
74
|
+
@rds_instance_types[api_name]
|
51
75
|
end
|
52
76
|
|
53
77
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'amazon-pricing'
|
2
|
+
|
3
|
+
describe AwsPricing::RdsPriceList do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@region_name = ['us-east', 'us-west', 'us-west-2', 'eu-ireland', 'apac-sin', 'apac-syd', 'apac-tokyo', 'sa-east-1']
|
7
|
+
@db_types = [:mysql, :oracle, :oracle_byol, :sqlserver, :sqlserver_express, :sqlserver_web, :sqlserver_byol]
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'new' do
|
11
|
+
it 'RdsPriceList.new should return the valid response' do
|
12
|
+
result = AwsPricing::RdsPriceList.new
|
13
|
+
result.regions.each do |region|
|
14
|
+
# response should have valid region
|
15
|
+
expect(@region_name).to include(region.name)
|
16
|
+
# Result have valid db name
|
17
|
+
expect(@db_types).to include(region.rds_instance_types.first.category_types.first.name)
|
18
|
+
# values should not be nil
|
19
|
+
region.rds_instance_types.first.category_types.first.ondemand_price_per_hour.should_not be_nil
|
20
|
+
region.rds_instance_types.first.category_types.first.light_price_per_hour_1_year.should_not be_nil
|
21
|
+
region.rds_instance_types.first.category_types.first.medium_price_per_hour_1_year.should_not be_nil
|
22
|
+
region.rds_instance_types.first.category_types.first.heavy_price_per_hour_1_year.should_not be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '::get_api_name' do
|
28
|
+
it "raises an UnknownTypeError on an unexpected instance type" do
|
29
|
+
expect {
|
30
|
+
AwsPricing::InstanceType::get_name 'QuantumODI', 'huge'
|
31
|
+
}.to raise_error(AwsPricing::UnknownTypeError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -16,17 +16,17 @@ require 'test/unit'
|
|
16
16
|
|
17
17
|
class TestEc2InstanceTypes < Test::Unit::TestCase
|
18
18
|
def test_cc8xlarge_issue
|
19
|
-
pricing = AwsPricing::
|
19
|
+
pricing = AwsPricing::Ec2PriceList.new
|
20
20
|
obj = pricing.get_instance_type('us-east', 'cc2.8xlarge')
|
21
21
|
assert obj.api_name == 'cc2.8xlarge'
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_name_lookup
|
25
|
-
pricing = AwsPricing::
|
25
|
+
pricing = AwsPricing::Ec2PriceList.new
|
26
26
|
pricing.regions.each do |region|
|
27
27
|
assert_not_nil region.name
|
28
28
|
|
29
|
-
region.
|
29
|
+
region.ec2_instance_types.each do |instance|
|
30
30
|
assert_not_nil(instance.api_name)
|
31
31
|
assert_not_nil(instance.name)
|
32
32
|
end
|
@@ -43,15 +43,15 @@ class TestEc2InstanceTypes < Test::Unit::TestCase
|
|
43
43
|
|
44
44
|
def test_available
|
45
45
|
# Validate instance types in specific regions are available
|
46
|
-
pricing = AwsPricing::
|
46
|
+
pricing = AwsPricing::Ec2PriceList.new
|
47
47
|
region = pricing.get_region('us-east')
|
48
48
|
assert region.instance_type_available?('m1.large')
|
49
49
|
end
|
50
50
|
|
51
51
|
def test_fetch_all_breakeven_months
|
52
|
-
pricing = AwsPricing::
|
52
|
+
pricing = AwsPricing::Ec2PriceList.new
|
53
53
|
pricing.regions.each do |region|
|
54
|
-
region.
|
54
|
+
region.ec2_instance_types.each do |instance|
|
55
55
|
instance.operating_systems.each do |os|
|
56
56
|
[:light, :medium, :heavy].each do |res_type|
|
57
57
|
next if not instance.available?(res_type)
|
@@ -64,30 +64,30 @@ class TestEc2InstanceTypes < Test::Unit::TestCase
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def test_breakeven_month
|
67
|
-
pricing = AwsPricing::
|
67
|
+
pricing = AwsPricing::Ec2PriceList.new
|
68
68
|
region = pricing.get_region('us-east')
|
69
|
-
instance = region.
|
69
|
+
instance = region.get_ec2_instance_type('m1.large')
|
70
70
|
bem = instance.get_breakeven_month(:linux, :heavy, :year1)
|
71
71
|
assert bem == 6
|
72
72
|
end
|
73
73
|
|
74
74
|
def test_memory
|
75
75
|
# Validate instance types in specific regions are available
|
76
|
-
pricing = AwsPricing::
|
76
|
+
pricing = AwsPricing::Ec2PriceList.new
|
77
77
|
region = pricing.get_region('us-east')
|
78
|
-
instance = region.
|
78
|
+
instance = region.get_ec2_instance_type('m1.large')
|
79
79
|
assert instance.memory_in_mb == 7500
|
80
80
|
end
|
81
81
|
|
82
82
|
def test_non_standard_region_name
|
83
|
-
pricing = AwsPricing::
|
83
|
+
pricing = AwsPricing::Ec2PriceList.new
|
84
84
|
region = pricing.get_region('eu-west-1')
|
85
|
-
instance = region.
|
85
|
+
instance = region.get_ec2_instance_type('m1.large')
|
86
86
|
assert instance.memory_in_mb == 7500
|
87
87
|
end
|
88
88
|
|
89
89
|
def test_ebs
|
90
|
-
pricing = AwsPricing::
|
90
|
+
pricing = AwsPricing::Ec2PriceList.new
|
91
91
|
region = pricing.get_region('us-east')
|
92
92
|
assert region.ebs_price.standard_per_gb == 0.10
|
93
93
|
assert region.ebs_price.standard_per_million_io == 0.10
|
@@ -97,7 +97,7 @@ class TestEc2InstanceTypes < Test::Unit::TestCase
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def test_ebs_not_null
|
100
|
-
pricing = AwsPricing::
|
100
|
+
pricing = AwsPricing::Ec2PriceList.new
|
101
101
|
pricing.regions.each do |region|
|
102
102
|
# Everyone should have standard pricing
|
103
103
|
assert_not_nil region.ebs_price.standard_per_gb
|
@@ -107,9 +107,9 @@ class TestEc2InstanceTypes < Test::Unit::TestCase
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def test_virtual_cores
|
110
|
-
pricing = AwsPricing::
|
110
|
+
pricing = AwsPricing::Ec2PriceList.new
|
111
111
|
region = pricing.get_region('us-east')
|
112
|
-
instance = region.
|
112
|
+
instance = region.get_ec2_instance_type('m1.large')
|
113
113
|
assert instance.virtual_cores == 2
|
114
114
|
end
|
115
115
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amazon-pricing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -27,13 +27,18 @@ files:
|
|
27
27
|
- Rakefile
|
28
28
|
- amazon-pricing.gemspec
|
29
29
|
- lib/amazon-pricing.rb
|
30
|
+
- lib/amazon-pricing/category-type.rb
|
31
|
+
- lib/amazon-pricing/database-type.rb
|
30
32
|
- lib/amazon-pricing/ebs-price.rb
|
33
|
+
- lib/amazon-pricing/ec2-instance-type.rb
|
31
34
|
- lib/amazon-pricing/instance-type.rb
|
32
35
|
- lib/amazon-pricing/operating-system.rb
|
36
|
+
- lib/amazon-pricing/rds-instance-type.rb
|
33
37
|
- lib/amazon-pricing/region.rb
|
34
38
|
- lib/amazon-pricing/version.rb
|
35
39
|
- spec/instance_type_spec.rb
|
36
40
|
- spec/price_list_spec.rb
|
41
|
+
- spec/rds-amazon-pricing-spec.rb
|
37
42
|
- test/helper.rb
|
38
43
|
- test/test-ec2-instance-types.rb
|
39
44
|
homepage: http://github.com/CloudHealth/amazon-pricing
|
@@ -69,6 +74,7 @@ summary: Amazon Web Services Pricing Ruby gem
|
|
69
74
|
test_files:
|
70
75
|
- spec/instance_type_spec.rb
|
71
76
|
- spec/price_list_spec.rb
|
77
|
+
- spec/rds-amazon-pricing-spec.rb
|
72
78
|
- test/helper.rb
|
73
79
|
- test/test-ec2-instance-types.rb
|
74
80
|
has_rdoc:
|