murakumo 0.3.7 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
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 0 Active my-host
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 0 Active my-host
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 0 Active my-host
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 0 Active my-host
56
- 10.11.12.14 60 Origin 0 Active other-host
55
+ 10.11.12.13 60 Origin - Active my-host
56
+ 10.11.12.14 60 Origin - Active other-host
57
57
 
@@ -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
@@ -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
@@ -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|
@@ -1,5 +1,5 @@
1
1
  module Murakumo
2
- VERSION = '0.3.7'
2
+ VERSION = '0.3.8'
3
3
 
4
4
  # Priority
5
5
  MASTER = 1
@@ -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
@@ -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
- indices = []
534
- buf = []
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: 29
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 7
10
- version: 0.3.7
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-05 00:00:00 Z
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.11
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.