murakumo 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -19,39 +19,39 @@ https://bitbucket.org/winebarrel/murakumo
19
19
  shell> murakumo-install-init-script
20
20
  shell> /etc/init.d/murakumo configure
21
21
  shell> /etc/init.d/murakumo start
22
- shell> dig @127.0.0.1 <any_hostname>
22
+ shell> dig @127.0.0.1 <hostname>
23
23
 
24
24
  == Example
25
25
  === display of a list of a record
26
26
 
27
27
  shell> mrkmctl -L
28
- IP address TTL Priority Activity Hostname
29
- --------------- ------ -------- -------- ----------
30
- 10.11.12.13 60 Origin Active my-host
28
+ IP address TTL Priority Weight Activity Hostname
29
+ --------------- ------ -------- ------ -------- ----------
30
+ 10.11.12.13 60 Origin 0 Active my-host
31
31
 
32
32
  === addition of a record
33
33
 
34
- shell> mrkmctl -A foo.bar,300,master
34
+ shell> mrkmctl -A foo.bar,300,master,100
35
35
  shell> mrkmctl -L
36
- IP address TTL Priority Activity Hostname
37
- --------------- ------ -------- -------- ----------
38
- 10.11.12.13 60 Origin Active my-host
39
- 10.11.12.13 300 Master Active foo.bar
36
+ IP address TTL Priority Weight Activity Hostname
37
+ --------------- ------ -------- ------ -------- ----------
38
+ 10.11.12.13 60 Origin 0 Active my-host
39
+ 10.11.12.13 300 Master 100 Active foo.bar
40
40
 
41
41
  === deletion of a record
42
42
 
43
43
  shell> mrkmctl -D foo.bar
44
44
  shell> mrkmctl -L
45
- IP address TTL Priority Activity Hostname
46
- --------------- ------ -------- -------- ----------
47
- 10.11.12.13 60 Origin Active my-host
45
+ IP address TTL Priority Weight Activity Hostname
46
+ --------------- ------ -------- ------ -------- ----------
47
+ 10.11.12.13 60 Origin 0 Active my-host
48
48
 
49
49
  === addition of a node
50
50
 
51
51
  shell> mrkmctl -a 10.11.12.14
52
52
  shell> mrkmctl -L
53
- IP address TTL Priority Activity Hostname
54
- --------------- ------ -------- -------- ----------
55
- 10.11.12.13 60 Origin Active my-host
56
- 10.11.12.14 60 Origin Active other-host
53
+ IP address TTL Priority Weight Activity Hostname
54
+ --------------- ------ -------- ------ -------- ----------
55
+ 10.11.12.13 60 Origin 0 Active my-host
56
+ 10.11.12.14 60 Origin 0 Active other-host
57
57
 
@@ -72,6 +72,11 @@ max-ip-num: 8
72
72
  #domain: ap-northeast-1.compute.internal
73
73
  #enable-cache: true
74
74
 
75
+ #name-includes: ^app-\d+$, ^db-\d+$
76
+ #name-excludes:
77
+ #addr-includes: ^10\..*
78
+ #addr-excludes:
79
+
75
80
  #notification:
76
81
  # host: my.smtp.server
77
82
  # #port: 25
@@ -85,13 +90,13 @@ max-ip-num: 8
85
90
  # #open_timeout: 30
86
91
  # #read_timeout: 60
87
92
 
88
- # Ip address, hostname, ttl
93
+ # ip address, hostname, ttl
89
94
  host: $ip_addr, $hostname, 60
90
95
 
91
- # alias hostname, ttl, master/secondary/backup
96
+ # alias hostname, ttl, master/secondary/backup, weight
92
97
  #alias:
93
- # - foo,60,master
94
- # - bar,60,backup
98
+ # - foo,60,master,100
99
+ # - bar,60,backup,100
95
100
 
96
101
  #health-check:
97
102
  # foo:
@@ -12,6 +12,11 @@ max-ip-num: 8
12
12
  #domain: ap-northeast-1.compute.internal
13
13
  #enable-cache: true
14
14
 
