murakumo 0.1.0

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.
@@ -0,0 +1,134 @@
1
+ require 'timeout'
2
+ require 'resolv-replace'
3
+
4
+ require 'srv/murakumo_health_checker_context'
5
+ require 'misc/murakumo_const'
6
+
7
+ module Murakumo
8
+
9
+ class HealthChecker
10
+
11
+ def initialize(name, cloud, logger, options)
12
+ # 名前は小文字に変換
13
+ @name = name.downcase
14
+ @cloud = cloud
15
+ @logger = logger
16
+
17
+ # 各種変数の設定
18
+ {
19
+ 'interval' => [30, 1, 300],
20
+ 'timeout' => [ 5, 1, 300],
21
+ 'healthy' => [10, 2, 10],
22
+ 'unhealthy' => [ 2, 2, 10],
23
+ }.each {|key, vals|
24
+ defval, min, max = vals
25
+ value = (options[key] || defval).to_i
26
+ value = min if value < min
27
+ value = max if value > max
28
+ instance_variable_set("@#{key}", value)
29
+ }
30
+
31
+ # スクリプトの読み込み
32
+ @script = options['script']
33
+ raise "health check script of #{@name} is not found" unless @script
34
+ @script = File.read(script) if File.exists?(@script)
35
+ end
36
+
37
+ def good
38
+ if @normal_health
39
+ @unhealthy_count = 0
40
+ elsif (@healthy_count += 1) >= @healthy
41
+ toggle_health
42
+ end
43
+ end
44
+
45
+ def bad
46
+ if not @normal_health
47
+ @healthy_count = 0
48
+ elsif (@unhealthy_count += 1) >= @unhealthy
49
+ toggle_health
50
+ end
51
+ end
52
+
53
+ def toggle_health
54
+ @normal_health = !@normal_health
55
+ activity = (@normal_health ? ACTIVE : INACTIVE)
56
+
57
+ @cloud.gossip.transaction do
58
+ @cloud.gossip.data.each do |i|
59
+ # 名前の一致するデータを更新
60
+ i[3] = activity if i[0] == @name
61
+ end
62
+ end
63
+
64
+ @cloud.db.execute(<<-EOS, activity, @cloud.address, @name)
65
+ UPDATE records SET activity = ?
66
+ WHERE ip_address = ? AND name = ?
67
+ EOS
68
+
69
+ @healthy_count = 0
70
+ @unhealthy_count = 0
71
+
72
+ status = @normal_health ? 'healthy' : 'unhealthy'
73
+ @logger.info("health condition changed: #{@name}: #{status}")
74
+ end
75
+
76
+ def start
77
+ # 各種変数は初期状態にする
78
+ @alive = true
79
+ @normal_health = true
80
+ @healthy_count = 0
81
+ @unhealthy_count = 0
82
+
83
+ # 既存のスレッドは破棄
84
+ if @thread and @thread.alive?
85
+ begin
86
+ @thread.kill
87
+ rescue ThreadError
88
+ end
89
+ end
90
+
91
+ @thread = Thread.start {
92
+ healthy = 0
93
+ unhealthy = 0
94
+
95
+ begin
96
+ while @alive
97
+ retval = nil
98
+
99
+ begin
100
+ retval = timeout(@timeout) {
101
+ HealthCheckerContext.new.instance_eval(@script)
102
+ }
103
+ rescue Timeout::Error
104
+ retval = false
105
+ end
106
+
107
+ status = retval == true ? 'good' : retval == false ? 'bad' : '-'
108
+ @logger.debug("result of a health check: #{@name}: #{status}")
109
+
110
+ if retval == true
111
+ good
112
+ elsif retval == false
113
+ bad
114
+ end
115
+
116
+ sleep @interval
117
+ end # while
118
+ rescue Exception => e
119
+ message = (["#{e.class}: #{e.message}"] + (e.backtrace || [])).join("\n\tfrom ")
120
+ @logger.error("#{@name}: #{message}")
121
+ end # begin
122
+ }
123
+ end
124
+
125
+ def stop
126
+ @alive = false
127
+ end
128
+
129
+ def alive?
130
+ @alive
131
+ end
132
+ end # HealthChecker
133
+
134
+ end # Murakumo
@@ -0,0 +1,33 @@
1
+ require 'net/http'
2
+ require 'socket'
3
+
4
+ require 'misc/murakumo_const'
5
+
6
+ module Murakumo
7
+
8
+ # ヘルスチェックのコンテキスト
9
+ class HealthCheckerContext
10
+
11
+ # TCPチェッカー
12
+ def tcp_check(port, host = '127.0.0.1')
13
+ s = TCPSocket.new(host, port)
14
+ s.close
15
+ return true
16
+ rescue Exception
17
+ return false
18
+ end
19
+
20
+ # HTTPチェッカー
21
+ def http_get(path, statuses = [200], host = '127.0.0.1', port = 80)
22
+ res = Net::HTTP.start('127.0.0.1', 80) do |http|
23
+ http.get(path)
24
+ end
25
+
26
+ statuses.include?(res.code.to_i)
27
+ rescue Exception
28
+ return false
29
+ end
30
+
31
+ end # HealthCheckerContext
32
+
33
+ end # Murakumo
@@ -0,0 +1,102 @@
1
+ require 'drb/drb'
2
+ require 'fileutils'
3
+ require 'rexec'
4
+ require 'rexec/daemon'
5
+ require 'rubydns'
6
+ require 'socket'
7
+
8
+ require 'srv/murakumo_cloud'
9
+
10
+ BasicSocket.do_not_reverse_lookup = true
11
+
12
+ module Murakumo
13
+
14
+ # RExecに依存しているのでこんな設計に…
15
+ class Server < RExec::Daemon::Base
16
+
17
+ # クラスメソッドを定義
18
+ class << self
19
+
20
+ def init(options)
21
+ # クラスインスタンス変数を使わないこと
22
+ @@options = options
23
+ @@cloud = Cloud.new(options)
24
+ end
25
+
26
+ def pid_directory=(path)
27
+ @@pid_directory = path
28
+ end
29
+
30
+ def run
31
+ RubyDNS.run_server(:listen => [[:udp, @@options[:dns_address], @@options[:dns_port]]]) do
32
+ # RubyDNS::Serverのコンテキスト
33
+ @logger = @@options[:logger] if @@options[:logger]
34
+
35
+ on(:start) do
36
+ if @@options[:socket]
37
+ # ServerクラスをDRuby化
38
+ DRb.start_service("drbunix:#{@@options[:socket]}", @@cloud)
39
+ at_exit { FileUtils.rm_f(@@options[:socket]) }
40
+ end
41
+
42
+ # HUPでログをローテート
43
+ if @@options[:logger]
44
+ trap(:HUP) do
45
+ if logger = @@options[:logger]
46
+ logdev = logger.instance_variable_get(:@logdev)
47
+
48
+ if (dev = logdev.dev).kind_of?(File)
49
+ path = dev.path
50
+ mutex = logdev.instance_variable_get(:@mutex)
51
+
52
+ mutex.synchronize do
53
+ dev.reopen(path, 'a')
54
+ dev.sync = true
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ # ゴシッププロトコルを開始
62
+ @@cloud.start
63
+ end
64
+
65
+ # look up A record
66
+ match(@@cloud.method(:address_exist?), :A) do |transaction|
67
+ records = @@cloud.lookup_addresses(transaction.name)
68
+
69
+ # 先頭のAレコードを決定
70
+ max_ip_num = [records.length, @@options[:max_ip_num]].min
71
+ first_index = rand(max_ip_num);
72
+
73
+ # Aレコードを返す
74
+ (records + records).slice(first_index, max_ip_num).each do |r|
75
+ address, ttl = r
76
+ transaction.respond!(address, :ttl => ttl)
77
+ end
78
+ end # match
79
+
80
+ # look up PTR record
81
+ match(@@cloud.method(:name_exist?), :PTR) do |transaction|
82
+ name, ttl = @@cloud.lookup_name(transaction.name)
83
+ transaction.respond!(Resolv::DNS::Name.create("#{name}."), :ttl => ttl)
84
+ end
85
+
86
+ if @@options[:resolver]
87
+ otherwise do |transaction|
88
+ transaction.passthrough!(@@options[:resolver])
89
+ end
90
+ end
91
+ end # RubyDNS.run_server
92
+ end # run
93
+
94
+ def shutdown
95
+ @@cloud.stop
96
+ end
97
+
98
+ end # class << self
99
+
100
+ end # Server
101
+
102
+ end # Murakumo
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: murakumo
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - winebarrel
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-11-16 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rubydns
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 21
29
+ segments:
30
+ - 0
31
+ - 3
32
+ - 3
33
+ version: 0.3.3
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rgossip2
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 17
45
+ segments:
46
+ - 0
47
+ - 1
48
+ - 5
49
+ version: 0.1.5
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: optopus
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 9
61
+ segments:
62
+ - 0
63
+ - 1
64
+ - 9
65
+ version: 0.1.9
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: sqlite3-ruby
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 21
77
+ segments:
78
+ - 1
79
+ - 2
80
+ - 5
81
+ version: 1.2.5
82
+ type: :runtime
83
+ version_requirements: *id004
84
+ description:
85
+ email: sgwr_dts@yahoo.co.jp
86
+ executables:
87
+ - murakumo
88
+ - mrkmctl
89
+ extensions: []
90
+
91
+ extra_rdoc_files: []
92
+
93
+ files:
94
+ - README
95
+ - bin/mrkmctl
96
+ - bin/murakumo
97
+ - lib/cli/murakumo_options.rb
98
+ - lib/cli/mrkmctl.rb
99
+ - lib/cli/mrkmctl_options.rb
100
+ - lib/cli/murakumo.rb
101
+ - lib/srv/murakumo_health_checker_context.rb
102
+ - lib/srv/murakumo_server.rb
103
+ - lib/srv/murakumo_health_checker.rb
104
+ - lib/srv/murakumo_cloud.rb
105
+ - lib/misc/murakumo_const.rb
106
+ - etc/murakumo.yml.sample
107
+ - etc/murakumo.server
108
+ homepage: https://bitbucket.org/winebarrel/murakumo
109
+ licenses: []
110
+
111
+ post_install_message:
112
+ rdoc_options: []
113
+
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ hash: 3
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ hash: 3
131
+ segments:
132
+ - 0
133
+ version: "0"
134
+ requirements: []
135
+
136
+ rubyforge_project:
137
+ rubygems_version: 1.8.1
138
+ signing_key:
139
+ specification_version: 3
140
+ summary: Murakumo is the internal DNS server which manages name information using a gossip protocol.
141
+ test_files: []
142
+