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 +24 -8
- data/lib/amazon-pricing.rb +12 -30
- data/lib/amazon-pricing/instance-type.rb +106 -60
- data/lib/amazon-pricing/operating-system.rb +150 -0
- data/lib/amazon-pricing/region.rb +22 -64
- data/lib/amazon-pricing/version.rb +1 -1
- data/spec/instance_type_spec.rb +1 -1
- data/spec/price_list_spec.rb +4 -0
- data/test/test-ec2-instance-types.rb +17 -24
- metadata +18 -17
- data/lib/amazon-pricing/reserved-instance-type.rb +0 -131
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-#{
|
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 "
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
44
|
-
|
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
|
data/lib/amazon-pricing.rb
CHANGED
@@ -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
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
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, :
|
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,
|
30
|
-
|
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
|
-
@
|
57
|
-
@name =
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
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
|
118
|
+
unless lookup.has_key? instance_type
|
100
119
|
raise UnknownTypeError, "Unknown instance type #{instance_type}", caller
|
101
120
|
else
|
102
|
-
|
121
|
+
api_name = lookup[instance_type][size]
|
103
122
|
end
|
104
|
-
end
|
105
123
|
|
106
|
-
|
107
|
-
@@
|
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
|
-
@
|
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
|
29
|
-
@
|
25
|
+
def instance_types
|
26
|
+
@instance_types.values
|
30
27
|
end
|
31
28
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
#
|
54
|
-
|
55
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
76
|
-
|
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
|
data/spec/instance_type_spec.rb
CHANGED
@@ -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::
|
7
|
+
AwsPricing::InstanceType::get_name 'QuantumODI', 'huge'
|
8
8
|
}.to raise_error(AwsPricing::UnknownTypeError)
|
9
9
|
end
|
10
10
|
end
|
data/spec/price_list_spec.rb
CHANGED
@@ -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',
|
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
|
-
|
29
|
-
|
30
|
-
assert_not_nil
|
31
|
-
|
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?(
|
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(
|
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(
|
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(
|
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
|
-
|
5
|
-
|
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:
|
56
|
-
|
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:
|
61
|
+
version: !binary |-
|
62
|
+
MA==
|
63
|
+
none: false
|
62
64
|
requirements: []
|
63
65
|
rubyforge_project: amazon-pricing
|
64
|
-
rubygems_version: 1.8.
|
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
|