15
+ #name-includes: ^app-\d+$, ^db-\d+$
16
+ #name-excludes:
17
+ #addr-includes: ^10\..*
18
+ #addr-excludes:
19
+
15
20
  #notification:
16
21
  # host: my.smtp.server
17
22
  # #port: 25
@@ -25,13 +30,13 @@ max-ip-num: 8
25
30
  # #open_timeout: 30
26
31
  # #read_timeout: 60
27
32
 
28
- # Ip address, hostname, ttl
33
+ # ip address, hostname, ttl
29
34
  host: 10.11.12.13, my-host, 60
30
35
 
31
- # alias hostname, ttl, master/secondary/backup
36
+ # alias hostname, ttl, master/secondary/backup, weight
32
37
  alias:
33
- - foo,60,master
34
- - bar,60,master
38
+ - foo,60,master,100
39
+ - bar,60,master,100
35
40
 
36
41
  health-check:
37
42
  foo:
@@ -35,23 +35,23 @@ begin
35
35
  end
36
36
 
37
37
  r[3] = priority
38
- r[4] = (r[4] == Murakumo::ACTIVE ? 'Active' : 'Inactive')
38
+ r[5] = (r[5] == Murakumo::ACTIVE ? 'Active' : 'Inactive')
39
39
  end
40
40
 
41
41
  if arg.kind_of?(String)
