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 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.