pomelo-citrus-admin 0.0.1

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,607 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 12 July 2014
4
+
5
+ module CitrusAdmin
6
+ # MasterAgent
7
+ #
8
+ #
9
+ class MasterAgent
10
+ include Protocol
11
+ include Utils::EventEmitter
12
+
13
+ # Create a new master agent
14
+ #
15
+ # @param [Hash] args Options
16
+ #
17
+ # @option args [Object] :console_service
18
+ # @option args [Array] :white_list
19
+ def initialize args={}
20
+ @console_service = args[:console_service]
21
+ @servers = {}
22
+ @servers_map = {}
23
+ @slaves_map = {}
24
+ @clients = {}
25
+ @req_id = 1
26
+ @callbacks = {}
27
+ @wss = {}
28
+ @white_list = args[:white_list]
29
+ @state = :state_inited
30
+ end
31
+
32
+ # Listen to a port and handle register and request
33
+ #
34
+ # @param [Integer] port
35
+ def listen port, &block
36
+ if @state != :state_inited
37
+ return
38
+ end
39
+ @state = :state_started
40
+ begin
41
+ @server = WebSocket::EventMachine::Server.start(:host => '0.0.0.0', :port => port.to_s) { |ws|
42
+ ws_context = {
43
+ # 'monitor' or 'client'
44
+ :type => nil,
45
+ # for monitor connection
46
+ :server_id => nil, :server_type => nil, :server_info => nil,
47
+ # for client connection
48
+ :client_id => nil,
49
+ # for both connection
50
+ :username => nil, :registered => false
51
+ }
52
+ ws.onopen {
53
+ @wss[ws.signature] = ws
54
+ peer_port, peer_host = Socket.unpack_sockaddr_in ws.get_peername
55
+ emit 'connection', { :id => ws.signature, :ip => peer_host }
56
+ }
57
+ ws.onmessage { |msg, type|
58
+ begin
59
+ event, msg = parse msg
60
+ case event
61
+ when 'register'
62
+ process_register_msg ws, ws_context, msg
63
+ when 'monitor'
64
+ process_msg_from_monitor ws, ws_context, msg
65
+ when 'client'
66
+ process_msg_from_client ws, ws_context, msg
67
+ else
68
+ end
69
+ rescue => err
70
+ end
71
+ }
72
+ ws.onclose {
73
+ @wss.delete ws.signature
74
+ if ws_context[:registered]
75
+ case ws_context[:type]
76
+ when 'monitor'
77
+ remove_monitor_connection(ws_context[:server_id],
78
+ ws_context[:server_type], ws_context[:server_info])
79
+ when 'client'
80
+ remove_client_connection ws_context[:client_id]
81
+ else
82
+ end
83
+ emit 'disconnect', ws_context
84
+ end
85
+ }
86
+ ws.onerror { |err|
87
+ emit 'err', err
88
+ }
89
+ }
90
+ block_given? && yield
91
+ rescue => err
92
+ emit 'error', err
93
+ end
94
+ on('connection') { |obj| ip_filter obj }
95
+ end
96
+
97
+ # Close the agent
98
+ def close
99
+ return unless @state == :state_started
100
+ @state = :state_closed
101
+ EM.stop_server @server
102
+ end
103
+
104
+ # Get client by id
105
+ #
106
+ # @param [String] client_id
107
+ def get_client_by_id client_id
108
+ @clients[client_id]
109
+ end
110
+
111
+ # Request by server id
112
+ #
113
+ # @param [String] server_id
114
+ # @param [String] module_id
115
+ # @param [Object] msg
116
+ # @param [#call] block
117
+ def request server_id, module_id, msg, block
118
+ return if @state != :state_started
119
+ if !server = @servers[server_id]
120
+ block.call Exception.new 'unknown server id: ' + server_id
121
+ return
122
+ end
123
+ req_id = @req_id
124
+ @req_id += 1
125
+ @callbacks[req_id] = block
126
+ send_msg_to_monitor server[:ws], req_id, module_id, msg
127
+ end
128
+
129
+ # Request by server
130
+ #
131
+ # @param [String] server_id
132
+ # @param [Object] server_info
133
+ # @param [String] module_id
134
+ # @param [Object] msg
135
+ # @param [#call] block
136
+ def request_by_server server_id, server_info, module_id, msg, block
137
+ return if @state != :state_started
138
+ if !server = @servers[server_id]
139
+ block.call Exception.new 'unknown server id: ' + server_id
140
+ return
141
+ end
142
+ req_id = @req_id
143
+ @req_id += 1
144
+ @callbacks[req_id] = block
145
+ if Utils.compare_server server[:server_info], server_info
146
+ send_msg_to_monitor server[:ws], req_id, module_id, msg
147
+ else
148
+ @slaves_map[server_id].each { |server|
149
+ if Utils.compare_server server[:server_info], server_info
150
+ send_msg_to_monitor server[:ws], req_id, module_id, msg
151
+ break
152
+ end
153
+ }
154
+ end
155
+ end
156
+
157
+ # Notify server by server id
158
+ #
159
+ # @param [String] server_id
160
+ # @param [String] module_id
161
+ # @param [Object] msg
162
+ def notify server_id, module_id, msg
163
+ return if @state != :state_started
164
+ if !server = @servers[server_id]
165
+ return false
166
+ end
167
+ send_msg_to_monitor server[:ws], nil, module_id, msg
168
+ return true
169
+ end
170
+
171
+ # Notify server by server
172
+ #
173
+ # @param [String] server_id
174
+ # @param [Object] server_info
175
+ # @param [String] module_id
176
+ # @param [Object] msg
177
+ def notify_by_server server_id, server_info, module_id, msg
178
+ return if @state != :state_started
179
+ if !server = @servers[server_id]
180
+ return false
181
+ end
182
+ if Utils.compare_server server[:server_info], server_info
183
+ send_msg_to_monitor server[:ws], nil, module_id, msg
184
+ else
185
+ @slaves_map[server_id].each { |server|
186
+ if Utils.compare_server server[:server_info], server_info
187
+ send_msg_to_monitor server[:ws], nil, module_id, msg
188
+ break
189
+ end
190
+ }
191
+ end
192
+ return true
193
+ end
194
+
195
+ # Notify by server type
196
+ #
197
+ # @param [String] server_type
198
+ # @param [String] module_id
199
+ # @param [Object] msg
200
+ def notify_by_server_type server_type, module_id, msg
201
+ return if @state != :state_started
202
+ servers = @servers_map[server_type]
203
+ if !servers || servers.empty?
204
+ return false
205
+ end
206
+ broadcast_notify_msg servers, module_id, msg
207
+ return true
208
+ end
209
+
210
+ # Notify to slaves
211
+ #
212
+ # @param [String] server_id
213
+ # @param [String] module_id
214
+ # @param [Object] msg
215
+ def notify_to_slaves server_id, module_id, msg
216
+ return if @state != :state_started
217
+ servers = @slaves_map[server_id]
218
+ if !servers || servers.empty?
219
+ return false
220
+ end
221
+ broadcast_notify_msg servers, module_id, msg
222
+ return true
223
+ end
224
+
225
+ # Broadcast notify
226
+ #
227
+ # @param [String] module_id
228
+ # @param [Object] msg
229
+ def broadcast_notify module_id, msg
230
+ return if @state != :state_started
231
+ broadcast_notify_msg @servers.values, module_id, msg
232
+ end
233
+
234
+ # Broadcast command
235
+ #
236
+ # @param [String] command
237
+ # @param [String] module_id
238
+ # @param [Object] msg
239
+ def broadcast_command command, module_id, msg
240
+ return if @state != :state_started
241
+ broadcast_command_msg @servers.values, command, module_id, msg
242
+ end
243
+
244
+ # Notify client
245
+ #
246
+ # @param [String] client_id
247
+ # @param [String] module_id
248
+ # @param [Object] msg
249
+ def notify_client client_id, module_id, msg
250
+ return if @state != :state_started
251
+ if !client = @clients[client_id]
252
+ return
253
+ end
254
+ send_msg_to_client client[:ws], nil, module_id, msg
255
+ end
256
+
257
+ private
258
+
259
+ # Process register message
260
+ #
261
+ # @param [Object] ws
262
+ # @param [Object] ws_context
263
+ # @param [Object] msg
264
+ #
265
+ # @private
266
+ def process_register_msg ws, ws_context, msg
267
+ return unless msg && msg[:type]
268
+ case msg[:type]
269
+ when 'monitor'
270
+ return unless msg[:server_id]
271
+ ws_context[:type] = msg[:type]
272
+ ws_context[:server_id] = msg[:server_id]
273
+ ws_context[:server_type] = msg[:server_type]
274
+ ws_context[:server_info] = msg[:server_info]
275
+ do_auth_server(msg, ws) { |err|
276
+ if err
277
+ ws.close
278
+ return
279
+ end
280
+ ws_context[:registered] = true
281
+ }
282
+ when 'client'
283
+ ws_context[:type] = msg[:type]
284
+ do_auth_user(msg, ws) { |err|
285
+ if err
286
+ ws.close
287
+ return
288
+ end
289
+ ws_context[:username] = msg[:username]
290
+ ws_context[:registered] = true
291
+ }
292
+ else
293
+ ws.send ['register', {
294
+ :code => PRO_FAIL,
295
+ :msg => 'unknown auth type'
296
+ }].to_json
297
+ ws.close
298
+ end
299
+ end
300
+
301
+ # Process message from monitor
302
+ #
303
+ # @param [Object] ws
304
+ # @param [Object] ws_context
305
+ # @param [Object] msg
306
+ #
307
+ # @private
308
+ def process_msg_from_monitor ws, ws_context, msg
309
+ if !ws_context[:registered]
310
+ ws.close
311
+ return
312
+ end
313
+ if ws_context[:type] != 'monitor'
314
+ return
315
+ end
316
+ if resp_id = msg[:resp_id]
317
+ # response from monitor
318
+ callback = @callbacks[resp_id]
319
+ if !callback
320
+ return
321
+ end
322
+ @callbacks.delete resp_id
323
+ callback.call msg[:err], msg[:body]
324
+ return
325
+ end
326
+ # request or notify from monitor
327
+ @console_service.execute(msg[:module_id], :master_handler, msg[:body]) { |err, res|
328
+ if is_request? msg
329
+ if resp = compose_response(msg, err, res)
330
+ ws.send ['monitor', resp].to_json
331
+ end
332
+ else
333
+ # notify should not have a callback
334
+ end
335
+ }
336
+ end
337
+
338
+ # Process message from client
339
+ #
340
+ # @param [Object] ws
341
+ # @param [Object] ws_context
342
+ # @param [Object] msg
343
+ #
344
+ # @private
345
+ def process_msg_from_client ws, ws_context, msg
346
+ if !ws_context[:registered]
347
+ ws.close
348
+ return
349
+ end
350
+ if ws_context[:type] != 'client'
351
+ return
352
+ end
353
+ if msg[:command]
354
+ # a command from client
355
+ @console_service.command(msg[:command], msg[:module_id], msg[:body]) { |err, res|
356
+ if is_request? msg
357
+ if resp = compose_response(msg, err, res)
358
+ ws.send ['client', resp].to_json
359
+ end
360
+ else
361
+ # notify should not have a callback
362
+ end
363
+ }
364
+ else
365
+ # a request or a notify from client
366
+ @console_service.execute(msg[:module_id], :client_handler, msg[:body]) { |err, res|
367
+ if is_request? msg
368
+ if resp = compose_response(msg, err, res)
369
+ ws.send ['client', resp].to_json
370
+ end
371
+ else
372
+ # notify should not have a callback
373
+ end
374
+ }
375
+ end
376
+ end
377
+
378
+ # Add server connection
379
+ #
380
+ # @param [String] server_id
381
+ # @param [String] server_type
382
+ # @param [Object] server_info
383
+ # @param [Integer] pid
384
+ # @param [Obejct] ws
385
+ #
386
+ # @private
387
+ def add_monitor_connection server_id, server_type, server_info, pid, ws
388
+ server = {
389
+ :server_id => server_id,
390
+ :server_type => server_type,
391
+ :server_info => server_info,
392
+ :pid => pid,
393
+ :ws => ws
394
+ }
395
+ if !@servers[server_id]
396
+ @servers[server_id] = server
397
+ @servers_map[server_type] ||= []
398
+ @servers_map[server_type] << server
399
+ else
400
+ @slaves_map[server_id] ||= []
401
+ @slaves_map[server_id] << server
402
+ end
403
+ server
404
+ end
405
+
406
+ # Add client connection
407
+ #
408
+ # @param [String] client_id
409
+ # @param [Object] user
410
+ # @param [Object] ws
411
+ #
412
+ # @private
413
+ def add_client_connection client_id, user, ws
414
+ client = {
415
+ :client_id => client_id,
416
+ :user_info => user,
417
+ :ws => ws
418
+ }
419
+ @clients[client_id] = client
420
+ client
421
+ end
422
+
423
+ # Remove monitor connection
424
+ #
425
+ # @param [String] server_id
426
+ # @param [String] server_type
427
+ # @param [Object] server_info
428
+ #
429
+ # @private
430
+ def remove_monitor_connection server_id, server_type, server_info
431
+ # if Utils.compare_server @servers[server_id][:server_info], server_info
432
+ # @servers.delete server_id
433
+ # if @servers_map[server_type]
434
+ # @servers_map[server_type].delete_if { |server|
435
+ # server[:server_id] == server_id
436
+ # }
437
+ # if @servers_map[server_type].empty?
438
+ # @servers_map.delete server_type
439
+ # end
440
+ # end
441
+ # else
442
+ # if @slaves_map[server_id]
443
+ # @slaves_map[server_id].delete_if { |server|
444
+ # Utils.compare_server server[:server_info], server_info
445
+ # }
446
+ # if @slaves_map[server_id].empty?
447
+ # @slaves_map.delete server_id
448
+ # end
449
+ # end
450
+ # end
451
+ end
452
+
453
+ # Remove client connection
454
+ #
455
+ # @param [String] client_id
456
+ #
457
+ # @private
458
+ def remove_client_connection client_id
459
+ @clients.delete ws_context[:client_id]
460
+ end
461
+
462
+ # Send message to monitor
463
+ #
464
+ # @param [Object] ws
465
+ # @param [Integer] req_id
466
+ # @param [String] module_id
467
+ # @param [Object] msg
468
+ #
469
+ # @private
470
+ def send_msg_to_monitor ws, req_id, module_id, msg
471
+ msg = compose_request req_id, module_id, msg
472
+ ws.send ['monitor', msg].to_json
473
+ end
474
+
475
+ # Send message to client
476
+ #
477
+ # @param [Object] ws
478
+ # @param [Integer] req_id
479
+ # @param [String] module_id
480
+ # @param [Object] msg
481
+ #
482
+ # @private
483
+ def send_msg_to_client ws, req_id, module_id, msg
484
+ msg = compose_request req_id, module_id, msg
485
+ ws.send ['client', msg].to_json
486
+ end
487
+
488
+ # Broadcast notify message
489
+ #
490
+ # @param [Array] servers
491
+ # @param [String] module_id
492
+ # @param [Object] msg
493
+ #
494
+ # @private
495
+ def broadcast_notify_msg servers, module_id, msg
496
+ msg = compose_request nil, module_id, msg
497
+ servers.each { |server|
498
+ server[:ws].send ['monitor', msg].to_json
499
+ }
500
+ end
501
+
502
+ # Broadcast command message
503
+ #
504
+ # @param [Array] servers
505
+ # @param [String] command
506
+ # @param [String] module_id
507
+ # @param [Object] msg
508
+ #
509
+ # @private
510
+ def broadcast_command_msg servers, command, module_id, msg
511
+ msg = compose_command nil, command, module_id, msg
512
+ servers.each { |server|
513
+ server[:ws].send ['monitor', msg].to_json
514
+ }
515
+ end
516
+
517
+ # Do auth server
518
+ #
519
+ # @param [Object] msg
520
+ # @param [Object] ws
521
+ #
522
+ # @private
523
+ def do_auth_server msg, ws, &block
524
+ if !block_given?
525
+ raise ArgumentError 'expected a code block'
526
+ end
527
+ @console_service.auth_server.call(msg, @console_service.env) { |res|
528
+ if res != 'ok'
529
+ ws.send ['register', {
530
+ :code => PRO_FAIL,
531
+ :msg => 'server auth failed'
532
+ }].to_json
533
+ yield Exception.new 'server auth failed'
534
+ return
535
+ end
536
+ add_monitor_connection msg[:server_id], msg[:server_type], msg[:server_info], msg[:pid], ws
537
+ ws.send ['register', {
538
+ :code => PRO_OK,
539
+ :msg => 'ok'
540
+ }].to_json
541
+
542
+ if msg[:server_info]
543
+ msg[:server_info][:pid] = msg[:pid]
544
+ emit 'register', msg[:server_info]
545
+ end
546
+
547
+ yield nil
548
+ }
549
+ end
550
+
551
+ # Do auth user
552
+ #
553
+ # @param [Object] msg
554
+ # @param [Object] ws
555
+ #
556
+ # @private
557
+ def do_auth_user msg, ws, &block
558
+ if !block_given?
559
+ raise ArgumentError 'expected a code block'
560
+ end
561
+ if !client_id = msg[:client_id]
562
+ yield Exception.new 'client should have a client id'
563
+ return
564
+ end
565
+ if !username = msg[:username]
566
+ ws.send ['register', {
567
+ :code => PRO_FAIL,
568
+ :msg => 'client should auth with username'
569
+ }].to_json
570
+ yield Exception.new 'client should auth with username'
571
+ return
572
+ end
573
+ @console_service.auth_user.call(msg, @console_service.env) { |user|
574
+ if !user
575
+ ws.send ['register', {
576
+ :code => PRO_FAIL,
577
+ :msg => 'client auth failed'
578
+ }].to_json
579
+ yield Exception.new 'client auth failed'
580
+ return
581
+ end
582
+ if @clients[client_id]
583
+ ws.send ['register', {
584
+ :code => PRO_FAIL,
585
+ :msg => 'client id has already been registered'
586
+ }].to_json
587
+ yield Exception.new 'client id has already been registered'
588
+ return
589
+ end
590
+ add_client_connection client_id, user, ws
591
+ ws.send ['register', {
592
+ :code => PRO_OK,
593
+ :msg => 'ok'
594
+ }].to_json
595
+ yield nil
596
+ }
597
+ end
598
+
599
+ # ip filter
600
+ #
601
+ # @param [Object] obj
602
+ #
603
+ # @private
604
+ def ip_filter obj
605
+ end
606
+ end
607
+ end