aws-rikanjo 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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',
|