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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Aws
2
2
  module Rikanjo
3
- VERSION = "0.0.8"
3
+ VERSION = "0.0.9"
4
4
  end
5
5
  end
@@ -1,14 +1,14 @@
1
1
  require "spec_helper"
2
- require "aws/rikanjo/mode/ec2"
2
+ require "aws/rikanjo/ec2"
3
3
 
4
4
  include RikanjoSpecHelper
5
5
 
6
- describe 'AWS::Rikanjo::Mode::Ec2' do
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
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::Mode::Ec2.new(
111
+ a = Aws::RiKanjoo::Ec2.new(
112
112
  region = region,
113
113
  instance_type = 'm3.large.not.exists',
114
114
  ri_util = 'medium',