murakumo 0.4.4 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/murakumo-attach-ec2-attach-interface +38 -0
- data/bin/murakumo-show-ec2-instances +15 -10
- data/bin/murakumo-show-ec2-interfaces +36 -0
- data/bin/murakumo-show-ec2-private-ip-addresses +37 -0
- data/bin/murakumo-show-ec2-tags +16 -11
- data/etc/attach_if.sh.example +12 -0
- data/etc/murakumo-init.rb.for-ec2 +9 -5
- data/etc/murakumo.server +48 -0
- data/etc/murakumo.yml.example +65 -17
- data/lib/cli/murakumo_initializer_context.rb +3 -0
- data/lib/cli/murakumo_options.rb +78 -0
- data/lib/misc/murakumo_const.rb +1 -1
- data/lib/srv/murakumo_activity_check_notifier.rb +55 -0
- data/lib/srv/murakumo_activity_checker.rb +198 -0
- data/lib/srv/murakumo_cloud.rb +64 -1
- data/lib/srv/murakumo_health_check_notifier.rb +5 -5
- data/lib/srv/murakumo_health_checker.rb +5 -16
- data/lib/util/murakumo_ec2_attach_interface.rb +108 -0
- data/lib/util/murakumo_ec2_client.rb +5 -5
- data/lib/util/murakumo_ec2_interfaces.rb +59 -0
- data/lib/util/murakumo_ec2_private_ip_addresses.rb +42 -0
- metadata +36 -26
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
require 'util/murakumo_ec2_attach_interface'
|
7
|
+
|
8
|
+
access_key = nil
|
9
|
+
secret_key = nil
|
10
|
+
if_id = nil
|
11
|
+
dev_idx = nil
|
12
|
+
endpoint = nil
|
13
|
+
instance_id = nil
|
14
|
+
|
15
|
+
ARGV.options do |opt|
|
16
|
+
begin
|
17
|
+
opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
|
18
|
+
opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
|
19
|
+
opt.on('-n', '--network-if-id IF_ID') {|v| if_id = v }
|
20
|
+
opt.on('-d', '--device-index INDEX') {|v| dev_idx = v }
|
21
|
+
opt.on('-r', '--region REGION') {|v| endpoint = v }
|
22
|
+
opt.on('-i', '--instance-id INSTANCE_ID') {|v| instance_id = v }
|
23
|
+
opt.parse!
|
24
|
+
|
25
|
+
access_key ||= (ENV['AMAZON_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY_ID'])
|
26
|
+
secret_key ||= (ENV['AMAZON_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'])
|
27
|
+
|
28
|
+
unless access_key and secret_key and if_id
|
29
|
+
puts opt.help
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
rescue => e
|
33
|
+
$stderr.puts e
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Murakumo::Util::ec2_attach_interface(access_key, secret_key, if_id, dev_idx, endpoint, instance_id)
|
@@ -12,16 +12,21 @@ endpoint = nil
|
|
12
12
|
instance_id = nil
|
13
13
|
|
14
14
|
ARGV.options do |opt|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
begin
|
16
|
+
opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
|
17
|
+
opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
|
18
|
+
opt.on('-r', '--region REGION') {|v| endpoint = v }
|
19
|
+
opt.parse!
|
20
|
+
|
21
|
+
access_key ||= (ENV['AMAZON_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY_ID'])
|
22
|
+
secret_key ||= (ENV['AMAZON_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'])
|
23
|
+
|
24
|
+
unless access_key and secret_key
|
25
|
+
puts opt.help
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
$stderr.puts e
|
25
30
|
exit 1
|
26
31
|
end
|
27
32
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
require 'util/murakumo_ec2_interfaces'
|
8
|
+
|
9
|
+
access_key = nil
|
10
|
+
secret_key = nil
|
11
|
+
endpoint = nil
|
12
|
+
instance_id = nil
|
13
|
+
|
14
|
+
ARGV.options do |opt|
|
15
|
+
begin
|
16
|
+
opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
|
17
|
+
opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
|
18
|
+
opt.on('-r', '--region REGION') {|v| endpoint = v }
|
19
|
+
opt.parse!
|
20
|
+
|
21
|
+
access_key ||= (ENV['AMAZON_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY_ID'])
|
22
|
+
secret_key ||= (ENV['AMAZON_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'])
|
23
|
+
|
24
|
+
unless access_key and secret_key
|
25
|
+
puts opt.help
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
$stderr.puts e
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
instances = Murakumo::Util::ec2_interfaces(access_key, secret_key, endpoint)
|
35
|
+
|
36
|
+
puts YAML.dump(instances)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
require 'util/murakumo_ec2_private_ip_addresses'
|
7
|
+
|
8
|
+
access_key = nil
|
9
|
+
secret_key = nil
|
10
|
+
endpoint = nil
|
11
|
+
instance_id = nil
|
12
|
+
|
13
|
+
ARGV.options do |opt|
|
14
|
+
begin
|
15
|
+
opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
|
16
|
+
opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
|
17
|
+
opt.on('-r', '--region REGION') {|v| endpoint = v }
|
18
|
+
opt.parse!
|
19
|
+
|
20
|
+
access_key ||= (ENV['AMAZON_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY_ID'])
|
21
|
+
secret_key ||= (ENV['AMAZON_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'])
|
22
|
+
|
23
|
+
unless access_key and secret_key
|
24
|
+
puts opt.help
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
rescue => e
|
28
|
+
$stderr.puts e
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
ip_addrs = Murakumo::Util::ec2_private_ip_addresses(access_key, secret_key, endpoint)
|
34
|
+
|
35
|
+
ip_addrs.each do |i|
|
36
|
+
puts i.join("\t")
|
37
|
+
end
|
data/bin/murakumo-show-ec2-tags
CHANGED
@@ -11,17 +11,22 @@ endpoint = nil
|
|
11
11
|
instance_id = nil
|
12
12
|
|
13
13
|
ARGV.options do |opt|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
begin
|
15
|
+
opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
|
16
|
+
opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
|
17
|
+
opt.on('-r', '--region REGION') {|v| endpoint = v }
|
18
|
+
opt.on('-i', '--instance-id INSTANCE_ID') {|v| instance_id = v }
|
19
|
+
opt.parse!
|
20
|
+
|
21
|
+
access_key ||= (ENV['AMAZON_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY_ID'])
|
22
|
+
secret_key ||= (ENV['AMAZON_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'])
|
23
|
+
|
24
|
+
unless access_key and secret_key
|
25
|
+
puts opt.help
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
$stderr.puts e
|
25
30
|
exit 1
|
26
31
|
end
|
27
32
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
AWS_ACCESS_KEY_ID=...
|
3
|
+
AWS_SECRET_ACCESS_KEY=...
|
4
|
+
REGION=ap-northeast-1
|
5
|
+
IF_ID=eni-...
|
6
|
+
|
7
|
+
/usr/bin/murakumo-attach-ec2-attach-interface \
|
8
|
+
-k "$AWS_ACCESS_KEY_ID" -s "$AWS_SECRET_ACCESS_KEY" \
|
9
|
+
-r $REGION -n $IF_ID 2>&1 | logger
|
10
|
+
|
11
|
+
exit 0
|
12
|
+
|
@@ -1,19 +1,23 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
AWS_ACCESS_KEY_ID = '...'
|
2
|
+
AWS_SECRET_ACCESS_KEY = '...'
|
3
|
+
REGION = 'ap-northeast-1'
|
3
4
|
|
4
5
|
# get self ip address
|
5
6
|
ip_addr = Murakumo::Util.self_ip_address
|
6
7
|
|
7
8
|
# get hostname
|
8
|
-
tags = Murakumo::Util.ec2_tags(
|
9
|
+
tags = Murakumo::Util.ec2_tags(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, REGION)
|
9
10
|
hostname = tags['Name'] || `curl -s http://169.254.169.254/latest/meta-data/local-hostname`
|
10
11
|
|
11
12
|
# rewrite host option
|
12
13
|
@options['host'] = "#{ip_addr}, #{hostname}"
|
13
14
|
|
14
15
|
# get instances
|
15
|
-
|
16
|
+
ip_addrs = Murakumo::Util::ec2_private_ip_addresses(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, REGION)
|
16
17
|
|
17
18
|
# rewrite initial-nodes
|
18
|
-
nodes =
|
19
|
+
nodes = ip_addrs.select {|inst_id, ip_addr, status|
|
20
|
+
status == 'running'
|
21
|
+
}.map {|inst_id, ip_addr, status| ip_addr }
|
22
|
+
|
19
23
|
@options['initial-nodes'] = nodes.join(',') unless nodes.empty?
|
data/etc/murakumo.server
CHANGED
@@ -134,6 +134,54 @@ host: $ip_addr, $hostname, 60
|
|
134
134
|
# unhealthy: 2
|
135
135
|
# script: |
|
136
136
|
# http_get '/index.html'
|
137
|
+
|
138
|
+
# hook of server start
|
139
|
+
#on-start: /foo/bar/on-start.sh # args: address hostname
|
140
|
+
|
141
|
+
# hook of activation of backup/secondary
|
142
|
+
#activity-check:
|
143
|
+
# foo:
|
144
|
+
# start-delay: 60
|
145
|
+
# interval: 10
|
146
|
+
# #init-status: undefined # active/inactive/undefined (default: undefined)
|
147
|
+
# active: 2 # active threshold count
|
148
|
+
# inactive: 2 # inactive threshold count
|
149
|
+
# on-activate: /usr/sbin/attach_if.sh # args: address name status
|
150
|
+
# #on-inactivate: /foo/bar/zoo.sh # args: address name status
|
151
|
+
# bar:
|
152
|
+
# start-delay: 60
|
153
|
+
# interval: 10
|
154
|
+
# #init-status: undefined # active/inactive/undefined (default: undefined)
|
155
|
+
# active: 2 # active threshold count
|
156
|
+
# inactive: 2 # inactive threshold count
|
157
|
+
# on-activate: /usr/sbin/attach_if.sh # args: address name status
|
158
|
+
# #on-inactivate: /foo/bar/zoo.sh # args: address name status
|
159
|
+
|
160
|
+
## about activity-check
|
161
|
+
## ---
|
162
|
+
## when BACKUP:
|
163
|
+
## if BACKUP is started:
|
164
|
+
## init-status: active -> on-inactivate
|
165
|
+
## inactive -> nothing to do
|
166
|
+
## if MASTER became unhealthy or down:
|
167
|
+
## -> on-activate
|
168
|
+
## if MASTER is restored:
|
169
|
+
## -> on-inactivate
|
170
|
+
##
|
171
|
+
## when MASTER:
|
172
|
+
## if MASTER is started:
|
173
|
+
## init-status: active -> nothing to do
|
174
|
+
## inactive -> on-activate
|
175
|
+
## if BACKUP became unhealthy or down:
|
176
|
+
## -> nothing to do
|
177
|
+
## if MASTER became unhealthy:
|
178
|
+
## -> on-inactivate
|
179
|
+
## if MASTER became down:
|
180
|
+
## -> cannot do anything
|
181
|
+
## if MASTER is restored from down:
|
182
|
+
## -> same as MASTER start
|
183
|
+
## if MASTER is restored from unhealthy:
|
184
|
+
## -> on-activate
|
137
185
|
EOF
|
138
186
|
|
139
187
|
chmod 600 $conf
|
data/etc/murakumo.yml.example
CHANGED
@@ -57,20 +57,68 @@ alias:
|
|
57
57
|
- foo,60,master,100
|
58
58
|
- bar,60,master,100
|
59
59
|
|
60
|
-
health-check:
|
61
|
-
foo:
|
62
|
-
interval: 5
|
63
|
-
timeout: 5
|
64
|
-
healthy: 2
|
65
|
-
unhealthy: 2
|
66
|
-
#on-activate: /foo/bar/zoo.sh # args: address name status
|
67
|
-
#on-inactivate: /foo/bar/zoo.sh # args: address name status
|
68
|
-
script: |
|
69
|
-
tcp_check 80
|
70
|
-
bar:
|
71
|
-
interval: 5
|
72
|
-
timeout: 5
|
73
|
-
healthy: 2
|
74
|
-
unhealthy: 2
|
75
|
-
script: |
|
76
|
-
http_get '/index.html'
|
60
|
+
#health-check:
|
61
|
+
# foo:
|
62
|
+
# interval: 5
|
63
|
+
# timeout: 5
|
64
|
+
# healthy: 2
|
65
|
+
# unhealthy: 2
|
66
|
+
# #on-activate: /foo/bar/zoo.sh # args: address name status
|
67
|
+
# #on-inactivate: /foo/bar/zoo.sh # args: address name status
|
68
|
+
# script: |
|
69
|
+
# tcp_check 80
|
70
|
+
# bar:
|
71
|
+
# interval: 5
|
72
|
+
# timeout: 5
|
73
|
+
# healthy: 2
|
74
|
+
# unhealthy: 2
|
75
|
+
# script: |
|
76
|
+
# http_get '/index.html'
|
77
|
+
|
78
|
+
# hook of server start
|
79
|
+
#on-start: /foo/bar/on-start.sh # args: address hostname
|
80
|
+
|
81
|
+
# hook of activation of backup/secondary
|
82
|
+
#activity-check:
|
83
|
+
# foo:
|
84
|
+
# start-delay: 60
|
85
|
+
# interval: 10
|
86
|
+
# #init-status: undefined # active/inactive/undefined (default: undefined)
|
87
|
+
# active: 2 # active threshold count
|
88
|
+
# inactive: 2 # inactive threshold count
|
89
|
+
# on-activate: /usr/sbin/attach_if.sh # args: address name status
|
90
|
+
# #on-inactivate: /foo/bar/zoo.sh # args: address name status
|
91
|
+
# bar:
|
92
|
+
# start-delay: 60
|
93
|
+
# interval: 10
|
94
|
+
# #init-status: undefined # active/inactive/undefined (default: undefined)
|
95
|
+
# active: 2 # active threshold count
|
96
|
+
# inactive: 2 # inactive threshold count
|
97
|
+
# on-activate: /usr/sbin/attach_if.sh # args: address name status
|
98
|
+
# #on-inactivate: /foo/bar/zoo.sh # args: address name status
|
99
|
+
|
100
|
+
## about activity-check
|
101
|
+
## ---
|
102
|
+
## when BACKUP:
|
103
|
+
## if BACKUP is started:
|
104
|
+
## init-status: active -> on-inactivate
|
105
|
+
## inactive -> nothing to do
|
106
|
+
## if MASTER became unhealthy or down:
|
107
|
+
## -> on-activate
|
108
|
+
## if MASTER is restored:
|
109
|
+
## -> on-inactivate
|
110
|
+
##
|
111
|
+
## when MASTER:
|
112
|
+
## if MASTER is started:
|
113
|
+
## init-status: active -> nothing to do
|
114
|
+
## inactive -> on-activate
|
115
|
+
## if BACKUP became unhealthy or down:
|
116
|
+
## -> nothing to do
|
117
|
+
## if MASTER became unhealthy:
|
118
|
+
## -> on-inactivate
|
119
|
+
## if MASTER became down:
|
120
|
+
## -> cannot do anything
|
121
|
+
## if MASTER is restored from down:
|
122
|
+
## -> same as MASTER start
|
123
|
+
## if MASTER is restored from unhealthy:
|
124
|
+
## -> on-activate
|
@@ -2,6 +2,9 @@ require 'misc/murakumo_const'
|
|
2
2
|
require 'util/murakumo_self_ip_address'
|
3
3
|
require 'util/murakumo_ec2_tags'
|
4
4
|
require 'util/murakumo_ec2_instances'
|
5
|
+
require 'util/murakumo_ec2_private_ip_addresses'
|
6
|
+
require 'util/murakumo_ec2_attach_interface'
|
7
|
+
require 'util/murakumo_ec2_interfaces'
|
5
8
|
|
6
9
|
module Murakumo
|
7
10
|
|
data/lib/cli/murakumo_options.rb
CHANGED
@@ -220,9 +220,80 @@ def murakumo_parse_args
|
|
220
220
|
parse_error('configuration of a health check is not right', "#{name}/#{key}")
|
221
221
|
end
|
222
222
|
end
|
223
|
+
|
224
|
+
# 各種変数の設定
|
225
|
+
{
|
226
|
+
'interval' => [ 5, 1, 300],
|
227
|
+
'timeout' => [ 5, 1, 300],
|
228
|
+
'healthy' => [ 2, 1, 60],
|
229
|
+
'unhealthy' => [ 2, 1, 60],
|
230
|
+
}.each {|key, vals|
|
231
|
+
defval, min, max = vals
|
232
|
+
value = (conf[key] || defval).to_i
|
233
|
+
|
234
|
+
if value < min
|
235
|
+
value = min
|
236
|
+
parse_error("health-check/#{name}/#{key} is smaller than #{min}.", "#{name}/#{key}")
|
237
|
+
elsif value > max
|
238
|
+
value = max
|
239
|
+
parse_error("health-check/#{name}/#{key} is larger than #{max}.", "#{name}/#{key}")
|
240
|
+
end
|
241
|
+
|
242
|
+
conf[key] = value
|
243
|
+
}
|
223
244
|
end
|
224
245
|
end # health check
|
225
246
|
|
247
|
+
# activity check
|
248
|
+
if (activity_check = options[:activity_check])
|
249
|
+
activity_check.kind_of?(Hash) or parse_error('configuration of a activity check is not right')
|
250
|
+
|
251
|
+
activity_check.each do |name, conf|
|
252
|
+
# 'on-activate'か'on-inactivate'のいずれかが必須
|
253
|
+
if (conf['on-activate'] || conf['on-inactivate'] || '').empty?
|
254
|
+
parse_error('configuration of a health check is not right', "on-activate or on-inactivate is not defined")
|
255
|
+
end
|
256
|
+
|
257
|
+
conf['init-status'] ||= 'undefined'
|
258
|
+
|
259
|
+
unless /\A(active|inactive|undefined)\Z/i =~ conf['init-status']
|
260
|
+
parse_error('configuration of a health check is not right', "#{name}/init-status")
|
261
|
+
end
|
262
|
+
|
263
|
+
conf['init-status'] = conf['init-status'].downcase.to_sym
|
264
|
+
|
265
|
+
%w(on-activate on-inactivate).each do |key|
|
266
|
+
next unless conf[key]
|
267
|
+
path = conf[key] = conf[key].strip
|
268
|
+
|
269
|
+
if FileTest.directory?(path) or not FileTest.executable?(path)
|
270
|
+
parse_error('configuration of a health check is not right', "#{name}/#{key}")
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# 各種変数の設定
|
275
|
+
{
|
276
|
+
'interval' => [ 10, 1, 300],
|
277
|
+
'start-delay' => [ 60, 1, 300],
|
278
|
+
'active' => [ 2, 1, 60],
|
279
|
+
'inactive' => [ 2, 1, 60],
|
280
|
+
}.each {|key, vals|
|
281
|
+
defval, min, max = vals
|
282
|
+
value = (conf[key] || defval).to_i
|
283
|
+
|
284
|
+
if value < min
|
285
|
+
value = min
|
286
|
+
parse_error("activateation-check/#{name}/#{key} is smaller than #{min}.", "#{name}/#{key}")
|
287
|
+
elsif value > max
|
288
|
+
value = max
|
289
|
+
parse_error("activation-check/#{name}/#{key} is larger than #{max}.", "#{name}/#{key}")
|
290
|
+
end
|
291
|
+
|
292
|
+
conf[key] = value
|
293
|
+
}
|
294
|
+
end
|
295
|
+
end # activity check
|
296
|
+
|
226
297
|
# notification
|
227
298
|
if (ntfc = options[:notification])
|
228
299
|
ntfc.kind_of?(Hash) or parse_error('configuration of a notification is not right')
|
@@ -318,6 +389,13 @@ def murakumo_parse_args
|
|
318
389
|
balancing_h[reg_dest] = attrs_h
|
319
390
|
end
|
320
391
|
end # balancing
|
392
|
+
|
393
|
+
# on start
|
394
|
+
if (on_start = options[:on_start])
|
395
|
+
unless File.exist?(on_start)
|
396
|
+
parse_error('on_start script is not found')
|
397
|
+
end
|
398
|
+
end # on start
|
321
399
|
end # after
|
322
400
|
|
323
401
|
error do |e|
|
data/lib/misc/murakumo_const.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'net/smtp'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Murakumo
|
5
|
+
|
6
|
+
class ActivityCheckNotifier
|
7
|
+
|
8
|
+
def initialize(address, name, logger, options)
|
9
|
+
@address = address
|
10
|
+
@name = name
|
11
|
+
@logger = logger
|
12
|
+
@args = options[:args]
|
13
|
+
@sender = options[:sender]
|
14
|
+
@recipients = options[:recipients]
|
15
|
+
@open_timeout = options[:open_timeout]
|
16
|
+
@read_timeout = options[:read_timeout]
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify_active
|
20
|
+
notify('active', "#{@name} became active.")
|
21
|
+
end
|
22
|
+
|
23
|
+
def notify_inactive
|
24
|
+
notify('inactive', "#{@name} became inactive. Zzz...")
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def notify(status, body)
|
29
|
+
Net::SMTP.start(*@args) do |smtp|
|
30
|
+
smtp.open_timeout = @open_timeout if @open_timeout
|
31
|
+
smtp.read_timeout = @read_timeout if @read_timeout
|
32
|
+
|
33
|
+
smtp.send_mail(<<-EOS, @sender, *@recipients)
|
34
|
+
From: Murakumo Activity Check Notifier <#{@sender}>
|
35
|
+
To: #{@recipients.join(', ')}
|
36
|
+
Subject: [Activity] #{@name}/#{@address} => #{status}
|
37
|
+
Date: #{Time.now.rfc2822}
|
38
|
+
|
39
|
+
Address: #{@address}
|
40
|
+
Name: #{@name}
|
41
|
+
Status: #{status}
|
42
|
+
|
43
|
+
#{body.strip}
|
44
|
+
EOS
|
45
|
+
end
|
46
|
+
|
47
|
+
@logger.info("sent the notice: #{status}")
|
48
|
+
rescue Exception => e
|
49
|
+
message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
|
50
|
+
@logger.error("activity check failed: #{@name}: #{message}")
|
51
|
+
end
|
52
|
+
|
53
|
+
end # ActivityCheckNotifier
|
54
|
+
|
55
|
+
end # Murakumo
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
require 'srv/murakumo_activity_check_notifier'
|
4
|
+
require 'misc/murakumo_const'
|
5
|
+
|
6
|
+
module Murakumo
|
7
|
+
|
8
|
+
class ActivityChecker
|
9
|
+
|
10
|
+
def initialize(address, name, cloud, logger, options)
|
11
|
+
@address = address
|
12
|
+
@name = name
|
13
|
+
@cloud = cloud
|
14
|
+
@logger = logger
|
15
|
+
@options = options
|
16
|
+
|
17
|
+
# 各種変数の設定
|
18
|
+
['interval', 'start-delay', 'active', 'inactive', 'init-status'].each {|key|
|
19
|
+
value = options[key]
|
20
|
+
instance_variable_set("@#{key.gsub('-', '_')}", value)
|
21
|
+
}
|
22
|
+
|
23
|
+
# 通知オブジェクトの設定
|
24
|
+
if options[:notification]
|
25
|
+
@notifier = ActivityCheckNotifier.new(@address, @name, @logger, options[:notification])
|
26
|
+
end
|
27
|
+
|
28
|
+
# イベントハンドラの設定
|
29
|
+
@on_activate = options['on-activate']
|
30
|
+
@on_inactivate = options['on-inactivate']
|
31
|
+
end
|
32
|
+
|
33
|
+
def mark_active
|
34
|
+
if @activated.nil?
|
35
|
+
# 状態がなかったら、まず状態をセット
|
36
|
+
@logger.info("initial activity: #{@name}: active")
|
37
|
+
@activated = true
|
38
|
+
elsif @activated == true # わざとですよ…
|
39
|
+
@inactive_count = 0
|
40
|
+
elsif (@active_count += 1) >= @active
|
41
|
+
toggle_activity
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def mark_inactive
|
46
|
+
if @activated.nil?
|
47
|
+
# 状態がなかったら、まず状態をセット
|
48
|
+
@logger.info("initial activity: #{@name}: inactive")
|
49
|
+
@activated = false
|
50
|
+
elsif @activated == false # わざとですよ…
|
51
|
+
@active_count = 0
|
52
|
+
elsif (@inactive_count += 1) >= @inactive
|
53
|
+
toggle_activity
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def toggle_activity
|
58
|
+
@activated = !@activated
|
59
|
+
@active_count = 0
|
60
|
+
@inactive_count = 0
|
61
|
+
|
62
|
+
status = @activated ? 'active' : 'inactive'
|
63
|
+
@logger.info("activity condition changed: #{@name}: #{status}")
|
64
|
+
|
65
|
+
if @activated
|
66
|
+
@notifier.notify_active if @notifier
|
67
|
+
handle_event(@on_activate, 'active') if @on_activate
|
68
|
+
else
|
69
|
+
@notifier.notify_inactive if @notifier
|
70
|
+
handle_event(@on_inactivate, 'inactive') if @on_inactivate
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def start
|
75
|
+
# 各種変数は初期状態にする
|
76
|
+
@alive = true
|
77
|
+
@active_count = 0
|
78
|
+
@inactive_count = 0
|
79
|
+
|
80
|
+
# アクティビティの初期状態を設定
|
81
|
+
case @init_status
|
82
|
+
when :active
|
83
|
+
@activated = true
|
84
|
+
@logger.info("initial activity: #{@name}: active")
|
85
|
+
when :inactive
|
86
|
+
@activated = false
|
87
|
+
@logger.info("initial activity: #{@name}: inactive")
|
88
|
+
else
|
89
|
+
@activated = nil
|
90
|
+
end
|
91
|
+
|
92
|
+
# 既存のスレッドは破棄
|
93
|
+
if @thread and @thread.alive?
|
94
|
+
begin
|
95
|
+
@thread.kill
|
96
|
+
rescue ThreadError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
@thread = Thread.start {
|
101
|
+
begin
|
102
|
+
# 初回実行の遅延
|
103
|
+
if @start_delay
|
104
|
+
@logger.debug("activity check is delaying: #{@name}")
|
105
|
+
sleep @start_delay
|
106
|
+
@start_delay = nil
|
107
|
+
@logger.debug("activity check is starting: #{@name}")
|
108
|
+
end
|
109
|
+
|
110
|
+
while @alive
|
111
|
+
retval = nil
|
112
|
+
|
113
|
+
begin
|
114
|
+
retval = validate_activity
|
115
|
+
rescue => e
|
116
|
+
retval = false
|
117
|
+
message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
|
118
|
+
@logger.error("activity check failed: #{@name}: #{message}")
|
119
|
+
end
|
120
|
+
|
121
|
+
status = retval == true ? 'active' : retval == false ? 'inactive' : '-'
|
122
|
+
@logger.debug("result of a activity check: #{@name}: #{status}")
|
123
|
+
|
124
|
+
if retval == true
|
125
|
+
mark_active
|
126
|
+
elsif retval == false
|
127
|
+
mark_inactive
|
128
|
+
end
|
129
|
+
|
130
|
+
sleep @interval
|
131
|
+
end # while
|
132
|
+
rescue Exception => e
|
133
|
+
message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
|
134
|
+
@logger.error("#{@name}: #{message}")
|
135
|
+
end # begin
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
def stop
|
140
|
+
@alive = false
|
141
|
+
end
|
142
|
+
|
143
|
+
def alive?
|
144
|
+
@alive
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def validate_activity
|
150
|
+
records = @cloud.db.execute(<<-EOS, @name, ACTIVE)
|
151
|
+
SELECT ip_address, priority FROM records
|
152
|
+
WHERE name = ? AND activity = ?
|
153
|
+
EOS
|
154
|
+
|
155
|
+
# レコードがなければ非アクティブ
|
156
|
+
return false if records.empty?
|
157
|
+
|
158
|
+
# マスタに自IPが含まれているならアクティブ
|
159
|
+
masters = records.select {|i| i['priority'] == MASTER }
|
160
|
+
|
161
|
+
if masters.any? {|i| i['ip_address'] == @address }
|
162
|
+
return true
|
163
|
+
end
|
164
|
+
|
165
|
+
# マスタがなくてセカンダリに自IPが含まれているならアクティブ
|
166
|
+
secondaries = records.select {|i| i['priority'] == SECONDARY }
|
167
|
+
|
168
|
+
if masters.empty? and secondaries.any? {|i| i['ip_address'] == @address }
|
169
|
+
return true
|
170
|
+
end
|
171
|
+
|
172
|
+
# マスタ・セカンダリがなくてバックアップに自IPが含まれているならアクティブ
|
173
|
+
backups = records.select {|i| i['priority'] == BACKUP }
|
174
|
+
|
175
|
+
if masters.empty? and secondaries.empty? and backups.any? {|i| i['ip_address'] == @address }
|
176
|
+
return true
|
177
|
+
end
|
178
|
+
|
179
|
+
# 上記以外は非アクティブ
|
180
|
+
return false
|
181
|
+
end
|
182
|
+
|
183
|
+
def handle_event(handler, status)
|
184
|
+
Open3.popen3("#{@on_activate} '#{@address}' '#{@name}' '#{status}'") do |stdin, stdout, stderr|
|
185
|
+
out = stdout.read.strip
|
186
|
+
@logger.info(out) unless out.empty?
|
187
|
+
|
188
|
+
err = stderr.read.strip
|
189
|
+
@logger.error(err) unless err.empty?
|
190
|
+
end
|
191
|
+
rescue Exception => e
|
192
|
+
message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
|
193
|
+
@logger.error("#{@name}: #{message}")
|
194
|
+
end
|
195
|
+
|
196
|
+
end # ActivityChecker
|
197
|
+
|
198
|
+
end # Murakumo
|
data/lib/srv/murakumo_cloud.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'rgossip2'
|
3
3
|
require 'sqlite3'
|
4
|
+
require 'open3'
|
4
5
|
|
5
6
|
require 'srv/murakumo_health_checker'
|
7
|
+
require 'srv/murakumo_activity_checker'
|
6
8
|
require 'srv/murakumo_balancer'
|
7
9
|
require 'misc/murakumo_const'
|
8
10
|
|
@@ -96,6 +98,34 @@ module Murakumo
|
|
96
98
|
end
|
97
99
|
end
|
98
100
|
|
101
|
+
# アクティビティチェック
|
102
|
+
@activity_checkers = {}
|
103
|
+
|
104
|
+
if options[:activity_check]
|
105
|
+
activity_check = options[:activity_check]
|
106
|
+
|
107
|
+
if activity_check.kind_of?(Hash)
|
108
|
+
activity_check.each do |name, conf|
|
109
|
+
name = name.downcase
|
110
|
+
|
111
|
+
if options[:notification]
|
112
|
+
conf = conf.merge(:notification => options[:notification])
|
113
|
+
end
|
114
|
+
|
115
|
+
if datas.any? {|i| i[0] == name }
|
116
|
+
checker = ActivityChecker.new(@address, name, self, @logger, conf)
|
117
|
+
@activity_checkers[name] = checker
|
118
|
+
# アクティビティチェックはまだ起動しない
|
119
|
+
else
|
120
|
+
# ホスト名になかったら警告
|
121
|
+
@logger.warn("host for a activity check is not found: #{name}")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
@logger.warn('configuration of a activity check is not right')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
99
129
|
# キャッシュ
|
100
130
|
@cache = {} if options[:enable_cache]
|
101
131
|
|
@@ -113,9 +143,34 @@ module Murakumo
|
|
113
143
|
checker.start
|
114
144
|
end
|
115
145
|
|
146
|
+
# アクティビティチェックを起動
|
147
|
+
@activity_checkers.each do |name, checker|
|
148
|
+
checker.start
|
149
|
+
end
|
150
|
+
|
151
|
+
# 起動時フックスクリプトの実行
|
152
|
+
if @options[:on_start]
|
153
|
+
exec_start_script(@options[:on_start])
|
154
|
+
end
|
155
|
+
|
116
156
|
@gossip.start
|
117
157
|
end
|
118
158
|
|
159
|
+
def exec_start_script(script)
|
160
|
+
@logger.info("starting script is performed: #{script}")
|
161
|
+
|
162
|
+
Open3.popen3("#{script} '#{@address}' '#{@hostname}'") do |stdin, stdout, stderr|
|
163
|
+
out = stdout.read.strip
|
164
|
+
@logger.info(out) unless out.empty?
|
165
|
+
|
166
|
+
err = stderr.read.strip
|
167
|
+
@logger.error(err) unless err.empty?
|
168
|
+
end
|
169
|
+
rescue Exception => e
|
170
|
+
message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
|
171
|
+
@logger.error("#{@name}: #{message}")
|
172
|
+
end
|
173
|
+
|
119
174
|
def to_hash
|
120
175
|
keys = {
|
121
176
|
:auth_key => 'auth-key',
|
@@ -166,7 +221,7 @@ module Murakumo
|
|
166
221
|
hash['host'] = records.find {|r| r[3] == ORIGIN }[0..2].join(',')
|
167
222
|
|
168
223
|
aliases = records.select {|r| r[3] != ORIGIN }.map do |r|
|
169
|
-
[r[1], r[2], (r[3] == MASTER ? 'master' : 'backup'), r[4]].join(',')
|
224
|
+
[r[1], r[2], (r[3] == MASTER ? 'master' : r[3] == SECONDARY ? 'secondary' : 'backup'), r[4]].join(',')
|
170
225
|
end
|
171
226
|
|
172
227
|
hash['alias'] = aliases unless aliases.empty?
|
@@ -177,6 +232,10 @@ module Murakumo
|
|
177
232
|
hash['health-check'] = @options.config_file['health-check']
|
178
233
|
end
|
179
234
|
|
235
|
+
if @options.config_file['activity-check']
|
236
|
+
hash['activity-check'] = @options.config_file['activity-check']
|
237
|
+
end
|
238
|
+
|
180
239
|
if @options.config_file['notification']
|
181
240
|
hash['notification'] = @options.config_file['notification']
|
182
241
|
end
|
@@ -190,6 +249,10 @@ module Murakumo
|
|
190
249
|
if @options.config_file['balancing']
|
191
250
|
hash['balancing'] = @options.config_file['balancing']
|
192
251
|
end
|
252
|
+
|
253
|
+
if @options.config_file['on-start']
|
254
|
+
hash['on-start'] = @options.config_file['on-start']
|
255
|
+
end
|
193
256
|
end # 設定ファイルのみの項目
|
194
257
|
|
195
258
|
return hash
|
@@ -18,11 +18,11 @@ module Murakumo
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def notify_active
|
21
|
-
notify('
|
21
|
+
notify('healthy', "#{@name} became healthy :-)")
|
22
22
|
end
|
23
23
|
|
24
24
|
def notify_inactive
|
25
|
-
notify('
|
25
|
+
notify('unhealthy', "#{@name} became unhealthy :-(")
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
@@ -32,9 +32,9 @@ module Murakumo
|
|
32
32
|
smtp.read_timeout = @read_timeout if @read_timeout
|
33
33
|
|
34
34
|
smtp.send_mail(<<-EOS, @sender, *@recipients)
|
35
|
-
From: Murakumo Notifier <#{@sender}>
|
35
|
+
From: Murakumo Health Check Notifier <#{@sender}>
|
36
36
|
To: #{@recipients.join(', ')}
|
37
|
-
Subject: #{@name}/#{@address} => #{status}
|
37
|
+
Subject: [Health] #{@name}/#{@address} => #{status}
|
38
38
|
Date: #{Time.now.rfc2822}
|
39
39
|
|
40
40
|
Address: #{@address}
|
@@ -48,7 +48,7 @@ Status: #{status}
|
|
48
48
|
@logger.info("sent the notice: #{status}")
|
49
49
|
rescue Exception => e
|
50
50
|
message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
|
51
|
-
@logger.error("
|
51
|
+
@logger.error("health check failed: #{@name}: #{message}")
|
52
52
|
end
|
53
53
|
|
54
54
|
end # HealthCheckNotifier
|
@@ -18,16 +18,8 @@ module Murakumo
|
|
18
18
|
@options = options
|
19
19
|
|
20
20
|
# 各種変数の設定
|
21
|
-
{
|
22
|
-
|
23
|
-
'timeout' => [ 5, 1, 300],
|
24
|
-
'healthy' => [10, 2, 10],
|
25
|
-
'unhealthy' => [ 2, 2, 10],
|
26
|
-
}.each {|key, vals|
|
27
|
-
defval, min, max = vals
|
28
|
-
value = (options[key] || defval).to_i
|
29
|
-
value = min if value < min
|
30
|
-
value = max if value > max
|
21
|
+
['interval', 'timeout', 'healthy', 'unhealthy'].each {|key|
|
22
|
+
value = options[key]
|
31
23
|
instance_variable_set("@#{key}", value)
|
32
24
|
}
|
33
25
|
|
@@ -87,10 +79,10 @@ module Murakumo
|
|
87
79
|
case activity
|
88
80
|
when ACTIVE
|
89
81
|
@notifier.notify_active if @notifier
|
90
|
-
handle_event(@on_activate, '
|
82
|
+
handle_event(@on_activate, 'healthy') if @on_activate
|
91
83
|
when INACTIVE
|
92
84
|
@notifier.notify_inactive if @notifier
|
93
|
-
handle_event(@on_inactivate, '
|
85
|
+
handle_event(@on_inactivate, 'unhealthy') if @on_inactivate
|
94
86
|
end
|
95
87
|
end
|
96
88
|
|
@@ -110,9 +102,6 @@ module Murakumo
|
|
110
102
|
end
|
111
103
|
|
112
104
|
@thread = Thread.start {
|
113
|
-
healthy = 0
|
114
|
-
unhealthy = 0
|
115
|
-
|
116
105
|
begin
|
117
106
|
while @alive
|
118
107
|
retval = nil
|
@@ -126,7 +115,7 @@ module Murakumo
|
|
126
115
|
rescue => e
|
127
116
|
retval = false
|
128
117
|
message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
|
129
|
-
@logger.error("
|
118
|
+
@logger.error("health check failed: #{@name}: #{message}")
|
130
119
|
end
|
131
120
|
|
132
121
|
status = retval == true ? 'good' : retval == false ? 'bad' : '-'
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
require 'util/murakumo_ec2_client'
|
5
|
+
require 'util/murakumo_ec2_interfaces'
|
6
|
+
|
7
|
+
module Murakumo
|
8
|
+
|
9
|
+
class Util
|
10
|
+
WAIT_LIMIT = 99
|
11
|
+
WAIT_INTERVAL = 0.3
|
12
|
+
|
13
|
+
def self.ec2_attach_interface(access_key, secret_key, if_id, dev_idx = 1, endpoint = nil, instance_id = nil)
|
14
|
+
dev_idx = 1 unless dev_idx
|
15
|
+
|
16
|
+
unless instance_id
|
17
|
+
instance_id = Net::HTTP.get('169.254.169.254', '/latest/meta-data/instance-id')
|
18
|
+
end
|
19
|
+
|
20
|
+
check_own_attached(access_key, secret_key, endpoint, if_id, instance_id)
|
21
|
+
|
22
|
+
ec2_detach_interface(access_key, secret_key, if_id, endpoint, :force) rescue nil
|
23
|
+
|
24
|
+
wait_detach(access_key, secret_key, endpoint, if_id)
|
25
|
+
|
26
|
+
ec2cli = Murakumo::Util::EC2Client.new(access_key, secret_key, endpoint)
|
27
|
+
source = ec2cli.query('AttachNetworkInterface',
|
28
|
+
'NetworkInterfaceId' => if_id, 'InstanceId' => instance_id, 'DeviceIndex' => dev_idx)
|
29
|
+
|
30
|
+
errors = []
|
31
|
+
|
32
|
+
REXML::Document.new(source).each_element('//Errors/Error') do |element|
|
33
|
+
code = element.text('Code')
|
34
|
+
message = element.text('Message')
|
35
|
+
errors << "#{code}:#{message}"
|
36
|
+
end
|
37
|
+
|
38
|
+
raise errors.join(', ') unless errors.empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.ec2_detach_interface(access_key, secret_key, if_id, endpoint = nil, force = false)
|
42
|
+
interfaces = ec2_interfaces(access_key, secret_key, endpoint, if_id)
|
43
|
+
|
44
|
+
if not interfaces or interfaces.empty?
|
45
|
+
raise 'interface was not found'
|
46
|
+
end
|
47
|
+
|
48
|
+
interface = interfaces.first
|
49
|
+
attachment_id = (interface['attachment'] || {})['attachmentId'] || ''
|
50
|
+
|
51
|
+
if attachment_id.empty?
|
52
|
+
raise 'attachmentId was not found'
|
53
|
+
end
|
54
|
+
|
55
|
+
ec2cli = Murakumo::Util::EC2Client.new(access_key, secret_key, endpoint)
|
56
|
+
|
57
|
+
params = {'AttachmentId' => attachment_id}
|
58
|
+
params['Force'] = true if force
|
59
|
+
source = ec2cli.query('DetachNetworkInterface', params)
|
60
|
+
|
61
|
+
errors = []
|
62
|
+
|
63
|
+
REXML::Document.new(source).each_element('//Errors/Error') do |element|
|
64
|
+
code = element.text('Code')
|
65
|
+
message = element.text('Message')
|
66
|
+
errors << "#{code}:#{message}"
|
67
|
+
end
|
68
|
+
|
69
|
+
raise errors.join(', ') unless errors.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.check_own_attached(access_key, secret_key, endpoint, if_id, instance_id)
|
73
|
+
interfaces = ec2_interfaces(access_key, secret_key, endpoint, if_id)
|
74
|
+
|
75
|
+
if not interfaces or interfaces.empty?
|
76
|
+
raise 'interface was not found'
|
77
|
+
end
|
78
|
+
|
79
|
+
interface = interfaces.first
|
80
|
+
|
81
|
+
if (interface['attachment'] || {})['instanceId'] == instance_id
|
82
|
+
raise 'interface is already attached'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
private_class_method :check_own_attached
|
86
|
+
|
87
|
+
def self.wait_detach(access_key, secret_key, endpoint, if_id)
|
88
|
+
WAIT_LIMIT.times do
|
89
|
+
interfaces = ec2_interfaces(access_key, secret_key, endpoint, if_id)
|
90
|
+
|
91
|
+
if not interfaces or interfaces.empty?
|
92
|
+
raise 'interface was not found'
|
93
|
+
end
|
94
|
+
|
95
|
+
interface = interfaces.first
|
96
|
+
|
97
|
+
return if interface['status'] == 'available'
|
98
|
+
|
99
|
+
sleep WAIT_INTERVAL
|
100
|
+
end
|
101
|
+
|
102
|
+
raise 'cannot detach interface'
|
103
|
+
end
|
104
|
+
private_class_method :wait_detach
|
105
|
+
|
106
|
+
end # Util
|
107
|
+
|
108
|
+
end # Murakumo
|
@@ -11,10 +11,11 @@ module Murakumo
|
|
11
11
|
|
12
12
|
class EC2Client
|
13
13
|
|
14
|
-
API_VERSION = '2011-12-
|
14
|
+
API_VERSION = '2011-12-15'
|
15
15
|
SIGNATURE_VERSION = 2
|
16
|
+
SIGNATURE_ALGORITHM = :SHA256
|
16
17
|
|
17
|
-
def initialize(accessKeyId, secretAccessKey, endpoint = nil
|
18
|
+
def initialize(accessKeyId, secretAccessKey, endpoint = nil)
|
18
19
|
unless endpoint
|
19
20
|
local_hostname = Net::HTTP.get('169.254.169.254', '/latest/meta-data/local-hostname')
|
20
21
|
|
@@ -28,7 +29,6 @@ module Murakumo
|
|
28
29
|
@accessKeyId = accessKeyId
|
29
30
|
@secretAccessKey = secretAccessKey
|
30
31
|
@endpoint = endpoint
|
31
|
-
@algorithm = algorithm
|
32
32
|
|
33
33
|
if /\A[^.]+\Z/ =~ @endpoint
|
34
34
|
@endpoint = "ec2.#{@endpoint}.amazonaws.com"
|
@@ -41,7 +41,7 @@ module Murakumo
|
|
41
41
|
:Version => API_VERSION,
|
42
42
|
:Timestamp => Time.now.getutc.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
43
43
|
:SignatureVersion => SIGNATURE_VERSION,
|
44
|
-
:SignatureMethod => "Hmac#{
|
44
|
+
:SignatureMethod => "Hmac#{SIGNATURE_ALGORITHM}",
|
45
45
|
:AWSAccessKeyId => @accessKeyId,
|
46
46
|
}.merge(params)
|
47
47
|
|
@@ -69,7 +69,7 @@ module Murakumo
|
|
69
69
|
def aws_sign(params)
|
70
70
|
params = params.sort_by {|a, b| a.to_s }.map {|k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join('&')
|
71
71
|
string_to_sign = "POST\n#{@endpoint}\n/\n#{params}"
|
72
|
-
digest = OpenSSL::HMAC.digest(OpenSSL::Digest.const_get(
|
72
|
+
digest = OpenSSL::HMAC.digest(OpenSSL::Digest.const_get(SIGNATURE_ALGORITHM).new, @secretAccessKey, string_to_sign)
|
73
73
|
Base64.encode64(digest).gsub("\n", '')
|
74
74
|
end
|
75
75
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
require 'util/murakumo_ec2_client'
|
5
|
+
|
6
|
+
module Murakumo
|
7
|
+
|
8
|
+
class Util
|
9
|
+
|
10
|
+
def self.ec2_interfaces(access_key, secret_key, endpoint = nil, if_id = nil)
|
11
|
+
dev_idx = 1 unless dev_idx
|
12
|
+
params = {}
|
13
|
+
|
14
|
+
if if_id
|
15
|
+
params.update('Filter.1.Name' => 'network-interface-id', 'Filter.1.Value' => if_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
ec2cli = Murakumo::Util::EC2Client.new(access_key, secret_key, endpoint)
|
19
|
+
|
20
|
+
source = ec2cli.query('DescribeNetworkInterfaces', params)
|
21
|
+
interfaces = []
|
22
|
+
|
23
|
+
items = REXML::Document.new(source).get_elements('//networkInterfaceSet/item')
|
24
|
+
walk_item_list(items, interfaces)
|
25
|
+
|
26
|
+
return interfaces
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.walk_item_list(list, ary)
|
30
|
+
list.each do |item|
|
31
|
+
hash = {}
|
32
|
+
walk_item(item, hash)
|
33
|
+
ary << hash
|
34
|
+
end
|
35
|
+
end
|
36
|
+
private_class_method :walk_item_list
|
37
|
+
|
38
|
+
def self.walk_item(item, hash)
|
39
|
+
return unless item.has_elements?
|
40
|
+
|
41
|
+
item.elements.each do |child|
|
42
|
+
if child.has_elements?
|
43
|
+
if child.elements.all? {|i| i.name =~ /\Aitem\Z/i }
|
44
|
+
hash[child.name] = nested = []
|
45
|
+
walk_item_list(child.elements, nested)
|
46
|
+
else
|
47
|
+
hash[child.name] = nested = {}
|
48
|
+
walk_item(child, nested)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
hash[child.name] = child.text
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
private_class_method :walk_item
|
56
|
+
|
57
|
+
end # Util
|
58
|
+
|
59
|
+
end # Murakumo
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'rexml/parsers/pullparser'
|
3
|
+
|
4
|
+
require 'util/murakumo_ec2_client'
|
5
|
+
|
6
|
+
module Murakumo
|
7
|
+
|
8
|
+
class Util
|
9
|
+
|
10
|
+
def self.ec2_private_ip_addresses(access_key, secret_key, endpoint = nil)
|
11
|
+
ec2cli = Murakumo::Util::EC2Client.new(access_key, secret_key, endpoint)
|
12
|
+
source = ec2cli.query('DescribeInstances')
|
13
|
+
|
14
|
+
parser = REXML::Parsers::PullParser.new(source)
|
15
|
+
ip_addrs = []
|
16
|
+
instance_id = nil
|
17
|
+
status = nil
|
18
|
+
|
19
|
+
while parser.has_next?
|
20
|
+
event = parser.pull
|
21
|
+
next if event.event_type != :start_element
|
22
|
+
|
23
|
+
case event[0]
|
24
|
+
when 'instanceId'
|
25
|
+
instance_id = parser.pull[0]
|
26
|
+
when 'instanceState'
|
27
|
+
until event.event_type == :start_element and event[0] == 'name'
|
28
|
+
event = parser.pull
|
29
|
+
end
|
30
|
+
|
31
|
+
status = parser.pull[0]
|
32
|
+
when 'privateIpAddress'
|
33
|
+
ip_addrs << [instance_id, parser.pull[0], status]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
return ip_addrs
|
38
|
+
end
|
39
|
+
|
40
|
+
end # Util
|
41
|
+
|
42
|
+
end # Murakumo
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: murakumo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 5
|
10
|
+
version: 0.4.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- winebarrel
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-01-
|
19
|
-
default_executable:
|
18
|
+
date: 2012-01-22 00:00:00 Z
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
21
|
name: rubydns
|
@@ -42,12 +41,12 @@ dependencies:
|
|
42
41
|
requirements:
|
43
42
|
- - ">="
|
44
43
|
- !ruby/object:Gem::Version
|
45
|
-
hash:
|
44
|
+
hash: 17
|
46
45
|
segments:
|
47
46
|
- 0
|
48
|
-
-
|
49
|
-
-
|
50
|
-
version: 0.
|
47
|
+
- 2
|
48
|
+
- 3
|
49
|
+
version: 0.2.3
|
51
50
|
type: :runtime
|
52
51
|
version_requirements: *id002
|
53
52
|
- !ruby/object:Gem::Dependency
|
@@ -91,41 +90,52 @@ executables:
|
|
91
90
|
- murakumo-show-ip-address
|
92
91
|
- murakumo-show-ec2-tags
|
93
92
|
- murakumo-show-ec2-instances
|
93
|
+
- murakumo-show-ec2-private-ip-addresses
|
94
|
+
- murakumo-attach-ec2-attach-interface
|
95
|
+
- murakumo-show-ec2-interfaces
|
94
96
|
extensions: []
|
95
97
|
|
96
98
|
extra_rdoc_files: []
|
97
99
|
|
98
100
|
files:
|
99
101
|
- README
|
100
|
-
- bin/murakumo-show-ec2-tags
|
101
|
-
- bin/murakumo-install-init-script
|
102
102
|
- bin/murakumo-show-ec2-instances
|
103
|
+
- bin/murakumo-show-ec2-private-ip-addresses
|
104
|
+
- bin/murakumo-attach-ec2-attach-interface
|
103
105
|
- bin/murakumo
|
106
|
+
- bin/murakumo-show-ec2-tags
|
104
107
|
- bin/murakumo-show-ip-address
|
108
|
+
- bin/murakumo-show-ec2-interfaces
|
109
|
+
- bin/murakumo-install-init-script
|
105
110
|
- bin/mrkmctl
|
106
|
-
- lib/misc/murakumo_const.rb
|
107
|
-
- lib/srv/murakumo_health_check_context.rb
|
108
|
-
- lib/srv/murakumo_balancer.rb
|
109
|
-
- lib/srv/murakumo_cloud.rb
|
110
|
-
- lib/srv/murakumo_health_check_notifier.rb
|
111
|
-
- lib/srv/murakumo_health_checker.rb
|
112
|
-
- lib/srv/murakumo_server.rb
|
113
|
-
- lib/cli/mrkmctl_options.rb
|
114
|
-
- lib/cli/murakumo_initializer_context.rb
|
115
111
|
- lib/cli/mrkmctl.rb
|
116
112
|
- lib/cli/murakumo.rb
|
117
113
|
- lib/cli/murakumo_options.rb
|
114
|
+
- lib/cli/murakumo_initializer_context.rb
|
115
|
+
- lib/cli/mrkmctl_options.rb
|
116
|
+
- lib/misc/murakumo_const.rb
|
117
|
+
- lib/util/murakumo_ec2_private_ip_addresses.rb
|
118
118
|
- lib/util/murakumo_ec2_instances.rb
|
119
|
-
- lib/util/murakumo_self_ip_address.rb
|
120
119
|
- lib/util/murakumo_ec2_tags.rb
|
120
|
+
- lib/util/murakumo_ec2_interfaces.rb
|
121
|
+
- lib/util/murakumo_self_ip_address.rb
|
122
|
+
- lib/util/murakumo_ec2_attach_interface.rb
|
121
123
|
- lib/util/murakumo_ec2_client.rb
|
124
|
+
- lib/srv/murakumo_health_check_context.rb
|
125
|
+
- lib/srv/murakumo_server.rb
|
126
|
+
- lib/srv/murakumo_health_check_notifier.rb
|
127
|
+
- lib/srv/murakumo_activity_check_notifier.rb
|
128
|
+
- lib/srv/murakumo_activity_checker.rb
|
129
|
+
- lib/srv/murakumo_health_checker.rb
|
130
|
+
- lib/srv/murakumo_balancer.rb
|
131
|
+
- lib/srv/murakumo_cloud.rb
|
132
|
+
- etc/murakumo.server
|
122
133
|
- etc/murakumo-init.rb.for-ec2
|
134
|
+
- etc/murakumo.yml.example
|
123
135
|
- etc/gai.conf.example
|
124
|
-
- etc/murakumo.server
|
125
|
-
- etc/dhclient-script.patch
|
126
136
|
- etc/murakumo-update-config-for-ec2
|
127
|
-
- etc/
|
128
|
-
|
137
|
+
- etc/attach_if.sh.example
|
138
|
+
- etc/dhclient-script.patch
|
129
139
|
homepage: https://github.com/cookpad/murakumo/wiki
|
130
140
|
licenses: []
|
131
141
|
|
@@ -155,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
165
|
requirements: []
|
156
166
|
|
157
167
|
rubyforge_project:
|
158
|
-
rubygems_version: 1.
|
168
|
+
rubygems_version: 1.8.15
|
159
169
|
signing_key:
|
160
170
|
specification_version: 3
|
161
171
|
summary: Murakumo is the internal DNS server which manages name information using a gossip protocol.
|