wakame-dolphin 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +4 -0
- data/.travis.yml +25 -0
- data/Gemfile +18 -0
- data/Makefile +56 -0
- data/README.md +77 -0
- data/Rakefile +67 -0
- data/bin/dolphin_server +98 -0
- data/config/db/cassandra_clear.txt +1 -0
- data/config/db/cassandra_schema.txt +14 -0
- data/config/db/sequel/migrations/0001_add_notification.rb +15 -0
- data/config/db/sequel/migrations/0002_add_event.rb +15 -0
- data/config/dolphin-mysql.conf.travis +26 -0
- data/config/dolphin.conf.example +26 -0
- data/lib/dolphin.rb +148 -0
- data/lib/dolphin/data_store.rb +55 -0
- data/lib/dolphin/data_stores/base_rdb.rb +57 -0
- data/lib/dolphin/data_stores/cassandra.rb +98 -0
- data/lib/dolphin/data_stores/mysql.rb +31 -0
- data/lib/dolphin/helpers/message/zabbix_helper.rb +16 -0
- data/lib/dolphin/helpers/request_helper.rb +72 -0
- data/lib/dolphin/mailer.rb +83 -0
- data/lib/dolphin/manager.rb +35 -0
- data/lib/dolphin/message_builder.rb +98 -0
- data/lib/dolphin/models/base.rb +8 -0
- data/lib/dolphin/models/cassandra/base.rb +11 -0
- data/lib/dolphin/models/cassandra/event.rb +42 -0
- data/lib/dolphin/models/cassandra/notification.rb +28 -0
- data/lib/dolphin/models/rdb/base.rb +12 -0
- data/lib/dolphin/models/rdb/event.rb +47 -0
- data/lib/dolphin/models/rdb/notification.rb +27 -0
- data/lib/dolphin/models/rdb/orm/base.rb +10 -0
- data/lib/dolphin/models/rdb/orm/event.rb +8 -0
- data/lib/dolphin/models/rdb/orm/notification.rb +8 -0
- data/lib/dolphin/query_processor.rb +50 -0
- data/lib/dolphin/request_handler.rb +150 -0
- data/lib/dolphin/sender.rb +47 -0
- data/lib/dolphin/util.rb +18 -0
- data/lib/dolphin/version.rb +3 -0
- data/lib/dolphin/worker.rb +149 -0
- data/script/console +13 -0
- data/spec/files/cassandra_models_spec.rb +127 -0
- data/spec/files/dolphin_spec.rb +110 -0
- data/spec/files/endpoint/event_spec.rb +123 -0
- data/spec/files/endpoint/notification_spec.rb +54 -0
- data/spec/files/message_builder_spec.rb +15 -0
- data/spec/helpers/test_helper.rb +21 -0
- data/spec/helpers/web_request_helper.rb +40 -0
- data/spec/spec_helper.rb +18 -0
- data/templates/email/alert_port.erb +24 -0
- data/templates/email/default.erb +3 -0
- data/tests/test_dolphin +10 -0
- data/tests/test_get_event +31 -0
- data/tests/test_get_notification +27 -0
- data/tests/test_post_event +37 -0
- data/tests/test_post_notification +43 -0
- data/wakame-dolphin.gemspec +40 -0
- metadata +311 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
[server]
|
2
|
+
host = 127.0.0.1
|
3
|
+
port = 9004
|
4
|
+
|
5
|
+
[agent]
|
6
|
+
request_handler = 1
|
7
|
+
query_processor = 1
|
8
|
+
worker = 1
|
9
|
+
sender = 1
|
10
|
+
|
11
|
+
[database]
|
12
|
+
adapter = cassandra
|
13
|
+
hosts = 127.0.0.1
|
14
|
+
port = 9160
|
15
|
+
max_retry_count = 3
|
16
|
+
retry_interval = 3
|
17
|
+
|
18
|
+
[mail]
|
19
|
+
from = demo@example.com
|
20
|
+
# host = localhost
|
21
|
+
# port = 25
|
22
|
+
type = file
|
23
|
+
|
24
|
+
[logger]
|
25
|
+
format = human_readable
|
26
|
+
# format = ltsv
|
data/lib/dolphin.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'parseconfig'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'extlib/blank'
|
6
|
+
|
7
|
+
Signal.trap(:INT, "EXIT")
|
8
|
+
|
9
|
+
$LOAD_PATH.unshift File.expand_path('../', __FILE__)
|
10
|
+
|
11
|
+
module Dolphin
|
12
|
+
def self.settings(config='')
|
13
|
+
|
14
|
+
if @settings.nil?
|
15
|
+
|
16
|
+
# overwrite
|
17
|
+
if !ENV['CONFIG_FILE'].blank?
|
18
|
+
config = File.join(Dolphin.config_path, ENV['CONFIG_FILE'])
|
19
|
+
elsif config.blank?
|
20
|
+
config = File.join(Dolphin.config_path, 'dolphin.conf')
|
21
|
+
end
|
22
|
+
|
23
|
+
if !File.exists?(config)
|
24
|
+
puts "File not found #{config}"
|
25
|
+
exit!
|
26
|
+
end
|
27
|
+
|
28
|
+
# TODO:validation
|
29
|
+
@config = config
|
30
|
+
@settings = ParseConfig.new(config)
|
31
|
+
end
|
32
|
+
@settings
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.config
|
36
|
+
@config
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.root_path
|
40
|
+
File.expand_path('../../', __FILE__)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.templates_path
|
44
|
+
File.join(root_path, '/templates')
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.config_path
|
48
|
+
File.join(root_path, '/config')
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.db_path
|
52
|
+
File.join(config_path, '/db')
|
53
|
+
end
|
54
|
+
|
55
|
+
class EventObject < OpenStruct;end
|
56
|
+
class NotificationObject < OpenStruct; end
|
57
|
+
class ResponseObject
|
58
|
+
attr_accessor :message
|
59
|
+
def initialize
|
60
|
+
@success = nil
|
61
|
+
@message = ''
|
62
|
+
end
|
63
|
+
|
64
|
+
def success!
|
65
|
+
@success = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def success?
|
69
|
+
warn 'Does not happened anything.' if @success.nil?
|
70
|
+
@success === true
|
71
|
+
end
|
72
|
+
|
73
|
+
def fail!
|
74
|
+
@success = false
|
75
|
+
end
|
76
|
+
|
77
|
+
def fail?
|
78
|
+
warn 'Does not happened anything.' if @success.nil?
|
79
|
+
@success === false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class FailureObject < ResponseObject
|
84
|
+
def initialize(message = '')
|
85
|
+
fail!
|
86
|
+
@message = message
|
87
|
+
freeze
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class SuccessObject < ResponseObject
|
92
|
+
def initialize(message = '')
|
93
|
+
success!
|
94
|
+
@message = message
|
95
|
+
freeze
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
autoload :VERSION, 'dolphin/version'
|
100
|
+
|
101
|
+
autoload :Util, 'dolphin/util'
|
102
|
+
autoload :MessageBuilder, 'dolphin/message_builder'
|
103
|
+
autoload :TemplateBuilder, 'dolphin/message_builder'
|
104
|
+
autoload :Mailer, 'dolphin/mailer'
|
105
|
+
autoload :DataStore, 'dolphin/data_store'
|
106
|
+
|
107
|
+
module Models
|
108
|
+
autoload :Base, 'dolphin/models/base'
|
109
|
+
module Cassandra
|
110
|
+
autoload :Base, 'dolphin/models/cassandra/base'
|
111
|
+
autoload :Event, 'dolphin/models/cassandra/event'
|
112
|
+
autoload :Notification, 'dolphin/models/cassandra/notification'
|
113
|
+
end
|
114
|
+
|
115
|
+
module Rdb
|
116
|
+
autoload :Base, 'dolphin/models/rdb/base'
|
117
|
+
autoload :Event, 'dolphin/models/rdb/event'
|
118
|
+
autoload :Notification, 'dolphin/models/rdb/notification'
|
119
|
+
module Orm
|
120
|
+
autoload :Event, 'dolphin/models/rdb/orm/event'
|
121
|
+
autoload :Notification, 'dolphin/models/rdb/orm/notification'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Helpers
|
127
|
+
autoload :RequestHelper, 'dolphin/helpers/request_helper'
|
128
|
+
module Message
|
129
|
+
autoload :ZabbixHelper, 'dolphin/helpers/message/zabbix_helper'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
module DataStore
|
134
|
+
autoload :Cassandra, 'dolphin/data_stores/cassandra'
|
135
|
+
autoload :Mysql, 'dolphin/data_stores/mysql'
|
136
|
+
autoload :BaseRdb, 'dolphin/data_stores/base_rdb'
|
137
|
+
end
|
138
|
+
|
139
|
+
# Celluloid supervisor
|
140
|
+
autoload :Manager, 'dolphin/manager'
|
141
|
+
|
142
|
+
# Celluloid actors
|
143
|
+
autoload :RequestHandler, 'dolphin/request_handler'
|
144
|
+
autoload :Worker, 'dolphin/worker'
|
145
|
+
autoload :QueryProcessor, 'dolphin/query_processor'
|
146
|
+
autoload :Sender, 'dolphin/sender'
|
147
|
+
|
148
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Dolphin
|
2
|
+
module DataStore
|
3
|
+
include Dolphin::Util
|
4
|
+
|
5
|
+
DATABASE = 'dolphin'.freeze
|
6
|
+
|
7
|
+
def self.current_store
|
8
|
+
create(Dolphin.settings['database']['adapter'].to_sym)
|
9
|
+
end
|
10
|
+
|
11
|
+
def hosts
|
12
|
+
Dolphin.settings['database']['hosts']
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create(adapter)
|
16
|
+
config = {}
|
17
|
+
case adapter
|
18
|
+
when :cassandra
|
19
|
+
klass = Dolphin::DataStore::Cassandra
|
20
|
+
config.merge!({
|
21
|
+
:keyspace => DATABASE,
|
22
|
+
:hosts => Dolphin.settings['database']['hosts'],
|
23
|
+
:port => Dolphin.settings['database']['port'],
|
24
|
+
:max_retry_count => Dolphin.settings['database']['max_retry_count'].to_i,
|
25
|
+
:retry_interval => Dolphin.settings['database']['retry_interval'].to_i
|
26
|
+
})
|
27
|
+
when :mysql
|
28
|
+
klass = Dolphin::DataStore::Mysql
|
29
|
+
config.merge!({
|
30
|
+
:adapter => 'mysql2',
|
31
|
+
:database => DATABASE,
|
32
|
+
:host => Dolphin.settings['database']['host'],
|
33
|
+
:port => Dolphin.settings['database']['port'],
|
34
|
+
:user => Dolphin.settings['database']['user'],
|
35
|
+
:password => Dolphin.settings['database']['password'],
|
36
|
+
})
|
37
|
+
else
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
klass.new(config)
|
41
|
+
end
|
42
|
+
|
43
|
+
class ConncetionBase
|
44
|
+
include Dolphin::Util
|
45
|
+
|
46
|
+
def connect
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
def path
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Dolphin
|
2
|
+
module DataStore
|
3
|
+
class BaseRdb
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@adapter = config[:adapter]
|
7
|
+
@host = config[:host]
|
8
|
+
@user = config[:user]
|
9
|
+
@password = config[:password]
|
10
|
+
@database = config[:database]
|
11
|
+
|
12
|
+
# Set timezone to UTC
|
13
|
+
Sequel.default_timezone = :utc
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_database
|
17
|
+
Sequel.connect(connect_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def closed?
|
21
|
+
@connection.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
ORM = Dolphin::Models::Rdb::Orm
|
25
|
+
|
26
|
+
def get_notification(id)
|
27
|
+
n = Dolphin::Models::Rdb::Notification.new(ORM::Notification)
|
28
|
+
n.get(id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def put_event(event)
|
32
|
+
e = Dolphin::Models::Rdb::Event.new(ORM::Event)
|
33
|
+
e.put(event)
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_event(params)
|
37
|
+
e = Dolphin::Models::Rdb::Event.new(ORM::Event)
|
38
|
+
e.get(params)
|
39
|
+
end
|
40
|
+
|
41
|
+
def put_notification(id, methods)
|
42
|
+
n = Dolphin::Models::Rdb::Notification.new(ORM::Notification)
|
43
|
+
n.put(id, methods)
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_notification(notification)
|
47
|
+
n = Dolphin::Models::Rdb::Notification.new(ORM::Notification)
|
48
|
+
n.delete(notification)
|
49
|
+
end
|
50
|
+
|
51
|
+
def connect_path
|
52
|
+
"#{@adapter}://#{@host}/#{@database}?user=#{@user}&password=#{@password}"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'cassandra/1.1'
|
4
|
+
|
5
|
+
module Thrift
|
6
|
+
class FramedTransport < BaseTransport
|
7
|
+
def write(buf,sz=nil)
|
8
|
+
if !['US-ASCII', 'ASCII-8BIT'].include?(buf.encoding.to_s)
|
9
|
+
buf = buf.unpack("a*").first
|
10
|
+
end
|
11
|
+
return @transport.write(buf) unless @write
|
12
|
+
|
13
|
+
@wbuf << (sz ? buf[0...sz] : buf)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Dolphin
|
19
|
+
module DataStore
|
20
|
+
class Cassandra
|
21
|
+
include Dolphin::Util
|
22
|
+
|
23
|
+
class UnAvailableNodeException < Exception; end
|
24
|
+
|
25
|
+
PATH_SEPARATOR = ':'.freeze
|
26
|
+
|
27
|
+
def initialize(config)
|
28
|
+
@keyspace = config[:keyspace]
|
29
|
+
raise "database hosts is blank" if config[:hosts].blank?
|
30
|
+
@hosts = config[:hosts].split(',')
|
31
|
+
@port = config[:port]
|
32
|
+
@max_retry_count = config[:max_retry_count] || 3
|
33
|
+
@retry_interval = config[:retry_interval] || 3
|
34
|
+
@retry_count = 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def connect
|
38
|
+
begin
|
39
|
+
if @connection.nil?
|
40
|
+
@connection = ::Cassandra.new(@keyspace, seeds)
|
41
|
+
|
42
|
+
# test connecting..
|
43
|
+
@connection.ring
|
44
|
+
return @connection
|
45
|
+
end
|
46
|
+
rescue ThriftClient::NoServersAvailable => e
|
47
|
+
logger :error, e
|
48
|
+
@connection = nil
|
49
|
+
if @retry_count < @max_retry_count
|
50
|
+
@retry_count += 1
|
51
|
+
logger :error, "retry connection..#{@retry_count}"
|
52
|
+
sleep @retry_interval
|
53
|
+
retry
|
54
|
+
end
|
55
|
+
rescue UnAvailableNodeException => e
|
56
|
+
logger :error, e
|
57
|
+
rescue CassandraThrift::InvalidRequestException => e
|
58
|
+
logger :error, e
|
59
|
+
end
|
60
|
+
@connection
|
61
|
+
end
|
62
|
+
|
63
|
+
def closed?
|
64
|
+
@connection.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_notification(id)
|
68
|
+
n = Dolphin::Models::Cassandra::Notification.new(@connection)
|
69
|
+
n.get(id)
|
70
|
+
end
|
71
|
+
|
72
|
+
def put_event(event)
|
73
|
+
e = Dolphin::Models::Cassandra::Event.new(@connection)
|
74
|
+
e.put(event)
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_event(params)
|
78
|
+
e = Dolphin::Models::Cassandra::Event.new(@connection)
|
79
|
+
e.get(params)
|
80
|
+
end
|
81
|
+
|
82
|
+
def put_notification(id, methods)
|
83
|
+
n = Dolphin::Models::Cassandra::Notification.new(@connection)
|
84
|
+
n.put(id, methods)
|
85
|
+
end
|
86
|
+
|
87
|
+
def delete_notification(notification)
|
88
|
+
n = Dolphin::Models::Cassandra::Notification.new(@connection)
|
89
|
+
n.delete(notification)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def seeds
|
94
|
+
@hosts.collect{|host| "#{host}:#{@port}"}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'sequel'
|
4
|
+
|
5
|
+
module Dolphin
|
6
|
+
module DataStore
|
7
|
+
class Mysql < BaseRdb
|
8
|
+
def connect
|
9
|
+
@connection = current_database
|
10
|
+
case @connection.adapter_scheme
|
11
|
+
when :mysql, :mysql2
|
12
|
+
Sequel::MySQL.default_charset = 'utf8'
|
13
|
+
Sequel::MySQL.default_collate = 'utf8_general_ci'
|
14
|
+
Sequel::MySQL.default_engine = ENV['MYSQL_DB_ENGINE'] || 'InnoDB'
|
15
|
+
|
16
|
+
# this is the mysql adapter specific constants. won't work with mysql2.
|
17
|
+
if @connection.adapter_scheme == :mysql
|
18
|
+
# Disable TEXT to Sequel::SQL::Blob translation.
|
19
|
+
# see the thread: MySQL text turning into blobs
|
20
|
+
# http://groups.google.com/group/sequel-talk/browse_thread/thread/d0f4c85abe9b3227/9ceaf291f90111e6
|
21
|
+
# lib/sequel/adapters/mysql.rb
|
22
|
+
[249, 250, 251, 252].each { |v|
|
23
|
+
Sequel::MySQL::MYSQL_TYPES.delete(v)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@connection
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'multi_json'
|
3
|
+
require 'extlib/blank'
|
4
|
+
|
5
|
+
module Dolphin
|
6
|
+
module Helpers
|
7
|
+
module RequestHelper
|
8
|
+
|
9
|
+
def attach_request_params(request)
|
10
|
+
raise "Unsuppoted Content-Type: #{request.env['Content-Type']}" unless request.env['Content-Type'] === 'application/json'
|
11
|
+
@params = {}
|
12
|
+
@notification_id = request.env['X-Notification-Id']
|
13
|
+
@message_type = request.env['X-Message-Type']
|
14
|
+
case request.method
|
15
|
+
when "POST"
|
16
|
+
v = request.input.to_a[0]
|
17
|
+
@params = MultiJson.load(v)
|
18
|
+
when "GET"
|
19
|
+
@params = parse_query_string(request.env["QUERY_STRING"])
|
20
|
+
end
|
21
|
+
@params
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(request, &blk)
|
25
|
+
begin
|
26
|
+
attach_request_params(request)
|
27
|
+
logger :info, @params
|
28
|
+
blk.call
|
29
|
+
rescue LoadError => e
|
30
|
+
logger :error, e
|
31
|
+
[400, {}, MultiJson.dump({
|
32
|
+
:message => 'Request faild.'
|
33
|
+
})]
|
34
|
+
rescue => e
|
35
|
+
logger :error, e
|
36
|
+
[400, {}, MultiJson.dump({
|
37
|
+
:message => e.message
|
38
|
+
})]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def respond_with(data)
|
43
|
+
[200, {}, MultiJson.dump(data)]
|
44
|
+
end
|
45
|
+
|
46
|
+
def required(name)
|
47
|
+
case name
|
48
|
+
when 'notification_id'
|
49
|
+
raise 'Not found X-Notification-Id' if @notification_id.nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def parse_query_string(query_string)
|
55
|
+
params = {}
|
56
|
+
unless query_string.blank?
|
57
|
+
parts = query_string.split('&')
|
58
|
+
parts.collect do |part|
|
59
|
+
key, value = part.split('=')
|
60
|
+
params.store(key, value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
params
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_time(time)
|
67
|
+
return nil if time.blank? || !time.is_a?(String)
|
68
|
+
Time.parse(URI.decode(time)).to_time
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|