pomelo-citrus-admin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a3cf2960e29ed5e8afa845d8d11e7e19ed7ab69c
4
+ data.tar.gz: f06a63aa30a903d6781370a7bbae0848d5b44c52
5
+ SHA512:
6
+ metadata.gz: 5afb2d8016d05f06e079407be8565b0636fe155ac4164764b3236bd3c89ff673668ee53d1156fff3d16a9fcb26aea7715ddc0da1a97e510e1d13e0445854bd93
7
+ data.tar.gz: fead007deb6e5bcdae62f3b19ce7b9a499919f35607ca8f79c3b53deae53301f19e3f9277b0a96341c2a1ce2c67c6bab15514799c2289f6f830378be1bbf5e94
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ ## Welcome to pomelo-citrus-admin
2
+
3
+ pomelo-citrus-admin is a simple clone of pomelo-admin written in Ruby using EventMachine.
4
+
5
+ ## Motivation
6
+
7
+ Since NodeJS is influenced by Ruby EventMachine and Python's Twisted model, Ruby should also be able to have its own game server framework like pomelo.
8
+
9
+ Ruby is a very expressive and eloquent programming language. I was an RoR programmer before and I really like Ruby, When developing this project, I have used many skills like meta-programming and they give me the real pleasures.
10
+
11
+ Recently, I would focus on my daily work, so I open source this project and hope to have more people participate in this project.
12
+
13
+ ## Todo
14
+
15
+ This gem is the very first gem I have done in my whole work, it needs to be improved but it already has the ablity to provide the basic infrastructure to other gems, other gems exist in my own repository.
16
+
17
+ ## Links
18
+
19
+ * [EventMachine](https://github.com/eventmachine/eventmachine)
20
+ * [pomelo-admin](https://github.com/NetEase/pomelo-admin)
data/Rakefile ADDED
File without changes
@@ -0,0 +1,34 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 8 July 2014
4
+
5
+ $:.push File.expand_path('../lib', __FILE__)
6
+
7
+ require 'citrus-admin/version'
8
+
9
+ Gem::Specification.new do |spec|
10
+ spec.name = 'pomelo-citrus-admin'
11
+ spec.version = CitrusAdmin::VERSION
12
+ spec.platform = Gem::Platform::RUBY
13
+ spec.authors = ['MinixLi']
14
+ spec.email = 'MinixLi1986@gmail.com'
15
+ spec.description = %q{pomelo-citrus-admin is a simple clone of pomelo-admin, it provides an admin module for pomelo monitor system}
16
+ spec.summary = %q{pomelo-admin clone written in Ruby using EventMachine}
17
+ spec.homepage = 'https://github.com/minixli/pomelo-citrus-admin'
18
+ spec.license = 'MIT'
19
+
20
+ spec.files = `git ls-files`.split($/)
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency('eventmachine', '~> 0')
26
+ spec.add_dependency('json', '~> 0')
27
+ spec.add_dependency('websocket-eventmachine-client', '~> 0')
28
+ spec.add_dependency('websocket-eventmachine-server', '~> 0')
29
+
30
+ spec.add_dependency('pomelo-citrus-loader', '~> 0')
31
+ spec.add_dependency('pomelo-citrus-logger', '~> 0')
32
+ spec.add_dependency('pomelo-citrus-monitor', '~> 0')
33
+ spec.add_dependency('pomelo-citrus-scheduler', '~> 0')
34
+ end
@@ -0,0 +1,21 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 8 July 2014
4
+
5
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+
7
+ require 'eventmachine'
8
+ require 'json'
9
+ require 'websocket-eventmachine-client'
10
+ require 'websocket-eventmachine-server'
11
+
12
+ require 'pomelo-citrus-monitor'
13
+
14
+ require 'citrus-admin/util/protocol'
15
+ require 'citrus-admin/util/utils'
16
+ require 'citrus-admin/console_service'
17
+
18
+ # Load all the console modules
19
+ Dir.glob(File.expand_path('../citrus-admin/modules/*.rb', __FILE__)).each { |filepath|
20
+ require filepath
21
+ }
@@ -0,0 +1,142 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 12 July 2014
4
+
5
+ module CitrusAdmin
6
+ # Client
7
+ #
8
+ #
9
+ class Client
10
+ include Protocol
11
+ include Utils::EventEmitter
12
+
13
+ # Create a new client
14
+ #
15
+ # @param [Hash] args Options
16
+ #
17
+ # @option args [String] :username
18
+ # @option args [String] :password
19
+ # @option args [Boolean] :md5
20
+ def initialize args={}
21
+ @client_id = ''
22
+ @req_id = 1
23
+ @callbacks = {}
24
+ @state = :state_inited
25
+ @username = args[:username] || ''
26
+ @password = args[:password] || ''
27
+ @md5 = args[:md5] || false
28
+ end
29
+
30
+ # Connect to master server
31
+ #
32
+ # @param [String] client_id
33
+ # @param [String] host
34
+ # @param [String] port
35
+ def connect client_id, host, port, &block
36
+ @client_id = client_id
37
+ @ws = WebSocket::EventMachine::Client.connect :uri => 'ws://' + host + ':' + port.to_s
38
+ @ws.onopen {
39
+ @state = :state_connected
40
+ @password = Utils.md5 @password if @md5
41
+ @ws.send ['register', {
42
+ :type => 'client',
43
+ :client_id => @client_id,
44
+ :username => @username,
45
+ :password => @password,
46
+ :md5 => @md5
47
+ }].to_json
48
+ }
49
+ @ws.onmessage { |msg, type|
50
+ begin
51
+ event, msg = parse msg
52
+ case event
53
+ when 'register'
54
+ process_register_msg msg, &block
55
+ when 'client'
56
+ process_client_msg msg
57
+ else
58
+ end
59
+ rescue => err
60
+ end
61
+ }
62
+ @ws.onclose { |code, reason|
63
+ @state = :state_closed
64
+ emit 'close'
65
+ }
66
+ @ws.onerror { |err|
67
+ if @state == :state_inited
68
+ block_given? and yield err
69
+ end
70
+ emit 'error', err
71
+ }
72
+ end
73
+
74
+ # Request master server with callback
75
+ #
76
+ # @param [String] module_id
77
+ # @param [Object] msg
78
+ def request module_id, msg, &block
79
+ req_id = @req_id
80
+ @req_id += 1
81
+ @callbacks[req_id] = block
82
+ msg[:client_id] = @client_id
83
+ msg[:username] = @username
84
+ @ws.send ['client', compose_request(req_id, module_id, msg)].to_json
85
+ end
86
+
87
+ # Notify master server without callback
88
+ #
89
+ # @param [String] module_id
90
+ # @param [Object] msg
91
+ def notify module_id, msg
92
+ msg[:client_id] = @client_id
93
+ msg[:username] = @username
94
+ @ws.send ['client', compose_request(nil, module_id, msg)].to_json
95
+ end
96
+
97
+ # Command
98
+ #
99
+ # @param [String] command
100
+ # @param [String] module_id
101
+ # @param [Object] msg
102
+ def command command, module_id, msg, &block
103
+ req_id = @req_id
104
+ @req_id += 1
105
+ @callbacks[req_id] = block
106
+ msg[:client_id] = @client_id
107
+ msg[:username] = @username
108
+ @ws.send ['client', compose_command(req_id, command, module_id, msg)].to_json
109
+ end
110
+
111
+ private
112
+
113
+ # Process register message
114
+ #
115
+ # @param [Object] msg
116
+ #
117
+ # @private
118
+ def process_register_msg msg, &block
119
+ if msg[:code] != PRO_OK
120
+ block_given? and yield msg[:msg]
121
+ return
122
+ end
123
+ @state = :state_registered
124
+ block_given? and yield
125
+ end
126
+
127
+ # Process client message
128
+ #
129
+ # @param [Object] msg
130
+ #
131
+ # @private
132
+ def process_client_msg msg
133
+ if resp_id = msg[:resp_id]
134
+ callback = @callbacks[resp_id]
135
+ @callbacks.delete resp_id
136
+ callback.call msg[:err], msg[:body]
137
+ elsif module_id = msg[:module_id]
138
+ emit module_id, msg
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,374 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 8 July 2014
4
+
5
+ require 'citrus-admin/master_agent'
6
+ require 'citrus-admin/monitor_agent'
7
+
8
+ module CitrusAdmin
9
+ # ConsoleService
10
+ #
11
+ #
12
+ class ConsoleService
13
+ include Protocol
14
+ include Utils; include Utils::EventEmitter
15
+
16
+ attr_reader :env, :master, :agent, :auth_user, :auth_server
17
+
18
+ # Create a new console service
19
+ #
20
+ # @param [Hash] args Options
21
+ #
22
+ # @option args [Boolean] :master
23
+ # @option args [String] :host
24
+ # @option args [Integer] :port
25
+ # @option args [String] :server_id
26
+ # @option args [String] :server_type
27
+ # @option args [Object] :server_info
28
+ # @option args [#call] :auth_user
29
+ # @option args [#call] :auth_server
30
+ def initialize args={}
31
+ @env = args[:env]
32
+ @master = args[:master]
33
+ @port = args[:port]
34
+ @console_modules = {}
35
+ @commands = {
36
+ :list => methods(:list_command),
37
+ :enable => methods(:enable_command),
38
+ :disable => methods(:disable_command)
39
+ }
40
+ if @master
41
+ @auth_user = args[:auth_user] || method(:df_auth_user)
42
+ @auth_server = args[:auth_server] || method(:df_auth_server_master)
43
+ args[:console_service] = self
44
+ @agent = MasterAgent.new args
45
+ else
46
+ @host = args[:host]
47
+ @server_id = args[:server_id]
48
+ @server_type = args[:server_type]
49
+ @auth_server = args[:auth_server] || method(:df_auth_server_monitor)
50
+ @agent = MonitorAgent.new({
51
+ :console_service => self,
52
+ :server_id => @server_id,
53
+ :server_type => @server_type,
54
+ :server_info => args[:server_info]
55
+ })
56
+ end
57
+ end
58
+
59
+ # Start master agent or monitor agent
60
+ def start &block
61
+ if @master
62
+ @agent.listen(@port) { |err|
63
+ if err
64
+ block_given? and yield err
65
+ return
66
+ end
67
+
68
+ @agent.on('register') { |*args| emit 'register', *args }
69
+ @agent.on('disconnect') { |*args| emit 'disconnect', *args }
70
+ @agent.on('reconnect') { |*args| emit 'reconnect', *args }
71
+
72
+ EM.next_tick {
73
+ block_given? and yield
74
+ }
75
+ }
76
+ else
77
+ @agent.connect @port, @host, &block
78
+ end
79
+ @console_modules.each { |module_id, console_module|
80
+ enable_module module_id
81
+ }
82
+ end
83
+
84
+ # Stop master agent or monitor agent
85
+ def stop
86
+ @console_modules.each { |module_id, console_module|
87
+ disable_module module_id
88
+ }
89
+ @agent.close
90
+ end
91
+
92
+ # Register console module
93
+ #
94
+ # @param [String] module_id
95
+ # @param [Object] module_entity
96
+ def register module_id, module_entity
97
+ console_module = {
98
+ :module_id => module_id,
99
+ :module_entity => module_entity,
100
+ :enable => false
101
+ }
102
+
103
+ if type = module_entity.type
104
+ if @master && type == 'pull' || !@master && type == 'push'
105
+ delay = module_entity.delay
106
+ interval = module_entity.interval
107
+ console_module[:delay] = delay ? (delay > 0 ? delay : 0) : 0
108
+ console_module[:interval] = interval ? (interval > 0 ? interval : 0) : 1
109
+ console_module[:schedule] = true
110
+ end
111
+ end
112
+
113
+ @console_modules[module_id] = console_module
114
+ end
115
+
116
+ # Enable console module
117
+ #
118
+ # @param [String] module_id
119
+ def enable_module module_id
120
+ console_module = @console_modules[module_id]
121
+ if console_module && !console_module[:enable]
122
+ console_module[:enable] = true
123
+ if console_module[:schedule]
124
+ add_to_scheduler console_module
125
+ end
126
+ return true
127
+ end
128
+ return false
129
+ end
130
+
131
+ # Disable console module
132
+ #
133
+ # @param [String] module_id
134
+ def disable_module module_id
135
+ console_module = @console_modules[module_id]
136
+ if console_module && console_module[:enable]
137
+ console_module[:enable] = false
138
+ if console_module[:schedule] && console_module[:job_id]
139
+ remove_from_scheduler console_module
140
+ end
141
+ return true
142
+ end
143
+ return false
144
+ end
145
+
146
+ # Execute console module's handler (monitor_handler, master_handler, client_handler)
147
+ #
148
+ # @param [String] module_id
149
+ # @param [String] method
150
+ # @param [Object] msg
151
+ def execute module_id, method, msg, &block
152
+ if !block_given?
153
+ raise ArgumentError 'expected a code block'
154
+ end
155
+
156
+ console_module = @console_modules[module_id]
157
+ if !console_module
158
+ yield 'unknown module id: ' + module_id
159
+ return
160
+ end
161
+ if !console_module[:enable]
162
+ yield 'module ' + module_id + ' is disabled'
163
+ return
164
+ end
165
+ module_entity = console_module[:module_entity]
166
+ if !module_entity
167
+ yield 'module ' + module_id + ' does not exist'
168
+ return
169
+ end
170
+ if !module_entity.respond_to? method
171
+ yield 'module ' + module_id + ' does not have such method: ' + method
172
+ return
173
+ end
174
+ acl_msg = acl_control 'execute', method, module_id, msg
175
+ if acl_msg != 0 && acl_msg != 1
176
+ yield Exception.new acl_msg
177
+ return
178
+ end
179
+ module_entity.send method, @agent, msg, &block
180
+ end
181
+
182
+ # Execute command
183
+ #
184
+ # @param [String] command
185
+ # @param [String] module_id
186
+ # @param [Object] msg
187
+ def command command, module_id, msg, &block
188
+ if !block_given?
189
+ raise ArgumentError 'expected a code block'
190
+ end
191
+
192
+ method = @commands[command.to_sym]
193
+ if !method
194
+ yield 'unknown command: ' + command
195
+ return
196
+ end
197
+ if !method.respond_to? :call
198
+ yield 'unknown command: ' + command
199
+ return
200
+ end
201
+
202
+ acl_msg = acl_control 'command', nil, module_id, msg
203
+ if acl_msg != 0 && acl_msg != 1
204
+ yield Exception.new acl_msg
205
+ return
206
+ end
207
+
208
+ method.call module_id, msg, &block
209
+ end
210
+
211
+ private
212
+
213
+ # Add console module to scheduler
214
+ #
215
+ # @param [Object] console_module
216
+ #
217
+ # @private
218
+ def add_to_scheduler console_module
219
+ args = [
220
+ # job_cb
221
+ method(:schedule_job_cb),
222
+ # job_cb_args
223
+ { :console_module => console_module },
224
+ # trigger_args
225
+ {
226
+ :start_time => Time.now.to_f + console_module[:delay],
227
+ :interval => console_module[:interval]
228
+ }
229
+ ]
230
+ console_module[:job_id] = CitrusScheduler.schedule_job *args
231
+ end
232
+
233
+ # Remove console module from scheduler
234
+ #
235
+ # @param [Object] console_module
236
+ #
237
+ # @private
238
+ def remove_from_scheduler console_module
239
+ CitrusScheduler.cancel_job console_module[:job_id]
240
+ console_module[:job_id] = nil
241
+ end
242
+
243
+ # Schedule job callback
244
+ #
245
+ # @param [Hash] args Options
246
+ #
247
+ # @option args [Object] :console_module
248
+ #
249
+ # @private
250
+ def schedule_job_cb args={}
251
+ console_module = args[:console_module]
252
+ return if !console_module || !console_module[:enable]
253
+
254
+ module_entity = console_module[:module_entity]
255
+ return if !module_entity
256
+
257
+ if @master
258
+ module_entity.master_handler(@agent, nil) { |err| }
259
+ else
260
+ module_entity.monitor_handler(@agent, nil) { |err| }
261
+ end
262
+ end
263
+
264
+ # List console modules
265
+ #
266
+ # @param [String] module_id
267
+ # @param [Object] msg
268
+ #
269
+ # @private
270
+ def list_command module_id, msg, &block
271
+ block.call nil, @console_modules.select { |module_id, console_module|
272
+ !(module_id =~ /^__\w+__$/)
273
+ }
274
+ end
275
+
276
+ # Enable console module
277
+ #
278
+ # @param [String] module_id
279
+ # @param [Object] msg
280
+ #
281
+ # @private
282
+ def enable_command module_id, msg, &block
283
+ if !block_given?
284
+ raise ArgumentError 'expected a code block'
285
+ end
286
+ if !@console_modules[module_id]
287
+ yield nil, PRO_FAIL
288
+ return
289
+ end
290
+ enable_module module_id
291
+ if @master
292
+ @agent.broadcast_command 'enable', module_id, msg
293
+ end
294
+ yield nil, PRO_OK
295
+ end
296
+
297
+ # Disable console module
298
+ #
299
+ # @param [String] module_id
300
+ # @param [Object] msg
301
+ #
302
+ # @private
303
+ def disable_command module_id, msg, &block
304
+ if !block_given?
305
+ raise ArgumentError 'expected a code block'
306
+ end
307
+ if !@console_modules[module_id]
308
+ yield nil, PRO_FAIL
309
+ return
310
+ end
311
+ disable_module module_id
312
+ if @master
313
+ @agent.broadcast_command 'disable', module_id, msg
314
+ end
315
+ yield nil, PRO_OK
316
+ end
317
+
318
+ # ACL control
319
+ #
320
+ # @param [String] action
321
+ # @param [String] method
322
+ # @param [String] module_id
323
+ # @param [Object] msg
324
+ #
325
+ # @private
326
+ def acl_control action, method, module_id, msg
327
+ if action == 'execute'
328
+ if method != 'client_handler' || module_id != '__console__'
329
+ return 0
330
+ end
331
+ signal = msg[:signal]
332
+ if !signal || !(['stop', 'add', 'kill'].include? signal)
333
+ return 0
334
+ end
335
+ end
336
+ if !client_id = msg[:client_id]
337
+ return 'unknown client id'
338
+ end
339
+ client = @agent.get_client_by_id client_id
340
+ if client && client[:user_info] && client[:user_info][:level]
341
+ level = client[:user_info][:level]
342
+ if level > 1
343
+ return 'command permission denied'
344
+ end
345
+ else
346
+ return 'unknown client info'
347
+ end
348
+ return 1
349
+ end
350
+
351
+ # Create master console service
352
+ #
353
+ # @param [Hash] args Options
354
+ #
355
+ # @option args [Integer] :port
356
+ def self.create_master_console args={}
357
+ args[:master] = true
358
+ ConsoleService.new args
359
+ end
360
+
361
+ # Create monitor console service
362
+ #
363
+ # @param [Hash] args Options
364
+ #
365
+ # @option args [String] :host
366
+ # @option args [Integer] :port
367
+ # @option args [String] :server_id
368
+ # @option args [String] :server_type
369
+ # @option args [Object] :server_info
370
+ def self.create_monitor_console args={}
371
+ ConsoleService.new args
372
+ end
373
+ end
374
+ end