aws-rikanjo 0.0.8 → 0.0.9
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.
- checksums.yaml +4 -4
- data/.hound.yml +6 -0
- data/.travis.yml +8 -0
- data/Gemfile +0 -6
- data/README.md +18 -15
- data/aws-rikanjo.gemspec +12 -9
- data/bin/rikanjo +3 -75
- data/lib/aws/rikanjo.rb +6 -126
- data/lib/aws/rikanjo/base.rb +40 -0
- data/lib/aws/rikanjo/calc.rb +103 -0
- data/lib/aws/rikanjo/cli.rb +113 -0
- data/lib/aws/rikanjo/ec2.rb +117 -0
- data/lib/aws/rikanjo/rds.rb +120 -0
- data/lib/aws/rikanjo/version.rb +1 -1
- data/spec/lib/rikanjo_ec2_spec.rb +12 -12
- data/spec/lib/rikanjo_rds_spec.rb +22 -12
- metadata +60 -13
- data/lib/aws/rikanjo/mode/ec2.rb +0 -120
- data/lib/aws/rikanjo/mode/rds.rb +0 -117
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'aws/rikanjo/base'
|
3
|
+
|
4
|
+
module Aws
|
5
|
+
module RiKanjoo
|
6
|
+
class CLI
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@options = {}
|
10
|
+
@optparse = nil
|
11
|
+
@cost = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
parse
|
16
|
+
rikanjo
|
17
|
+
output
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse
|
21
|
+
optparse = OptionParser.new do |opts|
|
22
|
+
opts.banner = 'Usage: rikanjo ec2/rds [options]'
|
23
|
+
|
24
|
+
# subcommand
|
25
|
+
mode = ARGV.shift
|
26
|
+
if mode == 'ec2'
|
27
|
+
elsif mode == 'rds'
|
28
|
+
opts.on('--multiaz', 'enable multi-az') do |value|
|
29
|
+
@options[:multiaz] = value
|
30
|
+
end
|
31
|
+
else
|
32
|
+
$stderr.puts "no such subcommand: #{mode}"
|
33
|
+
puts opts
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
@options[:mode] = mode
|
37
|
+
|
38
|
+
region_values = %w(us-east-1 us-west-1 us-west-2 eu-west-1 ap-southeast-1 ap-northeast-1 ap-southeast-2 sa-east-1 )
|
39
|
+
opts.on('-r', '--region=VALUE', region_values, "specify aws-region (#{region_values.join('/')})") do |value|
|
40
|
+
@options[:region] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-t', '--instance_type=VALUE', 'specify ec2-instance-type') do |value|
|
44
|
+
@options[:instance_type] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
ri_util_values = %w(light medium heavy)
|
48
|
+
opts.on('-u', '--ri_util=VALUE', ri_util_values, "specify ri-util (#{ri_util_values.join('/')})") do |value|
|
49
|
+
@options[:ri_util] = value
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on('-j', '--output_json', "specify output_json flg") do |value|
|
53
|
+
@options[:output_json] = value
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on('-h', '--help') do
|
57
|
+
puts opts
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# validation
|
63
|
+
begin
|
64
|
+
optparse.parse!
|
65
|
+
require_args = [:region, :instance_type, :ri_util]
|
66
|
+
error_args = require_args.select { |param| @options[param].nil? }
|
67
|
+
unless error_args.empty?
|
68
|
+
puts "require arguments: #{error_args.join(', ')}"
|
69
|
+
puts
|
70
|
+
puts optparse
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
rescue
|
74
|
+
puts $!.to_s
|
75
|
+
puts
|
76
|
+
puts optparse
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def rikanjo
|
82
|
+
# rikanjo
|
83
|
+
a = Aws::RiKanjoo::Base.new(
|
84
|
+
mode = @options[:mode],
|
85
|
+
region = @options[:region],
|
86
|
+
instance_type = @options[:instance_type],
|
87
|
+
ri_util = @options[:ri_util],
|
88
|
+
multiaz = @options[:multiaz],
|
89
|
+
)
|
90
|
+
@cost = a.total_cost_year
|
91
|
+
end
|
92
|
+
|
93
|
+
def output
|
94
|
+
if @options[:output_json]
|
95
|
+
output_json()
|
96
|
+
else
|
97
|
+
output_text()
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def output_json
|
102
|
+
puts @cost.to_json
|
103
|
+
end
|
104
|
+
|
105
|
+
def output_text
|
106
|
+
@cost.keys.each do |key|
|
107
|
+
puts "#{key}: #{@cost[key]}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'yajl'
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module RiKanjoo
|
5
|
+
class Ec2
|
6
|
+
attr_reader :region, :instance_type, :ri_util
|
7
|
+
|
8
|
+
def initialize(region, instance_type, ri_util)
|
9
|
+
@region = region
|
10
|
+
@instance_type = instance_type
|
11
|
+
@ri_util = ri_util
|
12
|
+
@os = 'linux'
|
13
|
+
@current_price_url = 'http://a0.awsstatic.com/pricing/1/ec2'
|
14
|
+
@previous_price_url = 'http://a0.awsstatic.com/pricing/1/ec2/previous-generation'
|
15
|
+
end
|
16
|
+
|
17
|
+
def om_price_from_contents(contents)
|
18
|
+
region = convert_region(@region)
|
19
|
+
|
20
|
+
# parse
|
21
|
+
json = parse_contents(contents)
|
22
|
+
|
23
|
+
# select 'region' and 'instance_type'
|
24
|
+
json['config']['regions'].each do |r|
|
25
|
+
# r = {"region"=>"us-east", "instanceTypes"=>[{"type"=>"generalCurrentGen", ...
|
26
|
+
next unless r['region'] == region
|
27
|
+
r['instanceTypes'].each do |type|
|
28
|
+
# type = {"type"=>"generalCurrentGen", "sizes"=>[{"size"=>"m3.medium", "vCPU"=>"1" ...
|
29
|
+
type['sizes'].each do |i|
|
30
|
+
next unless i['size'] == @instance_type
|
31
|
+
|
32
|
+
return { hr_price: i['valueColumns'][0]['prices']['USD'] }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
abort("[ec2][#{region}][#{@instance_type}] Not found om-price?")
|
38
|
+
end
|
39
|
+
|
40
|
+
def ri_price_from_contents(contents)
|
41
|
+
region = clean_region(@region)
|
42
|
+
|
43
|
+
# parse
|
44
|
+
json = parse_contents(contents)
|
45
|
+
|
46
|
+
ri_info = nil
|
47
|
+
|
48
|
+
json['config']['regions'].each do |r|
|
49
|
+
next unless r['region'] == region
|
50
|
+
|
51
|
+
r['instanceTypes'].each do |type|
|
52
|
+
type['sizes'].each do |i|
|
53
|
+
next unless i['size'] == @instance_type
|
54
|
+
|
55
|
+
ri_info = ri_info ||= {}
|
56
|
+
i['valueColumns'].each do |y|
|
57
|
+
case y['name']
|
58
|
+
when 'yrTerm1'
|
59
|
+
ri_info[@ri_util] = { upfront: y['prices']['USD'] }
|
60
|
+
when 'yrTerm1Hourly'
|
61
|
+
ri_info[@ri_util].store(:hr_price, y['prices']['USD'])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
abort("[ec2][#{region}][#{@instance_type}] Not found ri-price?") unless ri_info
|
69
|
+
|
70
|
+
return ri_info
|
71
|
+
end
|
72
|
+
|
73
|
+
def price_url
|
74
|
+
return (self.previous_generation_type) ? @previous_price_url : @current_price_url
|
75
|
+
end
|
76
|
+
|
77
|
+
def om_price_file
|
78
|
+
return "#{@os}-od.min.js"
|
79
|
+
end
|
80
|
+
|
81
|
+
def ri_price_file
|
82
|
+
return (self.previous_generation_type) ? "#{@ri_util}_#{@os}.min.js" : "#{@os}-ri-#{@ri_util}.min.js"
|
83
|
+
end
|
84
|
+
|
85
|
+
def previous_generation_type
|
86
|
+
case @instance_type
|
87
|
+
when /^(c1|m2|cc2\.8xlarge|cr1\.8xlarge|hi1\.4xlarge|cg1\.4xlarge)/ then true
|
88
|
+
when /^m1/ then (@instance_type == 'm1.small') ? false : true
|
89
|
+
else false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_contents(data)
|
94
|
+
data = data.gsub("/*\n * This file is intended for use only on aws.amazon.com. We do not guarantee its availability or accuracy.\n *\n * Copyright 2014 Amazon.com, Inc. or its affiliates. All rights reserved.\n */\ncallback({",'{').gsub("\);", '').gsub(/([a-zA-Z]+):/, '"\1":')
|
95
|
+
return Yajl::Parser.parse(data)
|
96
|
+
end
|
97
|
+
|
98
|
+
def convert_region(region)
|
99
|
+
# not same om region
|
100
|
+
case region
|
101
|
+
when 'us-east-1' then 'us-east'
|
102
|
+
when 'us-west-1' then 'us-west'
|
103
|
+
when 'us-west-2' then 'us-west-2'
|
104
|
+
when 'eu-west-1' then 'eu-ireland'
|
105
|
+
when 'ap-southeast-1' then 'apac-sin'
|
106
|
+
when 'ap-northeast-1' then 'apac-tokyo'
|
107
|
+
when 'ap-southeast-2' then 'apac-syd'
|
108
|
+
when 'sa-east-1' then 'sa-east-1'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def clean_region(region)
|
113
|
+
return region.gsub(/^us-east-1$/, 'us-east')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Aws
|
2
|
+
module RiKanjoo
|
3
|
+
class Rds
|
4
|
+
attr_reader :region, :instance_type, :ri_util
|
5
|
+
|
6
|
+
def initialize(region, instance_type, ri_util, multiaz)
|
7
|
+
@region = region
|
8
|
+
@instance_type = "db.#{instance_type}"
|
9
|
+
@ri_util = ri_util
|
10
|
+
@rdbms = 'mysql'
|
11
|
+
@current_price_url = "http://a0.awsstatic.com/pricing/1/rds/#{@rdbms}"
|
12
|
+
@previous_price_url = "http://a0.awsstatic.com/pricing/1/rds/#{@rdbms}/previous-generation"
|
13
|
+
@multiaz = multiaz
|
14
|
+
end
|
15
|
+
|
16
|
+
def om_price_from_contents(contents)
|
17
|
+
region = convert_region(@region)
|
18
|
+
|
19
|
+
# parse
|
20
|
+
json = parse_contents(contents)
|
21
|
+
|
22
|
+
# select 'region' and 'instance_type'
|
23
|
+
json['config']['regions'].each do |r|
|
24
|
+
# r = {"region"=>"us-east", "instanceTypes"=>[{"type"=>"generalCurrentGen", ...
|
25
|
+
next unless r['region'] == region
|
26
|
+
r['types'].each do |type|
|
27
|
+
# type = {"type"=>"generalCurrentGen", "sizes"=>[{"size"=>"m3.medium", "vCPU"=>"1" ...
|
28
|
+
type['tiers'].each do |i|
|
29
|
+
next unless i['name'] == @instance_type
|
30
|
+
|
31
|
+
return { hr_price: i['prices']['USD'] }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
abort("[rds][#{region}][#{@instance_type}] Not found om-price?")
|
37
|
+
end
|
38
|
+
|
39
|
+
def ri_price_from_contents(contents)
|
40
|
+
region = convert_region(@region)
|
41
|
+
mtype = (@multiaz) ? 'Multi-AZ' : 'Single-AZ'
|
42
|
+
|
43
|
+
# parse
|
44
|
+
json = parse_contents(contents)
|
45
|
+
|
46
|
+
ri_info = nil
|
47
|
+
|
48
|
+
json['config']['regions'].each do |r|
|
49
|
+
next unless r['region'] == region
|
50
|
+
|
51
|
+
r['instanceTypes'].each do |type|
|
52
|
+
next unless type['type'] =~ /#{mtype}/
|
53
|
+
|
54
|
+
type['tiers'].each do |i|
|
55
|
+
next unless i['size'] == @instance_type
|
56
|
+
|
57
|
+
ri_info = ri_info ||= {}
|
58
|
+
i['valueColumns'].each do |y|
|
59
|
+
# beauty
|
60
|
+
y['name'].gsub!(/^year/, 'yr')
|
61
|
+
y['prices']['USD'].gsub!(/,/, '')
|
62
|
+
|
63
|
+
case y['name']
|
64
|
+
when 'yrTerm1'
|
65
|
+
ri_info[@ri_util] = { upfront: y['prices']['USD'] }
|
66
|
+
when 'yrTerm1Hourly'
|
67
|
+
ri_info[@ri_util].store(:hr_price, y['prices']['USD'])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
abort("[rds][#{region}][#{@instance_type}] Not found ri-price?") unless ri_info
|
76
|
+
|
77
|
+
return ri_info
|
78
|
+
end
|
79
|
+
|
80
|
+
def price_url
|
81
|
+
return (self.previous_generation_type) ? @previous_price_url : @current_price_url
|
82
|
+
end
|
83
|
+
|
84
|
+
def om_price_file
|
85
|
+
return (@multiaz) ? "pricing-multiAZ-deployments.min.js" : "pricing-standard-deployments.min.js"
|
86
|
+
end
|
87
|
+
|
88
|
+
def ri_price_file
|
89
|
+
return "pricing-#{@ri_util}-utilization-reserved-instances.min.js"
|
90
|
+
end
|
91
|
+
|
92
|
+
def previous_generation_type
|
93
|
+
case @instance_type
|
94
|
+
when /^db.(c1|m2|cc2\.8xlarge|cr1\.8xlarge|hi1\.4xlarge|cg1\.4xlarge)/ then true
|
95
|
+
when /^db.m1/ then (@instance_type == 'db.m1.small') ? false : true
|
96
|
+
else false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def parse_contents(data)
|
101
|
+
data = data.gsub("/*\n * This file is intended for use only on aws.amazon.com. We do not guarantee its availability or accuracy.\n *\n * Copyright 2014 Amazon.com, Inc. or its affiliates. All rights reserved.\n */\ncallback({",'{').gsub("\);", '').gsub(/([a-zA-Z]+):/, '"\1":')
|
102
|
+
return Yajl::Parser.parse(data)
|
103
|
+
end
|
104
|
+
|
105
|
+
def convert_region(region)
|
106
|
+
# not same om region
|
107
|
+
case region
|
108
|
+
when 'us-east-1' then 'us-east'
|
109
|
+
when 'us-west-1' then 'us-west'
|
110
|
+
when 'us-west-2' then 'us-west-2'
|
111
|
+
when 'eu-west-1' then 'eu-ireland'
|
112
|
+
when 'ap-southeast-1' then 'apac-sin'
|
113
|
+
when 'ap-northeast-1' then 'apac-tokyo'
|
114
|
+
when 'ap-southeast-2' then 'apac-syd'
|
115
|
+
when 'sa-east-1' then 'sa-east-1'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/aws/rikanjo/version.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require "spec_helper"
|
2
|
-
require "aws/rikanjo/
|
2
|
+
require "aws/rikanjo/ec2"
|
3
3
|
|
4
4
|
include RikanjoSpecHelper
|
5
5
|
|
6
|
-
describe 'AWS::Rikanjo::
|
6
|
+
describe 'AWS::Rikanjo::Ec2' do
|
7
7
|
|
8
8
|
describe 'previous type' do
|
9
9
|
it "is previous instance type" do
|
10
10
|
%w{ m1.medium c1.medium m2.xlarge cc2.8xlarge cr1.8xlarge hi1.4xlarge cg1.4xlarge }.each do |t|
|
11
|
-
a = Aws::RiKanjoo::
|
11
|
+
a = Aws::RiKanjoo::Ec2.new(
|
12
12
|
region = 'ap-northeast-1',
|
13
13
|
instance_type = t,
|
14
14
|
ri_util = 'light',
|
@@ -19,7 +19,7 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
19
19
|
|
20
20
|
it "is current instance type" do
|
21
21
|
%w{ t1.micro m1.small m3.medium c3.medium r3.xlarge i2.xlarge }.each do |t|
|
22
|
-
a = Aws::RiKanjoo::
|
22
|
+
a = Aws::RiKanjoo::Ec2.new(
|
23
23
|
region = 'ap-northeast-1',
|
24
24
|
instance_type = t,
|
25
25
|
ri_util = 'light',
|
@@ -31,7 +31,7 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
31
31
|
|
32
32
|
describe 'url' do
|
33
33
|
it "build previous price url" do
|
34
|
-
a = Aws::RiKanjoo::
|
34
|
+
a = Aws::RiKanjoo::Ec2.new(
|
35
35
|
region = 'ap-northeast-1',
|
36
36
|
instance_type = 'm1.medium',
|
37
37
|
ri_util = 'light',
|
@@ -40,7 +40,7 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
it "build current price url" do
|
43
|
-
a = Aws::RiKanjoo::
|
43
|
+
a = Aws::RiKanjoo::Ec2.new(
|
44
44
|
region = 'ap-northeast-1',
|
45
45
|
instance_type = 'm3.medium',
|
46
46
|
ri_util = 'light',
|
@@ -49,7 +49,7 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
it "build previous ri price file" do
|
52
|
-
a = Aws::RiKanjoo::
|
52
|
+
a = Aws::RiKanjoo::Ec2.new(
|
53
53
|
region = 'ap-northeast-1',
|
54
54
|
instance_type = 'm1.medium',
|
55
55
|
ri_util = 'light',
|
@@ -58,7 +58,7 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
it "build current ri price file" do
|
61
|
-
a = Aws::RiKanjoo::
|
61
|
+
a = Aws::RiKanjoo::Ec2.new(
|
62
62
|
region = 'ap-northeast-1',
|
63
63
|
instance_type = 'm3.medium',
|
64
64
|
ri_util = 'heavy',
|
@@ -72,13 +72,13 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
72
72
|
|
73
73
|
before :all do
|
74
74
|
# rikanjo (current)
|
75
|
-
a1 = Aws::RiKanjoo::
|
75
|
+
a1 = Aws::RiKanjoo::Ec2.new(
|
76
76
|
region = 'ap-northeast-1',
|
77
77
|
instance_type = 'm3.medium',
|
78
78
|
ri_util = 'medium',
|
79
79
|
)
|
80
80
|
# rikanjo (previous)
|
81
|
-
a2 = Aws::RiKanjoo::
|
81
|
+
a2 = Aws::RiKanjoo::Ec2.new(
|
82
82
|
region = 'ap-northeast-1',
|
83
83
|
instance_type = 'm1.medium',
|
84
84
|
ri_util = 'medium',
|
@@ -91,7 +91,7 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
91
91
|
|
92
92
|
it "is able to get the price" do
|
93
93
|
regions.each do |region|
|
94
|
-
a = Aws::RiKanjoo::
|
94
|
+
a = Aws::RiKanjoo::Ec2.new(
|
95
95
|
region = region,
|
96
96
|
instance_type = 'm3.large',
|
97
97
|
ri_util = 'medium',
|
@@ -108,7 +108,7 @@ describe 'AWS::Rikanjo::Mode::Ec2' do
|
|
108
108
|
|
109
109
|
it "an exception is raised when there are no instance-type" do
|
110
110
|
regions.each do |region|
|
111
|
-
a = Aws::RiKanjoo::
|
111
|
+
a = Aws::RiKanjoo::Ec2.new(
|
112
112
|
region = region,
|
113
113
|
instance_type = 'm3.large.not.exists',
|
114
114
|
ri_util = 'medium',
|