murakumo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+