42
- # 引数がある場合はフィルタリング(TTLを除く)
43
- records = records.select {|r| r.values_at(0, 1, 3, 4).any?{|i| i.to_s =~ /\A#{arg.to_s}/i } }
42
+ # 引数がある場合はフィルタリング(TTL、weightを除く)
43
+ records = records.select {|r| r.values_at(0, 1, 3, 5).any?{|i| i.to_s =~ /\A#{arg.to_s}/i } }
44
44
  end
45
45
 
46
46
  if records.empty?
47
47
  puts 'No macth'
48
48
  else
49
49
  puts <<-EOF
50
- IP address TTL Priority Activity Hostname
51
- --------------- ------ --------- -------- ----------
50
+ IP address TTL Priority Weight Activity Hostname
51
+ --------------- ------ --------- ------ -------- ----------
52
52
  EOF
53
53
  records.each do |r|
54
- puts '%-15s %6d %-9s %-8s %s' % r.values_at(0, 2, 3, 4, 1)
54
+ puts '%-15s %6d %-9s %6d %-8s %s' % r.values_at(0, 2, 3, 4, 5, 1)
55
55
  end
56
56
  end
57
57
 
@@ -9,12 +9,12 @@ def mrkmctl_parse_args
9
9
  desc 'displays a list of a record'
10
10
  option :list, '-L', '--list [SEARCH_PHRASE]'
11
11
 
12
- desc 'adds or updates a record: <hostname>[,<TTL>[,{master|secondary|backup}]]'
12
+ desc 'adds or updates a record: <hostname>[,<TTL>[,{master|secondary|backup}[,<weight>]]]'
13
13
  option :add, '-A', '--add RECORD', :type => Array, :multiple => true do |value|
14
- (1 <= value.length and value.length <= 3) or invalid_argument
14
+ (1 <= value.length and value.length <= 4) or invalid_argument
15
15
 
16
16
  value = value.map {|i| i.strip }
17
- hostname, ttl, priority = value
17
+ hostname, ttl, priority, weight = value
18
18
 
19
19
  # hostname
20
20
  /\A[0-9a-z\.\-]+\Z/i =~ hostname or invalid_argument
@@ -26,6 +26,11 @@ def mrkmctl_parse_args
26
26
 
27
27
  # Priority
28
28
  priority.nil? or /\A(master|secondary|backup)\Z/i =~ priority or invalid_argument
29
+
30
+ # Weight
31
+ unless weight.nil? or (/\A\d+\Z/ =~ weight and weight.to_i > 0)
32
+ invalid_argument
33
+ end
29
34
  end
30
35
 
31
36
  desc 'deletes a record'
@@ -80,7 +85,7 @@ def mrkmctl_parse_args
80
85
  if options[:add]
81
86
  options[:add] = options[:add].map do |r|
82
87
  r = r.map {|i| i ? i.to_s.strip : i }
83
- [nil, 60, 'master'].each_with_index {|v, i| r[i] ||= v }
88
+ [nil, 60, 'master', 100].each_with_index {|v, i| r[i] ||= v }
84
89
 
85
90
  priority = case r[2].to_s
86
91
  when /master/i
@@ -92,9 +97,10 @@ def mrkmctl_parse_args
92
97
  end
93
98
 
94
99
  [
95
- r[0], # name
100
+ r[0], # name
96
101
  r[1].to_i, # TTL
97
102
  priority,
103
+ r[3].to_i # weight
98
104
  ]
99
105
  end
100
106
  end
@@ -47,12 +47,12 @@ def murakumo_parse_args
47
47
  end
48
48
  end # :host
49
49
 
50
- desc 'resource record of an alias: <hostname>[,<TTL>[,{master|secondary|backup}]]'
50
+ desc 'resource record of an alias: <hostname>[,<TTL>[,{master|secondary|backup}[, <weight>]]]'
51
51
  option :aliases, '-A', '--alias RECORD', :type => Array, :multiple => true do |value|
52
- (1 <= value.length and value.length <= 3) or invalid_argument
52
+ (1 <= value.length and value.length <= 4) or invalid_argument
53
53
 
54
54
  value = value.map {|i| i.strip }
55
- hostname, ttl, priority = value
55
+ hostname, ttl, priority, weight = value
56
56
 
57
57
  # hostname
58
58
  /\A[0-9a-z\.\-]+\Z/ =~ hostname or invalid_argument
@@ -64,6 +64,11 @@ def murakumo_parse_args
64
64
 
65
65
  # Priority
66
66
  priority.nil? or /\A(master|secondary|backup)\Z/i =~ priority or invalid_argument
67
+
68
+ # Weight
69
+ unless weight.nil? or (/\A\d+\Z/ =~ weight and weight.to_i > 0)
70
+ invalid_argument
71
+ end
67
72
  end # :aliases
68
73
 
69
74
  desc 'ip address of a default resolver'
@@ -153,7 +158,7 @@ def murakumo_parse_args
153
158
 
154
159
  options[:aliases] = (options[:aliases] || []).map do |r|
155
160
  r = r.map {|i| i.to_s.strip }
156
- [nil, 60, 'master'].each_with_index {|v, i| r[i] ||= v }
161
+ [nil, 60, 'master', 100].each_with_index {|v, i| r[i] ||= v }
157
162
 
158
163
  priority = case r[2].to_s
159
164
  when /master/i
@@ -165,9 +170,10 @@ def murakumo_parse_args
165
170
  end
166
171
 
167
172
  [
168
- r[0], # name
173
+ r[0], # name
169
174
  r[1].to_i, # TTL
170
175
  priority,
176
+ r[3].to_i, # weight
171
177
  ]
172
178
  end
173
179
 
@@ -204,7 +210,7 @@ def murakumo_parse_args
204
210
  end
205
211
  end
206
212
  end
207
- end
213
+ end # health check
208
214
 
209
215
  # notification
210
216
  if options.config_file and (ntfc = options.config_file['notification'])
@@ -241,8 +247,19 @@ def murakumo_parse_args
241
247
 
242
248
  ntfc_h[:open_timeout] = ntfc['open_timeout'].to_i if ntfc['open_timeout']
243
249
  ntfc_h[:read_timeout] = ntfc['read_timeout'].to_i if ntfc['read_timeout']
244
- end
245
- end
250
+ end # notification
251
+
252
+ # {name,addr}-{includes,excludes}
253
+ if options.config_file
254
+ %w(name-includes name-excludes addr-includes addr-excludes).each do |key|
255
+ unless (reg_vals = (options.config_file[key] || '').strip).empty?
256
+ reg_vals = reg_vals.split(/\s*,\s*/).select {|i| not i.empty? }.map {|i| Regexp.new(i.strip, Regexp::IGNORECASE) }
257
+ options[key.gsub('-', '_').to_sym] = reg_vals
258
+ end
259
+ end
260
+ end # {name,addr}-{includes,excludes}
261
+
262
+ end # after
246
263
 
247
264
  error do |e|
248
265
  abort(e.message)
@@ -1,5 +1,5 @@
1
1
  module Murakumo
2
- VERSION = '0.3.5'
2
+ VERSION = '0.3.6'
3
3
 
4
4
  # Priority
5
5
  MASTER = 1
@@ -22,15 +22,15 @@ module Murakumo
22
22
  # リソースレコードからホストのアドレスとデータを取り出す
23
23
  host_data = options[:host]
24
24
  @address = host_data.shift
25
- host_data.concat [ORIGIN, ACTIVE]
25
+ host_data.concat [ORIGIN, 0, ACTIVE]
26
26
  alias_datas = options[:aliases].map {|r| r + [ACTIVE] }
27
27
  @logger = options[:logger]
28
28
 
29
29
  # 名前は小文字に変換
30
30
  datas = ([host_data] + alias_datas).map do |i|
31
- name, ttl, priority, activity = i
31
+ name, ttl, priority, weight, activity = i
32
32
  name = name.downcase
33
- [name, ttl, priority, activity]
33
+ [name, ttl, priority, weight, activity]
34
34
  end
35
35
 
36
36
  # データベースを作成してレコードを更新
@@ -157,29 +157,38 @@ module Murakumo
157
157
  end
158
158
  end
159
159
 
160
- records = list_records
160
+ records = list_records.select {|r| r[0] == @address }
161
161
 
162
162
  hash['host'] = records.find {|r| r[3] == ORIGIN }[0..2].join(',')
163
163
 
164
164
  aliases = records.select {|r| r[3] != ORIGIN }.map do |r|
165
- [r[1], r[2], (r[3] == MASTER ? 'master' : 'backup')].join(',')
165
+ [r[1], r[2], (r[3] == MASTER ? 'master' : 'backup'), r[4]].join(',')
166
166
  end
167
167
 
168
168
  hash['alias'] = aliases unless aliases.empty?
169
169
 
170
- if @options.config_file and @options.config_file['health-check']
171
- hash['health-check'] = @options.config_file['health-check']
172
- end
170
+ # 設定ファイルのみの項目
171
+ if @options.config_file
172
+ if @options.config_file['health-check']
173
+ hash['health-check'] = @options.config_file['health-check']
174
+ end
173
175
 
174
- if @options.config_file and @options.config_file['notification']
175
- hash['notification'] = @options.config_file['notification']
176
- end
176
+ if @options.config_file['notification']
177
+ hash['notification'] = @options.config_file['notification']
178
+ end
179
+
180
+ %w(name-includes name-excludes addr-includes addr-excludes).each do |key|
181
+ if @options.config_file[key]
182
+ hash[key] = @options.config_file[key]
183
+ end
184
+ end
185
+ end # 設定ファイルのみの項目
177
186
 
178
187
  return hash
179
188
  end
180
189
 
181
190
  def list_records
182
- columns = %w(ip_address name ttl priority activity)
191
+ columns = %w(ip_address name ttl priority weight activity)
183
192
 
184
193
  @db.execute(<<-EOS).map {|i| i.values_at(*columns) }
185
194
  SELECT #{columns.join(', ')} FROM records ORDER BY ip_address, name
@@ -191,9 +200,9 @@ module Murakumo
191
200
 
192
201
  # 名前は小文字に変換
193
202
  records = records.map do |i|
194
- name, ttl, priority = i
203
+ name, ttl, priority, weight = i
195
204
  name = name.downcase
196
- [name, ttl, priority]
205
+ [name, ttl, priority, weight]
197
206
  end
198
207
 
199
208
  @gossip.transaction do
@@ -213,6 +222,7 @@ module Murakumo
213
222
  # データを更新
214
223
  records = records.map {|r| r + [ACTIVE] }
215
224
  @gossip.data.concat(records)
225
+
216
226
  end # transaction
217
227
 
218
228
  # データベースを更新
@@ -327,7 +337,7 @@ module Murakumo
327
337
  return unless datas
328
338
 
329
339
  datas.each do |i|
330
- name, ttl, priority, activity = i
340
+ name, ttl, priority, weight, activity = i
331
341
 
332
342
  # 名前は小文字に変換
333
343
  name = name.downcase
@@ -337,9 +347,9 @@ module Murakumo
337
347
  @logger.warn('same hostname as origin was found')
338
348
  end
339
349
 
340
- @db.execute(<<-EOS, address, name, ttl, priority, activity)
341
- REPLACE INTO records (ip_address, name, ttl, priority, activity)
342
- VALUES (?, ?, ?, ?, ?)
350
+ @db.execute(<<-EOS, address, name, ttl, priority, weight, activity)
351
+ REPLACE INTO records (ip_address, name, ttl, priority, weight, activity)
352
+ VALUES (?, ?, ?, ?, ?, ?)
343
353
  EOS
344
354
  end
345
355
 
@@ -370,6 +380,15 @@ module Murakumo
370
380
  # Search of records
371
381
 
372
382
  def address_exist?(name)
383
+ # includes、excludesのチェック
384
+ if @options[:name_excludes] and @options[:name_excludes].any? {|r| r =~ name }
385
+ return false
386
+ end
387
+
388
+ if @options[:name_includes] and not @options[:name_includes].any? {|r| r =~ name }
389
+ return false
390
+ end
391
+
373
392
  # 名前は小文字に変換
374
393
  name = name.downcase
375
394
 
@@ -379,7 +398,7 @@ module Murakumo
379
398
  if @cache.nil?
380
399
  # キャッシュを設定していないときはいつもの処理
381
400
  @address_records = @db.execute(<<-EOS, name, ACTIVE) # シングルスレッドェ…
382
- SELECT ip_address, ttl, priority FROM records
401
+ SELECT ip_address, ttl, priority, weight FROM records
383
402
  WHERE name = ? AND activity = ?
384
403
  EOS
385
404
  else
@@ -395,7 +414,7 @@ module Murakumo
395
414
 
396
415
  # 普通に検索
397
416
  @address_records = @db.execute(<<-EOS, name, ACTIVE) # シングルスレッドェ…
398
- SELECT ip_address, ttl, priority FROM records
417
+ SELECT ip_address, ttl, priority, weight FROM records
399
418
  WHERE name = ? AND activity = ?
400
419
  EOS
401
420
 
@@ -405,13 +424,13 @@ module Murakumo
405
424
 
406
425
  # レコードはハッシュに変換する
407
426
  cache_records = @address_records.map do |i|
408
- ip_address, ttl, priority = i.values_at('ip_address', 'ttl', 'priority')
427
+ ip_address, ttl, priority, weight = i.values_at('ip_address', 'ttl', 'priority', 'weight')
409
428
 
410
429
  if min_ttl.nil? or ttl < min_ttl
411
430
  min_ttl = ttl
412
431
  end
413
432
 
414
- {'ip_address' => ip_address, 'ttl' => ttl, 'priority' => priority}
433
+ {'ip_address' => ip_address, 'ttl' => ttl, 'priority' => priority, 'weight' => weight}
415
434
  end
416
435
 
417
436
  # 最小値をExpire期限として設定
@@ -458,6 +477,15 @@ module Murakumo
458
477
  def name_exist?(address)
459
478
  address = x_ip_addr(address)
460
479
 
480
+ # includes、excludesのチェック
481
+ if @options[:addr_excludes] and @options[:addr_excludes].any? {|r| r =~ address }
482
+ return false
483
+ end
484
+
485
+ if @options[:addr_includes] and not @options[:addr_includes].any? {|r| r =~ address }
486
+ return false
487
+ end
488
+
461
489
  # シングルスレッドェ…
462
490
  @name_records = @db.execute(<<-EOS, address, ACTIVE)
463
491
  SELECT name, ttl, priority FROM records
@@ -497,15 +525,32 @@ module Murakumo
497
525
 
498
526
  # 乱数でレコードをシャッフルする
499
527
  def shuffle_records(records)
500
- # レコードが一件の時はそのまま返す
501
- return records if records.length == 1
528
+ # レコードが1件以下の時はそのまま返す
529
+ return records if records.length <= 1
502
530
 
503
- # 先頭のAレコードを決定
504
531
  max_ip_num = [records.length, @options[:max_ip_num]].min
505
- first_index = rand(records.length)
506
532
 
507
- # Aレコードを返す
508
- (records + records).slice(first_index, max_ip_num)
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)
509
554
  end
510
555
 
511
556
  # リソースレコードのデータベース作成
@@ -523,6 +568,7 @@ module Murakumo
523
568
  name TEXT NOT NULL,
524
569
  ttl INTEGER NOT NULL,
525
570
  priority INTEGER NOT NULL, /* MASTER:1, BACKUP:0, ORIGIN:-1 */
571
+ weight INTEGER NOT NULL, /* Origin:0, Other:>=1 */
526
572
  activity INTEGER NOT NULL, /* Active:1, Inactive:0 */
527
573
  PRIMARY KEY (ip_address, name)
528
574
  )
@@ -28,7 +28,7 @@ module Murakumo
28
28
 
29
29
  # HTTPチェッカー
30
30
  def http_get(path, statuses = [200], port = 80, host = '127.0.0.1')
31
- res = Net::HTTP.start('127.0.0.1', 80) do |http|
31
+ res = Net::HTTP.start(host, port) do |http|
32
32
  http.read_timeout = @options['timeout']
33
33
  http.get(path)
34
34
  end
@@ -73,7 +73,9 @@ module Murakumo
73
73
  sock = port_sock
74
74
  end
75
75
 
76
- my = Mysql.new(host, user, passwd, db, port, sock)
76
+ my = Mysql.init
77
+ my.options(Mysql::OPT_CONNECT_TIMEOUT, @options['timeout'])
78
+ my.connect(host, user, passwd, db, port, sock)
77
79
  !!(my.respond_to?(:ping) ? my.ping : my.stat)
78
80
  rescue => e
79
81
  @logger.debug("#{@name}: #{e.message}")
@@ -86,7 +88,7 @@ module Murakumo
86
88
  rescue LoadError
87
89
  end
88
90
 
89
- unless defined?(:mysql_check)
91
+ unless method_defined?(:mysql_check)
90
92
  begin
91
93
  require 'mysql2'
92
94
 
@@ -96,6 +98,7 @@ module Murakumo
96
98
  opts[:password] = passwd if passwd
97
99
  opts[:host] = host if host
98
100
  opts[:database] = db if db
101
+ opts[:connect_timeout] = @options['timeout']
99
102
 
100
103
  if port_sock.kind_of?(Integer)
101
104
  opts[:port] = port_sock
@@ -69,7 +69,7 @@ module Murakumo
69
69
  @cloud.gossip.transaction do
70
70
  @cloud.gossip.data.each do |i|
71
71
  # 名前の一致するデータを更新
72
- i[3] = activity if i[0] == @name
72
+ i[4] = activity if i[0] == @name
73
73
  end
74
74
  end
75
75
 
@@ -92,6 +92,7 @@ module Murakumo
92
92
  # look up PTR record
93
93
  match(@@cloud.method(:name_exist?), :PTR) do |transaction|
94
94
  name, ttl = @@cloud.lookup_name(transaction.name)
95
+ name += ".#{@@options[:domain]}" if @@options[:domain]
95
96
  transaction.respond!(Resolv::DNS::Name.create("#{name}."), :ttl => ttl)
96
97
  end
97
98
 
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: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 5
10
- version: 0.3.5
9
+ - 6
10
+ version: 0.3.6
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-11-30 00:00:00 Z
18
+ date: 2011-12-04 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rubydns
@@ -140,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
140
  requirements: []
141
141
 
142
142
  rubyforge_project:
143
- rubygems_version: 1.8.1
143
+ rubygems_version: 1.8.11
144
144
  signing_key:
145
145
  specification_version: 3
146
146
  summary: Murakumo is the internal DNS server which manages name information using a gossip protocol.