murakumo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,57 @@
1
+ = Murakumo
2
+
3
+ == Description
4
+
5
+ Murakumo is the internal DNS server which manages name information using a gossip protocol.
6
+
7
+ == Source Code
8
+
9
+ https://bitbucket.org/winebarrel/murakumo
10
+
11
+ == Dependency
12
+
13
+ * RubyDNS
14
+ * SQLite
15
+ * MessagePack
16
+
17
+ == Install
18
+
19
+ gem install murakumo
20
+ cp /usr/local/lib/ruby/gems/1.8/gems/murakumo-*/etc/murakumo.server /etc/init.d/murakumo
21
+ chmod 755 /etc/init.d/murakumo
22
+ /etc/init.d/murakumo start
23
+
24
+ == Example
25
+ === display of a list of a record
26
+
27
+ shell> mrkmctl -L
28
+ IP address TTL Priority Activity Hostname
29
+ --------------- ------ -------- -------- ----------
30
+ 10.11.12.13 60 Origin Active my-host
31
+
32
+ === addition of a record
33
+
34
+ shell> mrkmctl -A foo.bar,300,master
35
+ shell> mrkmctl -L
36
+ IP address TTL Priority Activity Hostname
37
+ --------------- ------ -------- -------- ----------
38
+ 10.11.12.13 60 Origin Active dev-01
39
+ 10.11.12.13 300 Master Active foo.bar
40
+
41
+ === deletion of a record
42
+
43
+ shell> mrkmctl -D foo.bar
44
+ shell> mrkmctl -L
45
+ IP address TTL Priority Activity Hostname
46
+ --------------- ------ -------- -------- ----------
47
+ 10.11.12.13 60 Origin Active my-host
48
+
49
+ === addition of a node
50
+
51
+ shell> mrkmctl -a 10.11.12.14
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
57
+
data/bin/mrkmctl ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems'
5
+ require 'cli/mrkmctl'
data/bin/murakumo ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems'
5
+ require 'cli/murakumo'
@@ -0,0 +1,95 @@
1
+ #!/bin/sh
2
+ # chkconfig: 345 64 36
3
+ # description: The internal DNS server which manages name information using a gossip protocol.
4
+ # processname: /usr/local/bin/murakumo
5
+ # config: /etc/murakumo.yml
6
+
7
+ prog=/usr/local/bin/murakumo
8
+ ctlprog=/usr/local/bin/mrkmctl
9
+ conf=/etc/murakumo.yml
10
+
11
+ if [ "$1" != "configure" -a ! -e "$conf" ]; then
12
+ echo "configuration file is not found: $conf"
13
+ exit 1
14
+ fi
15
+
16
+ case "$1" in
17
+ start)
18
+ $prog -c $conf -d start
19
+ ;;
20
+ stop)
21
+ $prog -c $conf -d stop
22
+ ;;
23
+ status)
24
+ $prog -c $conf -d status
25
+ ;;
26
+ restart)
27
+ $prog -c $conf -d restart
28
+ ;;
29
+ save)
30
+ $ctlprog -y > ${conf}.new
31
+
32
+ if [ $? -eq 0 ]; then
33
+ cp ${conf}.new $conf
34
+ echo "configuration file was updated: $conf"
35
+ fi
36
+
37
+ rm -f ${conf}.new
38
+ ;;
39
+ configure)
40
+ if [ -e "$conf" ]; then
41
+ echo "configuration file already exists."
42
+ exit 1
43
+ fi
44
+
45
+ bind_addr=0.0.0.0
46
+ ip_addr=`/sbin/ifconfig eth0 | awk -F'[: ]+' '/inet/{print $4}'`
47
+ hostname=`hostname`
48
+ resolver=`grep nameserver /etc/resolv.conf | fgrep -v 127.0.0.1 | head -n 1 | awk '{print $2}'`
49
+
50
+ if [ -n "$resolver" ]; then
51
+ resolver="$resolver, 8.8.8.8"
52
+ else
53
+ resolver="8.8.8.8"
54
+ fi
55
+
56
+ cat <<EOF > $conf
57
+ ---
58
+ address: $bind_addr
59
+ port: 53
60
+
61
+ auth-key: onion
62
+ log-level: info
63
+ resolver: $resolver
64
+ max-ip-number: 8
65
+
66
+ host: $ip_addr, $hostname, 60
67
+
68
+ #alias:
69
+ # - foo,60,master
70
+ # - bar,60,backup
71
+
72
+ #health-check:
73
+ # foo:
74
+ # interval: 5
75
+ # timeout: 5
76
+ # healthy: 2
77
+ # unhealthy: 2
78
+ # script: |
79
+ # tcp_check 80
80
+ # bar:
81
+ # interval: 5
82
+ # timeout: 5
83
+ # healthy: 2
84
+ # unhealthy: 2
85
+ # script: |
86
+ # http_get '/index.html'
87
+ EOF
88
+
89
+ echo "configuration file was created: $conf"
90
+ echo -e "\033[0;31mPlease set 127.0.0.1 to resolv.conf.\033[0;39m"
91
+ ;;
92
+ *)
93
+ echo $"Usage: $0 {start|stop|restart|status|save|configureh}"
94
+ exit 1
95
+ esac
@@ -0,0 +1,33 @@
1
+ ---
2
+ # configuration of dns
3
+ address: 0.0.0.0
4
+ port: 53
5
+
6
+ auth-key: onion
7
+ log-level: info
8
+ resolver: 8.8.8.8
9
+ max-ip-num: 8
10
+
11
+ # Ip address, hostname, ttl
12
+ host: 10.11.12.13, my-host, 60
13
+
14
+ # alias hostname, ttl, master/backup
15
+ alias:
16
+ - foo,60,master
17
+ - bar,60,master
18
+
19
+ health-check:
20
+ foo:
21
+ interval: 5
22
+ timeout: 5
23
+ healthy: 10
24
+ unhealthy: 2
25
+ script: |
26
+ tcp_check 80
27
+ bar:
28
+ interval: 5
29
+ timeout: 5
30
+ healthy: 10
31
+ unhealthy: 2
32
+ script: |
33
+ http_get '/index.html'
@@ -0,0 +1,74 @@
1
+ require 'drb/drb'
2
+ require 'yaml'
3
+
4
+ require 'cli/mrkmctl_options'
5
+ require 'misc/murakumo_const'
6
+
7
+ # オプションをパース
8
+ options = mrkmctl_parse_args
9
+
10
+ # リモートオブジェクトを生成
11
+ there = DRbObject.new_with_uri("drbunix:#{options[:socket]}")
12
+
13
+ cmd, arg = options[:command]
14
+
15
+ # 各コマンドの処理
16
+ begin
17
+ case cmd
18
+ # 一覧表示
19
+ when :list
20
+ records = if arg.kind_of?(String)
21
+ # 引数がある場合はフィルタリング
22
+ there.list_records.select {|r| r[0..1].any?{|i| i.start_with?(arg) } }
23
+ else
24
+ there.list_records
25
+ end
26
+
27
+ puts <<-EOF
28
+ IP address TTL Priority Activity Hostname
29
+ --------------- ------ -------- -------- ----------
30
+ EOF
31
+ records.each do |r|
32
+ r[3] = (r[3] == Murakumo::ORIGIN ? 'Origin' : r[3] == Murakumo::MASTER ? 'Master' : 'Backup')
33
+ r[4] = (r[4] == Murakumo::ACTIVE ? 'Active' : 'Inactive')
34
+ puts '%-15s %6d %-8s %-8s %s' % r.values_at(0, 2, 3, 4, 1)
35
+ end
36
+
37
+ # レコードの追加・更新
38
+ when :add
39
+ is_success, errmsg = there.add_or_rplace_records(arg)
40
+ is_success or raise(errmsg)
41
+
42
+ # レコードの削除
43
+ when :delete
44
+ is_success, errmsg = there.delete_records(arg)
45
+ is_success or raise(errmsg)
46
+
47
+ # ノードの追加
48
+ when :add_node
49
+ is_success, errmsg = there.add_nodes(arg)
50
+ is_success or raise(errmsg)
51
+
52
+ # ノードの削除
53
+ when :delete_node
54
+ is_success, errmsg = there.delete_nodes(arg)
55
+ is_success or raise(errmsg)
56
+
57
+ # 属性の取得
58
+ when :get
59
+ puts "#{arg}=#{there.get_attr(arg)}"
60
+
61
+ # 属性の設定
62
+ when :set
63
+ is_success, errmsg = there.set_attr(*arg)
64
+ is_success or raise(errmsg)
65
+
66
+ # 設定の出力
67
+ when :yaml
68
+ puts there.to_hash.to_yaml
69
+
70
+ end
71
+ rescue => e
72
+ $stderr.puts "error: #{e.message}"
73
+ exit 1
74
+ end
@@ -0,0 +1,122 @@
1
+ require 'optopus'
2
+
3
+ require 'misc/murakumo_const'
4
+
5
+ Version = '0.1.0'
6
+
7
+ def mrkmctl_parse_args
8
+ optopus do
9
+ desc 'displays a list of a record'
10
+ option :list, '-L', '--list [NAME]'
11
+
12
+ desc 'adds or updates a record: <hostname>[,<TTL>[,{master|backup}]]'
13
+ option :add, '-A', '--add RECORD', :type => Array, :multiple => true do |value|
14
+ (1 <= value.length and value.length <= 3) or invalid_argument
15
+
16
+ hostname, ttl, master_backup = value
17
+
18
+ # hostname
19
+ /\A[0-9a-z\.\-]+\Z/i =~ hostname or invalid_argument
20
+
21
+ # TTL
22
+ unless ttl.nil? or (/\A\d+\Z/ =~ ttl and ttl.to_i > 0)
23
+ invalid_argument
24
+ end
25
+
26
+ # MASTER or BACKUP
27
+ master_backup.nil? or /\A(master|backup)\Z/i =~ master_backup or invalid_argument
28
+ end
29
+
30
+ desc 'deletes a record'
31
+ option :delete, '-D', '--delete NAME', :multiple => true do |value|
32
+ /\A[0-9a-z\.\-]+\Z/i =~ value or invalid_argument
33
+ end
34
+
35
+ desc 'adds a node'
36
+ option :add_node, '-a', '--add-node HOST', :multiple => true do |value|
37
+ unless [/\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z/, /\A[0-9a-z\.\-]+\Z/i].any? {|i| i =~ value }
38
+ invalid_argument
39
+ end
40
+ end
41
+
42
+ desc 'deletes a node'
43
+ option :delete_node, '-d', '--delete-node HOST', :multiple => true do |value|
44
+ unless [/\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z/, /\A[0-9a-z\.\-]+\Z/i].any? {|i| i =~ value }
45
+ invalid_argument
46
+ end
47
+ end
48
+
49
+ desc "gets an attribute: #{Murakumo::ATTRIBUTES.keys.join(',')}"
50
+ option :get, '-g', '--get ATTR' do |value|
51
+ Murakumo::ATTRIBUTES.keys.include?(value.to_sym) or invalid_argument
52
+ end
53
+
54
+ desc "sets an attribute (name=value): #{Murakumo::ATTRIBUTES.keys.join(',')}"
55
+ option :set, '-s', '--set ATTR' do |value|
56
+ /\A.+=.+\Z/ =~ value or invalid_argument
57
+ name, val = value.split('=', 2)
58
+ Murakumo::ATTRIBUTES.keys.include?(name.to_sym) or invalid_argument
59
+
60
+ if name == 'log_level'
61
+ %w(debug info warn error fatal).include?(val) or invalid_argument
62
+ end
63
+ end
64
+
65
+ desc ' configuration file is outputted by yaml'
66
+ option :yaml, '-y', '--yaml'
67
+
68
+ desc 'path of a socket file'
69
+ option :socket, '-S', '--socket PATH', :default => '/var/tmp/murakumo.sock'
70
+
71
+ after do |options|
72
+ # add
73
+ if options[:add]
74
+ options[:add] = options[:add].map do |r|
75
+ r = r.map {|i| i ? i.to_s.strip : i }
76
+ [nil, 60, 'master'].each_with_index {|v, i| r[i] ||= v }
77
+
78
+ [
79
+ r[0], # name
80
+ r[1].to_i, # TTL
81
+ ((/master/i =~ r[2].to_s) ? Murakumo::MASTER : Murakumo::BACKUP),
82
+ ]
83
+ end
84
+ end
85
+
86
+ # 一応、uniq
87
+ [:delete, :add_node, :delete_node].each do |key|
88
+ if options[key]
89
+ options[key] = options[key].uniq
90
+ end
91
+ end
92
+
93
+ if options[:get]
94
+ options[:get] = options[:get].to_sym
95
+ end
96
+
97
+ if options[:set]
98
+ options[:set] = options[:set].split('=', 2)
99
+ options[:set][0] = options[:set][0].to_sym
100
+ end
101
+
102
+ # command
103
+ commands = [:list, :add, :delete, :add_node, :delete_node, :get, :set, :yaml].map {|k|
104
+ [k, options[k]]
105
+ }.select {|i| not i[1].nil? }
106
+
107
+ opt_keys = %w(-L -A -D --add-node --delete-node --get --set --yaml)
108
+
109
+ if commands.length < 1
110
+ parse_error('command is not specified', *opt_keys)
111
+ elsif commands.length > 1
112
+ parse_error('cannot use together', *opt_keys)
113
+ end
114
+
115
+ options[:command] = commands.first
116
+ end
117
+
118
+ error do |e|
119
+ abort(e.message)
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,20 @@
1
+ require 'cli/murakumo_options'
2
+ require 'srv/murakumo_server'
3
+
4
+ # オプションをパース
5
+ options = murakumo_parse_args
6
+
7
+ # サーバの初期化
8
+ Murakumo::Server.init(options)
9
+
10
+ if options[:daemon]
11
+ # デーモン化する場合
12
+ # RExecに処理を委譲するのでARGVの先頭にdaemonizeのコマンドを格納
13
+ ARGV.unshift options[:daemon].to_s
14
+
15
+ Murakumo::Server.pid_directory = options[:pid_dir]
16
+ Murakumo::Server.daemonize
17
+ else
18
+ # デーモン化しない場合
19
+ Murakumo::Server.run
20
+ end
@@ -0,0 +1,158 @@
1
+ require 'logger'
2
+ require 'optopus'
3
+ require 'resolv'
4
+ require 'socket'
5
+
6
+ require 'misc/murakumo_const'
7
+
8
+ Version = '0.1.0'
9
+
10
+ def murakumo_parse_args
11
+ optopus do
12
+ desc 'key for authentication (required)'
13
+ option :auth_key, '-K', '--auth-key STRING', :required => true
14
+
15
+ desc 'ip address to bind'
16
+ option :dns_address, '-a', '--address IP', :default => '0.0.0.0' do |value|
17
+ /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z/ =~ value or invalid_argument
18
+ end
19
+
20
+ desc 'port number of a name service'
21
+ option :dns_port, '-p', '--port NUM', :type => Integer, :default => 53
22
+
23
+ desc 'initial node list of gossip protocols'
24
+ option :initial_nodes, '-i', '--initial-nodes IP_LIST', :type => Array, :default => [] do |value|
25
+ value.all? {|i| /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z/ =~ i } or invalid_argument
26
+ end
27
+
28
+ desc "host's resource record : <ip_addr>[,<hostname>[,<TTL>]] (required)"
29
+ option :host, '-H', '--host RECORD', :type => Array, :required => true do |value|
30
+ (1 <= value.length and value.length <= 3) or invalid_argument
31
+
32
+ ip_addr, hostname, ttl = value
33
+
34
+ # ip address
35
+ /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z/ =~ ip_addr or invalid_argument
36
+
37
+ # hostname
38
+ /\A[0-9a-z\.\-]+\Z/i =~ hostname or invalid_argument
39
+
40
+ # TTL
41
+ unless ttl.nil? or (/\A\d+\Z/ =~ ttl and ttl.to_i > 0)
42
+ invalid_argument
43
+ end
44
+ end # :host
45
+
46
+ desc 'resource record of an alias: <hostname>[,<TTL>[,{master|backup}]]'
47
+ option :aliases, '-A', '--alias RECORD', :type => Array, :multiple => true do |value|
48
+ (1 <= value.length and value.length <= 3) or invalid_argument
49
+
50
+ hostname, ttl, master_backup = value
51
+
52
+ # hostname
53
+ /\A[0-9a-z\.\-]+\Z/ =~ hostname or invalid_argument
54
+
55
+ # TTL
56
+ unless ttl.nil? or (/\A\d+\Z/ =~ ttl and ttl.to_i > 0)
57
+ invalid_argument
58
+ end
59
+
60
+ # MASTER or BACKUP
61
+ master_backup.nil? or /\A(master|backup)\Z/i =~ master_backup or invalid_argument
62
+ end # :aliases
63
+
64
+ desc 'ip address of a default resolver'
65
+ option :resolver, '-r', '--resolver IP_LIST', :type => Array do |value|
66
+ unless value.all? {|i| /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z/ =~ i }
67
+ invalid_argument
68
+ end
69
+ end
70
+
71
+ desc 'path of a socket file'
72
+ option :socket, '-S', '--socket PATH', :default => '/var/tmp/murakumo.sock'
73
+
74
+ desc 'maximum number of the IP address returned as a response'
75
+ option :max_ip_num, '-n', '--max-ip-num NUM', :type => Integer, :default => 8 do |value|
76
+ invalid_argument if value < 1
77
+ end
78
+
79
+ desc 'command of daemonize: {start|stop|restart|status}'
80
+ option :daemon, '-d', '--daemon CMD', :type => [:start, :stop, :restart, :status]
81
+
82
+ desc 'directory of a pid file'
83
+ option :pid_dir, '-f', '--pid-dir PATH'
84
+
85
+ desc 'output path of a log'
86
+ option :log_path, '-l', '--log-path PATH'
87
+
88
+ desc 'output level of a log'
89
+ option :log_level, '-L', '--log-level LEVEL', :type => [:debug, :info, :warn, :error, :fatal], :default => :info
90
+
91
+ desc 'path of a configuration file'
92
+ config_file '-c', '--config PATH'
93
+
94
+ desc 'port number of a gossip service'
95
+ option :gossip_port, '-P', '--gossip-port NUM', :type => Integer, :default => 10870
96
+
97
+ desc 'lifetime of the node of a gossip protocol'
98
+ option :gossip_node_lifetime, '-T', '--gossip-node-lifetime NUM', :type => Integer, :default => 10
99
+
100
+ desc 'transmitting interval of a gossip protocol'
101
+ option :gossip_send_interval, '-I', '--gossip-send-interval NUM', :type => Float, :default => 0.3
102
+
103
+ desc 'reception timeout of a gossip protocol'
104
+ option :gossip_receive_timeout, '-O', '--gossip-receive-timeout NUM', :type => Integer, :default => 3
105
+
106
+ after do |options|
107
+ # resolver
108
+ if options[:resolver]
109
+ options[:resolver] = options[:resolver].map {|i| i.strip }
110
+ options[:resolver] = Resolv::DNS.new(:nameserver => options[:resolver])
111
+ end
112
+
113
+ # initial nodes
114
+ if options[:initial_nodes]
115
+ options[:initial_nodes] = options[:initial_nodes].map {|i| i.strip }
116
+ end
117
+
118
+ # host
119
+ options[:host] = options[:host].map {|i| i.strip }
120
+ options[:host][1] ||= Socket.gethostname
121
+ options[:host][2] = (options[:host][2] || 60).to_i # TTL
122
+
123
+ # aliases
124
+ config_file_aliases = options.config_file ? options.config_file['alias'] : nil
125
+
126
+ if config_file_aliases
127
+ if config_file_aliases.kind_of?(Array)
128
+ options[:aliases] = config_file_aliases.map {|i| i.split(',') }
129
+ else
130
+ options[:aliases] = [options[:aliases]]
131
+ end
132
+ end
133
+
134
+ options[:aliases] = (options[:aliases] || []).map do |r|
135
+ r = r.map {|i| i.to_s.strip }
136
+ [nil, 60, 'master'].each_with_index {|v, i| r[i] ||= v }
137
+
138
+ [
139
+ r[0], # name
140
+ r[1].to_i, # TTL
141
+ ((/master/i =~ r[2].to_s) ? Murakumo::MASTER : Murakumo::BACKUP),
142
+ ]
143
+ end
144
+
145
+ # logger
146
+ if not options[:log_path] and options[:daemon]
147
+ options[:log_path] = '/var/log/murakumo.log'
148
+ end
149
+
150
+ options[:logger] = Logger.new(options[:log_path] || $stderr)
151
+ options[:logger].level = Logger.const_get(options[:log_level].to_s.upcase)
152
+ end
153
+
154
+ error do |e|
155
+ abort(e.message)
156
+ end
157
+ end
158
+ end