genesisreactor 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/genesis/agent.rb +25 -0
- data/lib/genesis/handler.rb +30 -0
- data/lib/genesis/protocol/echo/agent.rb +10 -0
- data/lib/genesis/protocol/echo/handler.rb +14 -0
- data/lib/genesis/protocol/echo/protocol.rb +16 -0
- data/lib/genesis/protocol/echo/server.rb +23 -0
- data/lib/genesis/protocol/echo.rb +4 -0
- data/lib/genesis/protocol/http/async_monkeypatch.rb +19 -0
- data/lib/genesis/protocol/http/handler.rb +21 -0
- data/lib/genesis/protocol/http/protocol.rb +19 -0
- data/lib/genesis/protocol/http/server.rb +88 -0
- data/lib/genesis/protocol/http.rb +3 -0
- data/lib/genesis/protocol/snmp/agent.rb +10 -0
- data/lib/genesis/protocol/snmp/handler.rb +14 -0
- data/lib/genesis/protocol/snmp/protocol.rb +16 -0
- data/lib/genesis/protocol/snmp/server.rb +81 -0
- data/lib/genesis/protocol/snmp.rb +55 -0
- data/lib/genesis/protocol.rb +20 -0
- data/lib/genesis/reactor.rb +169 -0
- data/lib/genesis/server.rb +39 -0
- data/lib/genesisreactor.rb +7 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4817f006c4e5a7f526e34621d7a2ade1dcf2d023
|
4
|
+
data.tar.gz: e5d4f096612c72f90d8e68045b2611f2f3e1b2b0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7e9893694f8f69a7f1c872604f7c39ff12e06be91288999408eb0349131254c40d62be9afdad0b12bceae9dcf4fdd3da0e154c95540b2066fec190b69d278d6e
|
7
|
+
data.tar.gz: b093bd04764782ed939d155fd98c01f06eaf5c4e00338f1fcd8c099a4b032a60bc3ad9eb7794e3ec6495a9b216f259da2ee9228829a85bf2c2caced7cc08f283
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Genesis
|
2
|
+
# Interface for a GenesisAgent
|
3
|
+
# Allows subclasess specifying agents in a DSL
|
4
|
+
class Agent
|
5
|
+
class << self
|
6
|
+
attr_accessor :agents
|
7
|
+
def register_agent(**kwargs, &block)
|
8
|
+
@agents << { interval: kwargs[:interval] || 60, opts: kwargs, block: block }
|
9
|
+
end
|
10
|
+
|
11
|
+
def reset!
|
12
|
+
@agents = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def inherited(subclass)
|
16
|
+
subclass.reset!
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :schedule, :register_agent
|
21
|
+
end
|
22
|
+
|
23
|
+
reset!
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Genesis
|
2
|
+
# Interface for a GenesisHandler
|
3
|
+
# Allows subclasess specifying routes and subscribers in a DSL
|
4
|
+
class Handler
|
5
|
+
class << self
|
6
|
+
attr_accessor :routes, :subscribers
|
7
|
+
def register_route(match, opts = {}, &block)
|
8
|
+
@routes[match] = { verb: __callee__.to_s, opts: opts, block: block }
|
9
|
+
end
|
10
|
+
|
11
|
+
def register_subscriber(*_args, &block)
|
12
|
+
@subscribers << { block: block }
|
13
|
+
end
|
14
|
+
|
15
|
+
def reset!
|
16
|
+
@subscribers = []
|
17
|
+
@routes = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def inherited(subclass)
|
21
|
+
subclass.reset!
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method :subscribe, :register_subscriber
|
26
|
+
end
|
27
|
+
|
28
|
+
reset!
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'genesis/handler'
|
2
|
+
|
3
|
+
module Genesis
|
4
|
+
module Echo
|
5
|
+
# A test handler to demonstrate functionality and facilitate testing
|
6
|
+
class Handler < Genesis::Handler
|
7
|
+
include Protocol
|
8
|
+
|
9
|
+
class << self
|
10
|
+
alias_method :say, :register_route
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'genesis/protocol'
|
2
|
+
|
3
|
+
module Genesis
|
4
|
+
module Echo
|
5
|
+
# Implement a simple protocol to demonstrate
|
6
|
+
# how to write a protocol, and facilitate testing.
|
7
|
+
# In keeping with event machine tradition, it's a simple 'echo' protocol.
|
8
|
+
module Protocol
|
9
|
+
include Genesis::Protocol
|
10
|
+
|
11
|
+
def self.protocol
|
12
|
+
:echo
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'genesis/server'
|
2
|
+
|
3
|
+
module Genesis
|
4
|
+
module Echo
|
5
|
+
# Implement a test server to demonstrate functionality and facilitate testing
|
6
|
+
class Server < EM::Connection
|
7
|
+
include Genesis::Server
|
8
|
+
include Protocol
|
9
|
+
def receive_data(data)
|
10
|
+
@channel << data
|
11
|
+
@handle_routes.each do |verb, matchdata|
|
12
|
+
case verb
|
13
|
+
when 'say'
|
14
|
+
matchdata.each do |pattern, blockdata|
|
15
|
+
send_data blockdata[:block].call(data) if data =~ pattern
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
close_connection_after_writing
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Monkey-patch async sinatra to support all verb sinatra supports
|
2
|
+
# Can remove if this patch gets merged:
|
3
|
+
# https://github.com/raggi/async_sinatra/pull/39
|
4
|
+
module Sinatra
|
5
|
+
# Monkey patch async to add some more methods
|
6
|
+
module Async
|
7
|
+
def apatch(path, opts = {}, &bk)
|
8
|
+
aroute('PATCH', path, opts, &bk)
|
9
|
+
end
|
10
|
+
|
11
|
+
def alink(path, opts = {}, &bk)
|
12
|
+
aroute('LINK', path, opts, &bk)
|
13
|
+
end
|
14
|
+
|
15
|
+
def aunlink(path, opts = {}, &bk)
|
16
|
+
aroute('UNLINK', path, opts, &bk)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'genesis/handler'
|
2
|
+
|
3
|
+
module Genesis
|
4
|
+
module Http
|
5
|
+
# Handle various HTTP verbs
|
6
|
+
class Handler < Genesis::Handler
|
7
|
+
include Protocol
|
8
|
+
|
9
|
+
class << self
|
10
|
+
alias_method :get, :register_route
|
11
|
+
alias_method :post, :register_route
|
12
|
+
alias_method :delete, :register_route
|
13
|
+
alias_method :head, :register_route
|
14
|
+
alias_method :options, :register_route
|
15
|
+
alias_method :patch, :register_route
|
16
|
+
alias_method :link, :register_route
|
17
|
+
alias_method :unlink, :register_route
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'genesis/protocol'
|
2
|
+
|
3
|
+
module Genesis
|
4
|
+
module Http
|
5
|
+
# Implement support for HTTP protocol
|
6
|
+
# Levering sinatra_async, and the fact that thin uses eventmachine
|
7
|
+
module Protocol
|
8
|
+
include Genesis::Protocol
|
9
|
+
|
10
|
+
def self.start_block
|
11
|
+
proc { Server.start_server }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.protocol
|
15
|
+
:http
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'sinatra/async'
|
2
|
+
require 'thin'
|
3
|
+
|
4
|
+
require 'genesis/server'
|
5
|
+
require 'async_monkeypatch'
|
6
|
+
|
7
|
+
module Genesis
|
8
|
+
module Http
|
9
|
+
# Implement an HTTP server using async_sinatra and thin
|
10
|
+
class Server < Sinatra::Base
|
11
|
+
include Genesis::Server
|
12
|
+
include Protocol
|
13
|
+
register Sinatra::Async
|
14
|
+
|
15
|
+
# Block to actually start the server
|
16
|
+
def self.start_server
|
17
|
+
app = new(channel: @channel, routes: @handle_routes, views: @args[:views] || [])
|
18
|
+
dispatch = Rack::Builder.app do
|
19
|
+
map '/' do
|
20
|
+
run app
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Since Thin is backed by EventMachine's TCPserver anyways,
|
25
|
+
# This is just a TCPServer like any other - running inside the same EventMachine!
|
26
|
+
Thin::Server.new(@port, '0.0.0.0', dispatch).backend.start
|
27
|
+
end
|
28
|
+
|
29
|
+
# Inject the channel and extended routes
|
30
|
+
def initialize(app = nil, **kwargs)
|
31
|
+
super(app)
|
32
|
+
@channel = kwargs[:channel] || nil
|
33
|
+
@extended_routes = kwargs[:routes] || {}
|
34
|
+
@views = kwargs[:views] || []
|
35
|
+
initialize_routes
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Register all routes provided
|
41
|
+
def initialize_routes
|
42
|
+
@extended_routes.each do |verb, matches|
|
43
|
+
matches.each do |match, data|
|
44
|
+
register_route(verb, match, data[:args], data[:block])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Injects a route into the sinatra class
|
50
|
+
def register_route(verb, match, opts, block)
|
51
|
+
async_verb = "a#{verb}" # force verb to async verb
|
52
|
+
self.class.send(async_verb, match, opts, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
helpers do
|
56
|
+
# Enable partial template rendering
|
57
|
+
def partial(template, locals = {})
|
58
|
+
erb(template, layout: false, locals: locals)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Override template search directorys to add spells
|
62
|
+
def find_template(views, *a, &block)
|
63
|
+
Array(@views).each { |v| super(v, *a, &block) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Define our asynchronous scheduling mechanism, could be anything
|
67
|
+
# Chose EM.defer for simplicity
|
68
|
+
# This powers our asynchronous requests, and keeps us from blocking the main thread.
|
69
|
+
def native_async_schedule(&b)
|
70
|
+
EM.defer(&b)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Needed to properly catch exceptions in async threads
|
74
|
+
def handle_exception!(context)
|
75
|
+
if context.message == 'Sinatra::NotFound'
|
76
|
+
error_msg = "Resource #{request.path} does not exist"
|
77
|
+
puts error_msg
|
78
|
+
ahalt(404, error_msg)
|
79
|
+
else
|
80
|
+
puts context.message
|
81
|
+
puts context.backtrace.join("\n")
|
82
|
+
ahalt(500, 'Uncaught exception occurred')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Genesis
|
2
|
+
module Snmp
|
3
|
+
# Implement support for SNMP protocol using pure-ruby snmp.
|
4
|
+
module Protocol
|
5
|
+
include Genesis::Protocol
|
6
|
+
|
7
|
+
def self.start_block
|
8
|
+
proc { Server.start_server }
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.protocol
|
12
|
+
:snmp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'snmp'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
require 'genesis/server'
|
5
|
+
|
6
|
+
module Genesis
|
7
|
+
module Snmp
|
8
|
+
# Implement an SNMP trap handling server
|
9
|
+
class Server < EM::Connection
|
10
|
+
include Genesis::Server
|
11
|
+
include Protocol
|
12
|
+
|
13
|
+
attr_accessor :mib, :community
|
14
|
+
|
15
|
+
def self.start_server
|
16
|
+
commstr = @args[:community] || ''
|
17
|
+
mib_dir = @args[:mib_dir] || SNMP::MIB::DEFAULT_MIB_PATH
|
18
|
+
mib_mods = @args[:mib_mods] || SNMP::Options.default_modules
|
19
|
+
mib = load_modules(mib_mods, mib_dir)
|
20
|
+
EM.open_datagram_socket('0.0.0.0', @port, self) do |conn|
|
21
|
+
conn.mib = mib
|
22
|
+
conn.community = commstr
|
23
|
+
conn.channel = @channel
|
24
|
+
conn.handle_routes = @handle_routes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.load_modules(module_list, mib_dir)
|
29
|
+
mib = SNMP::MIB.new
|
30
|
+
module_list.each { |m| mib.load_module(m, mib_dir) }
|
31
|
+
mib
|
32
|
+
end
|
33
|
+
|
34
|
+
def receive_data(data)
|
35
|
+
snmp_trap = handle_trap(data)
|
36
|
+
@channel << snmp_trap
|
37
|
+
route_trap(snmp_trap)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def handle_trap(data)
|
43
|
+
source_port, source_ip = Socket.unpack_sockaddr_in(get_peername)
|
44
|
+
|
45
|
+
message = SNMP::Message.decode(data, @mib)
|
46
|
+
snmp_trap = message.pdu
|
47
|
+
|
48
|
+
# If we configured a community and the message wasn't from our community, bail
|
49
|
+
close_connection if @community != '' && @community != message.community
|
50
|
+
|
51
|
+
# Handle inform requests, which want a response
|
52
|
+
handle_inform(snmp_trap, message, source_ip, source_port)
|
53
|
+
|
54
|
+
# Append source ip and return
|
55
|
+
snmp_trap.source_ip = source_ip
|
56
|
+
snmp_trap
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_inform(snmp_trap, message, source_ip, source_port)
|
60
|
+
return unless snmp_trap.is_a?(SNMP::InformRequest)
|
61
|
+
UDPSocket.new.send(message.response.encode, 0, source_ip, source_port)
|
62
|
+
end
|
63
|
+
|
64
|
+
def route_trap(snmp_trap)
|
65
|
+
@handle_routes.each do |verb, matchdata|
|
66
|
+
case verb
|
67
|
+
when 'trap'
|
68
|
+
route_trap_handler(snmp_trap, matchdata)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def route_trap_handler(snmp_trap, matchdata)
|
74
|
+
trap_oid = snmp_trap.trap_oid.join('.')
|
75
|
+
matchdata.each do |oid, blockdata|
|
76
|
+
blockdata[:block].call(snmp_trap) if oid =~ /#{trap_oid}/
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'snmp'
|
2
|
+
|
3
|
+
require 'genesis/protocol/snmp/protocol'
|
4
|
+
require 'genesis/protocol/snmp/server'
|
5
|
+
require 'genesis/protocol/snmp/handler'
|
6
|
+
require 'genesis/protocol/snmp/agent'
|
7
|
+
|
8
|
+
module Genesis
|
9
|
+
# Common helper methods for protocol
|
10
|
+
module Snmp
|
11
|
+
# Send an SNMP trap, returns the length of the trap message sent
|
12
|
+
def self.send_trap(sys_up_time, trap_oid, object_list = [], **kwargs)
|
13
|
+
manager(kwargs) do |mgr|
|
14
|
+
return mgr.trap_v2(sys_up_time, trap_oid, object_list)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Send an SNMP get request
|
19
|
+
def self.get(oids = [], **kwargs)
|
20
|
+
manager(kwargs) do |mgr|
|
21
|
+
mgr.get(oids)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# FIXME: implement - these are tricky to test without an actual SNMP server
|
26
|
+
# # Send an SNMP set request
|
27
|
+
# def self.set(bindings = {}, **kwargs)
|
28
|
+
# manager(kwargs) do |mgr|
|
29
|
+
# bindings.each do |oid, value|
|
30
|
+
# mgr.set(SNMP::VarBind.new(oid, value)) # FIXME maybe try and infer type for varbind from value's type to avoid leaky abstraction?
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
|
35
|
+
# # Walk an OID table rooted at any oids provided
|
36
|
+
# def self.walk(oids = [], **kwargs)
|
37
|
+
# manager(kwargs) do |mgr|
|
38
|
+
# mgr.walk(oids)
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Create a manager and yield it to perform the request
|
45
|
+
def self.manager(args)
|
46
|
+
host = args[:host] || '127.0.0.1'
|
47
|
+
port = args[:port] || 161
|
48
|
+
trapport = args[:trapport] || 162
|
49
|
+
community = args[:community] || 'public'
|
50
|
+
SNMP::Manager.open(Host: host, Port: port, TrapPort: trapport, Community: community) do |mgr|
|
51
|
+
yield mgr
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Genesis
|
2
|
+
# Use some metaprogramming to DRY up protocol definition
|
3
|
+
module Protocol
|
4
|
+
def self.included(base) # rubocop:disable Metrics/MethodLength
|
5
|
+
class << base
|
6
|
+
def included(base)
|
7
|
+
protocol = self
|
8
|
+
base.define_singleton_method(:protocol) { protocol.protocol }
|
9
|
+
end
|
10
|
+
|
11
|
+
def server
|
12
|
+
Kernel.const_get((to_s.split('::')[0..-2] << 'Server').join('::'))
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_block
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
## We can dynamically adjust the threadpool like below, if we want to.
|
2
|
+
## It should be possible to make the threadpool elastic very easily, though this may impact performance
|
3
|
+
# module EventMachine
|
4
|
+
# def self.adjust_threadpool
|
5
|
+
#
|
6
|
+
# unless @threadpool.size == @threadpool_size.to_i
|
7
|
+
# spawn_threadpool
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# end
|
11
|
+
|
12
|
+
require 'em-synchrony'
|
13
|
+
|
14
|
+
module Genesis
|
15
|
+
# Main reactor class
|
16
|
+
# rubocop:disable ClassLength
|
17
|
+
class Reactor
|
18
|
+
def initialize(**kwargs)
|
19
|
+
reset
|
20
|
+
@poolsize = kwargs[:threads] || 100 # maximum concurrency - larger = longer boot and shutdown time
|
21
|
+
@protocols = kwargs[:protocols] || {}
|
22
|
+
@views = kwargs[:views] || {}
|
23
|
+
register_handlers(kwargs[:handlers] || {})
|
24
|
+
register_agents(kwargs[:agents] || [])
|
25
|
+
end
|
26
|
+
|
27
|
+
# Convenience wrapper to run indefinitely as daemon
|
28
|
+
def start
|
29
|
+
EM.synchrony do
|
30
|
+
run
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Run the reactor - must be called from EM.run or EM.synchrony
|
35
|
+
def run
|
36
|
+
if running?
|
37
|
+
initialize_protocols
|
38
|
+
initialize_threadpool
|
39
|
+
initialize_sighandlers
|
40
|
+
return true
|
41
|
+
else
|
42
|
+
fail 'Must run from within reactor'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check if the reactor is running
|
47
|
+
def running?
|
48
|
+
EM.reactor_running?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Stop the reactor
|
52
|
+
def stop
|
53
|
+
puts 'Shutting down'
|
54
|
+
EM.stop
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Reset / initialize instance variables
|
60
|
+
def reset
|
61
|
+
@protocols = {}
|
62
|
+
@servers = {}
|
63
|
+
@routes = {}
|
64
|
+
@subscribers = {}
|
65
|
+
@agents = {}
|
66
|
+
@channels = {}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Initialize signal handlers to cleanly shutdown
|
70
|
+
def initialize_sighandlers
|
71
|
+
trap(:INT) do
|
72
|
+
stop
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Initialize servers for each protocol
|
78
|
+
def initialize_servers
|
79
|
+
@protocols.each do |protocol, _|
|
80
|
+
server = @servers[protocol.protocol]
|
81
|
+
block = server[:start]
|
82
|
+
server[:server].start(server[:port], @routes[protocol.protocol], views: @views, channel: @channels[protocol.protocol], &block)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Initialize protocols to be handled
|
87
|
+
def initialize_protocols
|
88
|
+
@protocols.each do |protocol, _|
|
89
|
+
server = protocol.server
|
90
|
+
@servers[protocol.protocol] = { server: server, port: @protocols[protocol], start: protocol.start_block }
|
91
|
+
@channels[protocol.protocol] = EM::Channel.new
|
92
|
+
end
|
93
|
+
initialize_servers
|
94
|
+
initialize_subscribers
|
95
|
+
initialize_agents
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the initial size of the threadpool
|
99
|
+
def initialize_threadpool
|
100
|
+
EM.threadpool_size = @poolsize
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sets up agents
|
104
|
+
def initialize_agents
|
105
|
+
@agents.each do |protocol, agents|
|
106
|
+
agents.each do |data|
|
107
|
+
EM.add_periodic_timer(data[:interval]) do
|
108
|
+
EM.defer { data[:block].call(@channels[protocol]) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Set up subscriptions to channels
|
115
|
+
def initialize_subscribers
|
116
|
+
@subscribers.each do |type, subscribers|
|
117
|
+
channel = @channels[type]
|
118
|
+
if channel
|
119
|
+
subscribers.each do |subscriber|
|
120
|
+
channel.subscribe do |message|
|
121
|
+
EM.defer { subscriber.call(message) }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Registers all handlers
|
129
|
+
def register_handlers(handlers)
|
130
|
+
handlers.each do |handler|
|
131
|
+
(handler.routes || {}).each do |match, data|
|
132
|
+
register_route(handler.protocol, data[:verb], match, data[:opts], data[:block])
|
133
|
+
end
|
134
|
+
|
135
|
+
(handler.subscribers || []).each do |data|
|
136
|
+
register_subscriber(handler.protocol, data[:block])
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Registers all agenst
|
142
|
+
def register_agents(agents)
|
143
|
+
agents.each do |agent|
|
144
|
+
(agent.agents || {}).each do |data|
|
145
|
+
register_agent(agent.protocol, data)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Registers a route for a given protocol
|
151
|
+
def register_route(protocol, verb, match, args, block)
|
152
|
+
@routes[protocol] ||= {}
|
153
|
+
@routes[protocol][verb] ||= {}
|
154
|
+
@routes[protocol][verb][match] = { block: block, args: args }
|
155
|
+
end
|
156
|
+
|
157
|
+
# Registers a handler for a given protocol
|
158
|
+
def register_subscriber(protocol, block)
|
159
|
+
@subscribers[protocol] ||= []
|
160
|
+
@subscribers[protocol] << block
|
161
|
+
end
|
162
|
+
|
163
|
+
# Registers an agent for a given protocol
|
164
|
+
def register_agent(protocol, method)
|
165
|
+
@agents[protocol] ||= []
|
166
|
+
@agents[protocol] << method
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module Genesis
|
4
|
+
# Abstract base class for all servers
|
5
|
+
module Server
|
6
|
+
attr_accessor :handle_routes, :channel
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# Methods to be injected onto the class
|
13
|
+
module ClassMethods
|
14
|
+
def start(port, routes, **kwargs, &block)
|
15
|
+
@port = port
|
16
|
+
@handle_routes = routes || []
|
17
|
+
@channel = kwargs[:channel]
|
18
|
+
@args = kwargs
|
19
|
+
|
20
|
+
# Allow a custom, non EM, server to be run
|
21
|
+
if block_given? && block
|
22
|
+
yield
|
23
|
+
else
|
24
|
+
default_start
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def default_start
|
31
|
+
# But default to an EM server if nothing else is provided
|
32
|
+
EM.start_server '0.0.0.0', @port, self do |conn|
|
33
|
+
conn.channel = @channel
|
34
|
+
conn.handle_routes = @handle_routes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: genesisreactor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dale Hamel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: em-synchrony
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: async_sinatra
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thin
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.6.3
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.6.3
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: snmp
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.2.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.2.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.2.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.2.0
|
83
|
+
description: GenesisReactor provides a protocol agnostic framework for implementing
|
84
|
+
simple pub/sub message production and handling.
|
85
|
+
email: dale.hamel@srvthe.net
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/genesis/agent.rb
|
91
|
+
- lib/genesis/handler.rb
|
92
|
+
- lib/genesis/protocol.rb
|
93
|
+
- lib/genesis/protocol/echo.rb
|
94
|
+
- lib/genesis/protocol/echo/agent.rb
|
95
|
+
- lib/genesis/protocol/echo/handler.rb
|
96
|
+
- lib/genesis/protocol/echo/protocol.rb
|
97
|
+
- lib/genesis/protocol/echo/server.rb
|
98
|
+
- lib/genesis/protocol/http.rb
|
99
|
+
- lib/genesis/protocol/http/async_monkeypatch.rb
|
100
|
+
- lib/genesis/protocol/http/handler.rb
|
101
|
+
- lib/genesis/protocol/http/protocol.rb
|
102
|
+
- lib/genesis/protocol/http/server.rb
|
103
|
+
- lib/genesis/protocol/snmp.rb
|
104
|
+
- lib/genesis/protocol/snmp/agent.rb
|
105
|
+
- lib/genesis/protocol/snmp/handler.rb
|
106
|
+
- lib/genesis/protocol/snmp/protocol.rb
|
107
|
+
- lib/genesis/protocol/snmp/server.rb
|
108
|
+
- lib/genesis/reactor.rb
|
109
|
+
- lib/genesis/server.rb
|
110
|
+
- lib/genesisreactor.rb
|
111
|
+
homepage: http://rubygems.org/gems/genesisreactor
|
112
|
+
licenses:
|
113
|
+
- MIT
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 2.4.6
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: Event driven infrastructor automation framework
|
135
|
+
test_files: []
|
136
|
+
has_rdoc:
|