murakumo 0.3.7 → 0.3.8
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.
- data/README +5 -5
- data/etc/murakumo.server +12 -0
- data/etc/murakumo.yml.example +12 -0
- data/lib/cli/murakumo_options.rb +49 -0
- data/lib/misc/murakumo_const.rb +1 -1
- data/lib/srv/murakumo_balancer.rb +135 -0
- data/lib/srv/murakumo_cloud.rb +20 -25
- metadata +6 -5
data/README
CHANGED
@@ -27,7 +27,7 @@ https://bitbucket.org/winebarrel/murakumo
|
|
27
27
|
shell> mrkmctl -L
|
28
28
|
IP address TTL Priority Weight Activity Hostname
|
29
29
|
--------------- ------ -------- ------ -------- ----------
|
30
|
-
10.11.12.13 60 Origin
|
30
|
+
10.11.12.13 60 Origin - Active my-host
|
31
31
|
|
32
32
|
=== addition of a record
|
33
33
|
|
@@ -35,7 +35,7 @@ https://bitbucket.org/winebarrel/murakumo
|
|
35
35
|
shell> mrkmctl -L
|
36
36
|
IP address TTL Priority Weight Activity Hostname
|
37
37
|
--------------- ------ -------- ------ -------- ----------
|
38
|
-
10.11.12.13 60 Origin
|
38
|
+
10.11.12.13 60 Origin - Active my-host
|
39
39
|
10.11.12.13 300 Master 100 Active foo.bar
|
40
40
|
|
41
41
|
=== deletion of a record
|
@@ -44,7 +44,7 @@ https://bitbucket.org/winebarrel/murakumo
|
|
44
44
|
shell> mrkmctl -L
|
45
45
|
IP address TTL Priority Weight Activity Hostname
|
46
46
|
--------------- ------ -------- ------ -------- ----------
|
47
|
-
10.11.12.13 60 Origin
|
47
|
+
10.11.12.13 60 Origin - Active my-host
|
48
48
|
|
49
49
|
=== addition of a node
|
50
50
|
|
@@ -52,6 +52,6 @@ https://bitbucket.org/winebarrel/murakumo
|
|
52
52
|
shell> mrkmctl -L
|
53
53
|
IP address TTL Priority Weight Activity Hostname
|
54
54
|
--------------- ------ -------- ------ -------- ----------
|
55
|
-
10.11.12.13 60 Origin
|
56
|
-
10.11.12.14 60 Origin
|
55
|
+
10.11.12.13 60 Origin - Active my-host
|
56
|
+
10.11.12.14 60 Origin - Active other-host
|
57
57
|
|
data/etc/murakumo.server
CHANGED
@@ -77,6 +77,18 @@ max-ip-num: 8
|
|
77
77
|
#addr-includes: ^10\..*
|
78
78
|
#addr-excludes:
|
79
79
|
|
80
|
+
#balancing:
|
81
|
+
# ^app-.*: # destination alias regex
|
82
|
+
# algorithm: random # balancing algorithm
|
83
|
+
# max-ip-num: 1
|
84
|
+
# ^db-.*: # destination alias regex
|
85
|
+
# algorithm: fix_by_src # balancing algorithm
|
86
|
+
# sources: foo, bar # source alias name
|
87
|
+
# max-ip-num: 8
|
88
|
+
# ^cache-.*: # destination alias regex
|
89
|
+
# algorithm: fix_by_src2 # balancing algorithm
|
90
|
+
# sources: zoo # source alias name
|
91
|
+
|
80
92
|
#notification:
|
81
93
|
# host: my.smtp.server
|
82
94
|
# #port: 25
|
data/etc/murakumo.yml.example
CHANGED
@@ -17,6 +17,18 @@ max-ip-num: 8
|
|
17
17
|
#addr-includes: ^10\..*
|
18
18
|
#addr-excludes:
|
19
19
|
|
20
|
+
#balancing:
|
21
|
+
# ^app-.*: # destination alias regex
|
22
|
+
# algorithm: random # balancing algorithm
|
23
|
+
# max-ip-num: 1
|
24
|
+
# ^db-.*: # destination alias regex
|
25
|
+
# algorithm: fix_by_src # balancing algorithm
|
26
|
+
# sources: foo, bar # source alias name
|
27
|
+
# max-ip-num: 8
|
28
|
+
# ^cache-.*: # destination alias regex
|
29
|
+
# algorithm: fix_by_src2 # balancing algorithm
|
30
|
+
# sources: zoo # source alias name
|
31
|
+
|
20
32
|
#notification:
|
21
33
|
# host: my.smtp.server
|
22
34
|
# #port: 25
|
data/lib/cli/murakumo_options.rb
CHANGED
@@ -259,6 +259,55 @@ def murakumo_parse_args
|
|
259
259
|
end
|
260
260
|
end # {name,addr}-{includes,excludes}
|
261
261
|
|
262
|
+
# balancing
|
263
|
+
if options.config_file and (balancing = options.config_file['balancing'])
|
264
|
+
balancing.kind_of?(Hash) or parse_error('configuration of a balancing is not right')
|
265
|
+
balancing_h = options[:balancing] = {}
|
266
|
+
|
267
|
+
balancing.map {|k, v| [k.to_s.strip.downcase, v] }.each do |dest, attrs|
|
268
|
+
if dest.empty? or attrs.empty?
|
269
|
+
parse_error('configuration of a balancing is not right', dest)
|
270
|
+
end
|
271
|
+
|
272
|
+
unless attrs.kind_of?(Hash)
|
273
|
+
parse_error('configuration of a balancing is not right', dest)
|
274
|
+
end
|
275
|
+
|
276
|
+
attrs_algorithm = (attrs['algorithm'] || 'random').strip.downcase
|
277
|
+
attrs_max_ip_num = attrs['max-ip-num']
|
278
|
+
attrs_sources = (attrs['sources'] || '').strip.split(/\s*,\s*/).map {|i| i.strip }
|
279
|
+
|
280
|
+
unless %w(random fix_by_src fix_by_src2).include?(attrs_algorithm)
|
281
|
+
parse_error('configuration of a balancing is not right', dest)
|
282
|
+
end
|
283
|
+
|
284
|
+
unless attrs_max_ip_num.nil? or (/\A\d+\Z/ =~ attrs_max_ip_num.to_s and attrs_max_ip_num.to_i > 0)
|
285
|
+
parse_error('configuration of a balancing is not right', dest)
|
286
|
+
end
|
287
|
+
|
288
|
+
unless attrs_sources.empty? or attrs_sources.all? {|i| /\A[0-9a-z\.\-]+\Z/ =~ i }
|
289
|
+
parse_error('configuration of a balancing is not right', dest)
|
290
|
+
end
|
291
|
+
|
292
|
+
reg_dest = Regexp.new(dest, Regexp::IGNORECASE)
|
293
|
+
|
294
|
+
attrs_h = {
|
295
|
+
:algorithm => attrs_algorithm.to_sym,
|
296
|
+
:max_ip_num => (attrs_max_ip_num || options[:max_ip_num]).to_i
|
297
|
+
}
|
298
|
+
|
299
|
+
case attrs_algorithm
|
300
|
+
when 'random'
|
301
|
+
parse_error('configuration of a balancing is not right', dest) unless attrs_sources.empty?
|
302
|
+
when 'fix_by_src', 'fix_by_src2'
|
303
|
+
parse_error('configuration of a balancing is not right', dest) if attrs_sources.empty?
|
304
|
+
attrs_h[:sources] = attrs_sources
|
305
|
+
end
|
306
|
+
|
307
|
+
balancing_h[reg_dest] = attrs_h
|
308
|
+
end
|
309
|
+
end # balancing
|
310
|
+
|
262
311
|
end # after
|
263
312
|
|
264
313
|
error do |e|
|
data/lib/misc/murakumo_const.rb
CHANGED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'misc/murakumo_const'
|
2
|
+
|
3
|
+
module Murakumo
|
4
|
+
|
5
|
+
class Balancer
|
6
|
+
|
7
|
+
def initialize(hash, address, db, logger)
|
8
|
+
@hash = hash
|
9
|
+
@address = address
|
10
|
+
@db = db
|
11
|
+
@logger = logger
|
12
|
+
end
|
13
|
+
|
14
|
+
def sort(records, max_ip_num, name)
|
15
|
+
# ハッシュが空ならランダムで
|
16
|
+
if @hash.nil? or @hash.empty?
|
17
|
+
return random(records, max_ip_num)
|
18
|
+
end
|
19
|
+
|
20
|
+
# 宛先を検索
|
21
|
+
dest, attrs = @hash.find {|k, v| k =~ name }
|
22
|
+
|
23
|
+
if dest.nil? or attrs.nil?
|
24
|
+
# 設定が見つからない場合はとりあえずランダムで
|
25
|
+
return random(records, max_ip_num)
|
26
|
+
end
|
27
|
+
|
28
|
+
algo = attrs[:algorithm]
|
29
|
+
max_ip_num = [(attrs[:max_ip_num] || max_ip_num), records.length].min
|
30
|
+
sources = attrs[:sources]
|
31
|
+
|
32
|
+
case algo
|
33
|
+
when :random
|
34
|
+
random(records, max_ip_num)
|
35
|
+
when :fix_by_src
|
36
|
+
fix_by_src(records, max_ip_num, sources)
|
37
|
+
when :fix_by_src2
|
38
|
+
fix_by_src2(records, max_ip_num, sources)
|
39
|
+
else
|
40
|
+
# 未対応のアルゴリズムの場合はとりあえずランダムで返す
|
41
|
+
@logger.warn("distribution setup which is not right: #{[dest, algo].inspect}")
|
42
|
+
random(records, max_ip_num)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# 重み付きランダム(デフォルト)
|
49
|
+
def random(records, max_ip_num)
|
50
|
+
indices = []
|
51
|
+
buf = []
|
52
|
+
|
53
|
+
# インデックスをWeight分追加
|
54
|
+
records.each_with_index do |r, i|
|
55
|
+
weight = r['weight']
|
56
|
+
weight.times { buf << i }
|
57
|
+
end
|
58
|
+
|
59
|
+
# インデックスをシャッフル
|
60
|
+
buf = buf.sort_by{ rand }
|
61
|
+
|
62
|
+
# ランダムにインデックスを取り出す
|
63
|
+
loop do
|
64
|
+
indices << buf.shift
|
65
|
+
indices.uniq!
|
66
|
+
break if (indices.size >= max_ip_num or buf.empty?)
|
67
|
+
end
|
68
|
+
|
69
|
+
# インデックスのレコードを返す
|
70
|
+
records.values_at(*indices)
|
71
|
+
end
|
72
|
+
|
73
|
+
def fix_by_src(records, max_ip_num, src_aliases)
|
74
|
+
fix_by_src0(records, max_ip_num, src_aliases) do |new_records|
|
75
|
+
# そのまま評価
|
76
|
+
new_records.slice(0, max_ip_num)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def fix_by_src2(records, max_ip_num, src_aliases)
|
81
|
+
fix_by_src0(records, max_ip_num, src_aliases) do |new_records|
|
82
|
+
# 先頭 + ランダムを返す
|
83
|
+
first = new_records.shift
|
84
|
+
[first] + new_records.slice(0, max_ip_num - 1).sort_by { rand }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# ソースで宛先を固定
|
89
|
+
def fix_by_src0(records, max_ip_num, src_aliases)
|
90
|
+
joined = src_aliases.map {|i| "'#{i.downcase}'" }.join(',')
|
91
|
+
|
92
|
+
# ソースエイリアスでIPアドレスを探す
|
93
|
+
sources = @db.execute(<<-EOS, ACTIVE).map {|i| i['ip_address'] }.sort
|
94
|
+
SELECT ip_address FROM records
|
95
|
+
WHERE name IN (#{joined}) AND activity = ?
|
96
|
+
EOS
|
97
|
+
|
98
|
+
# ソースが見つからない場合はとりあえずランダムで
|
99
|
+
if sources.empty?
|
100
|
+
@logger.warn("source is not found: #{src_aliases.join(',')}")
|
101
|
+
return random(records, max_ip_num)
|
102
|
+
end
|
103
|
+
|
104
|
+
# ソースが自分を含んでいない場合はとりあえずランダムで
|
105
|
+
unless sources.include?(@address)
|
106
|
+
@logger.warn("sources does not contain self: #{@address}")
|
107
|
+
return random(records, max_ip_num)
|
108
|
+
end
|
109
|
+
|
110
|
+
# 宛先をソート
|
111
|
+
dests = (0...records.length).map {|i| [records[i]['ip_address'], i] }.sort_by {|a, b| a }
|
112
|
+
dests_orig = dests
|
113
|
+
|
114
|
+
# 数をそろえる
|
115
|
+
if sources.length < dests.length
|
116
|
+
dests = dests.slice(0, sources.length)
|
117
|
+
elsif sources.length > dests.length
|
118
|
+
dests = dests_orig = dests * (sources.length.to_f / dests.length).ceil
|
119
|
+
dests = dests.slice(0, sources.length)
|
120
|
+
end
|
121
|
+
|
122
|
+
# 先頭を決めてローテート
|
123
|
+
first_index = sources.zip(dests).index {|s, d| s == @address }
|
124
|
+
|
125
|
+
unless first_index.zero?
|
126
|
+
dests = (dests_orig + dests_orig).slice(first_index, dests.length)
|
127
|
+
end
|
128
|
+
|
129
|
+
# 先頭インデックスからレコードを並べ直す
|
130
|
+
yield(records.values_at(*dests.map {|addr, i| i }))
|
131
|
+
end # fix_by_src0
|
132
|
+
|
133
|
+
end # Balancer
|
134
|
+
|
135
|
+
end # Murakumo
|
data/lib/srv/murakumo_cloud.rb
CHANGED
@@ -3,6 +3,7 @@ require 'rgossip2'
|
|
3
3
|
require 'sqlite3'
|
4
4
|
|
5
5
|
require 'srv/murakumo_health_checker'
|
6
|
+
require 'srv/murakumo_balancer'
|
6
7
|
require 'misc/murakumo_const'
|
7
8
|
|
8
9
|
module Murakumo
|
@@ -97,6 +98,9 @@ module Murakumo
|
|
97
98
|
|
98
99
|
# キャッシュ
|
99
100
|
@cache = {} if options[:enable_cache]
|
101
|
+
|
102
|
+
# バランサー
|
103
|
+
@balancer = Balancer.new(@options[:balancing], @address, @db, @logger)
|
100
104
|
end
|
101
105
|
|
102
106
|
# Control of service
|
@@ -182,6 +186,10 @@ module Murakumo
|
|
182
186
|
hash[key] = @options.config_file[key]
|
183
187
|
end
|
184
188
|
end
|
189
|
+
|
190
|
+
if @options.config_file['balancing']
|
191
|
+
hash['balancing'] = @options.config_file['balancing']
|
192
|
+
end
|
185
193
|
end # 設定ファイルのみの項目
|
186
194
|
|
187
195
|
return hash
|
@@ -452,15 +460,21 @@ module Murakumo
|
|
452
460
|
return @address_records.map {|i| i.values_at('ip_address', 'ttl') }
|
453
461
|
end
|
454
462
|
|
463
|
+
# 名前は小文字に変換
|
464
|
+
name = name.downcase
|
465
|
+
|
466
|
+
# ドメインが指定されていたら削除
|
467
|
+
name.sub!(/\.#{Regexp.escape(@options[:domain])}\Z/i, '') if @options[:domain]
|
468
|
+
|
455
469
|
# 優先度の高いレコードを検索
|
456
|
-
records = shuffle_records(@address_records.select {|i| i['priority'] == MASTER })
|
470
|
+
records = shuffle_records(@address_records.select {|i| i['priority'] == MASTER }, name)
|
457
471
|
|
458
472
|
# 次に優先度の高いレコードを検索
|
459
|
-
records.concat(shuffle_records(@address_records.select {|i| i['priority'] == SECONDARY }))
|
473
|
+
records.concat(shuffle_records(@address_records.select {|i| i['priority'] == SECONDARY }, name))
|
460
474
|
|
461
475
|
# レコードが見つからなかった場合はバックアップを選択
|
462
476
|
if records.empty?
|
463
|
-
records = shuffle_records(@address_records.select {|i| i['priority'] == BACKUP })
|
477
|
+
records = shuffle_records(@address_records.select {|i| i['priority'] == BACKUP }, name)
|
464
478
|
end
|
465
479
|
|
466
480
|
# それでもレコードが見つからなかった場合はオリジンを選択
|
@@ -524,33 +538,14 @@ module Murakumo
|
|
524
538
|
private
|
525
539
|
|
526
540
|
# 乱数でレコードをシャッフルする
|
527
|
-
def shuffle_records(records)
|
541
|
+
def shuffle_records(records, name)
|
528
542
|
# レコードが1件以下の時はそのまま返す
|
529
543
|
return records if records.length <= 1
|
530
544
|
|
531
545
|
max_ip_num = [records.length, @options[:max_ip_num]].min
|
532
546
|
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
# インデックスをWeight分追加
|
537
|
-
records.each_with_index do |r, i|
|
538
|
-
weight = r['weight']
|
539
|
-
weight.times { buf << i }
|
540
|
-
end
|
541
|
-
|
542
|
-
# インデックスをシャッフル
|
543
|
-
buf = buf.sort_by{ rand }
|
544
|
-
|
545
|
-
# ランダムにインデックスを取り出す
|
546
|
-
loop do
|
547
|
-
indices << buf.shift
|
548
|
-
indices.uniq!
|
549
|
-
break if (indices.size >= max_ip_num or buf.empty?)
|
550
|
-
end
|
551
|
-
|
552
|
-
# インデックスのレコードを返す
|
553
|
-
records.values_at(*indices)
|
547
|
+
# バランサーでソートする
|
548
|
+
@balancer.sort(records, max_ip_num, name)
|
554
549
|
end
|
555
550
|
|
556
551
|
# リソースレコードのデータベース作成
|
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: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 8
|
10
|
+
version: 0.3.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- winebarrel
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-12-
|
18
|
+
date: 2011-12-10 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rubydns
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- lib/cli/mrkmctl_options.rb
|
102
102
|
- lib/cli/murakumo.rb
|
103
103
|
- lib/srv/murakumo_health_check_notifier.rb
|
104
|
+
- lib/srv/murakumo_balancer.rb
|
104
105
|
- lib/srv/murakumo_server.rb
|
105
106
|
- lib/srv/murakumo_health_checker.rb
|
106
107
|
- lib/srv/murakumo_cloud.rb
|
@@ -140,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
141
|
requirements: []
|
141
142
|
|
142
143
|
rubyforge_project:
|
143
|
-
rubygems_version: 1.8.
|
144
|
+
rubygems_version: 1.8.1
|
144
145
|
signing_key:
|
145
146
|
specification_version: 3
|
146
147
|
summary: Murakumo is the internal DNS server which manages name information using a gossip protocol.
|