ezmobius-nanite 0.4.0 → 0.4.1.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.
- data/README.rdoc +70 -20
- data/Rakefile +1 -1
- data/bin/nanite-agent +34 -8
- data/bin/nanite-mapper +18 -8
- data/lib/nanite.rb +71 -0
- data/lib/nanite/actor.rb +60 -0
- data/lib/nanite/actor_registry.rb +24 -0
- data/lib/nanite/admin.rb +138 -0
- data/lib/nanite/agent.rb +250 -0
- data/lib/nanite/amqp.rb +47 -0
- data/lib/nanite/cluster.rb +203 -0
- data/lib/nanite/config.rb +102 -0
- data/lib/nanite/console.rb +39 -0
- data/lib/nanite/daemonize.rb +13 -0
- data/lib/nanite/dispatcher.rb +90 -0
- data/lib/nanite/identity.rb +16 -0
- data/lib/nanite/job.rb +104 -0
- data/lib/nanite/local_state.rb +34 -0
- data/lib/nanite/log.rb +64 -0
- data/lib/nanite/log/formatter.rb +39 -0
- data/lib/nanite/mapper.rb +277 -0
- data/lib/nanite/mapper_proxy.rb +56 -0
- data/lib/nanite/packets.rb +231 -0
- data/lib/nanite/pid_file.rb +52 -0
- data/lib/nanite/reaper.rb +38 -0
- data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
- data/lib/nanite/security/certificate.rb +55 -0
- data/lib/nanite/security/certificate_cache.rb +66 -0
- data/lib/nanite/security/distinguished_name.rb +34 -0
- data/lib/nanite/security/encrypted_document.rb +46 -0
- data/lib/nanite/security/rsa_key_pair.rb +53 -0
- data/lib/nanite/security/secure_serializer.rb +67 -0
- data/lib/nanite/security/signature.rb +40 -0
- data/lib/nanite/security/static_certificate_store.rb +35 -0
- data/lib/nanite/security_provider.rb +47 -0
- data/lib/nanite/serializer.rb +52 -0
- data/lib/nanite/state.rb +164 -0
- data/lib/nanite/streaming.rb +125 -0
- data/lib/nanite/util.rb +51 -0
- data/spec/actor_registry_spec.rb +62 -0
- data/spec/actor_spec.rb +59 -0
- data/spec/agent_spec.rb +235 -0
- data/spec/cached_certificate_store_proxy_spec.rb +34 -0
- data/spec/certificate_cache_spec.rb +49 -0
- data/spec/certificate_spec.rb +27 -0
- data/spec/cluster_spec.rb +300 -0
- data/spec/dispatcher_spec.rb +136 -0
- data/spec/distinguished_name_spec.rb +24 -0
- data/spec/encrypted_document_spec.rb +21 -0
- data/spec/job_spec.rb +219 -0
- data/spec/local_state_spec.rb +112 -0
- data/spec/packet_spec.rb +218 -0
- data/spec/rsa_key_pair_spec.rb +33 -0
- data/spec/secure_serializer_spec.rb +41 -0
- data/spec/serializer_spec.rb +107 -0
- data/spec/signature_spec.rb +30 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/static_certificate_store_spec.rb +30 -0
- data/spec/util_spec.rb +63 -0
- metadata +63 -2
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Nanite
|
5
|
+
class Log
|
6
|
+
class Formatter < Logger::Formatter
|
7
|
+
@@show_time = true
|
8
|
+
|
9
|
+
def self.show_time=(show=false)
|
10
|
+
@@show_time = show
|
11
|
+
end
|
12
|
+
|
13
|
+
# Prints a log message as '[time] severity: message' if Nanite::Log::Formatter.show_time == true.
|
14
|
+
# Otherwise, doesn't print the time.
|
15
|
+
def call(severity, time, progname, msg)
|
16
|
+
if @@show_time
|
17
|
+
sprintf("[%s] %s: %s\n", time.rfc2822(), severity, msg2str(msg))
|
18
|
+
else
|
19
|
+
sprintf("%s: %s\n", severity, msg2str(msg))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Converts some argument to a Logger.severity() call to a string. Regular strings pass through like
|
24
|
+
# normal, Exceptions get formatted as "message (class)\nbacktrace", and other random stuff gets
|
25
|
+
# put through "object.inspect"
|
26
|
+
def msg2str(msg)
|
27
|
+
case msg
|
28
|
+
when ::String
|
29
|
+
msg
|
30
|
+
when ::Exception
|
31
|
+
"#{ msg.message } (#{ msg.class })\n" <<
|
32
|
+
(msg.backtrace || []).join("\n")
|
33
|
+
else
|
34
|
+
msg.inspect
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
module Nanite
|
2
|
+
# Mappers are control nodes in nanite clusters. Nanite clusters
|
3
|
+
# can follow peer-to-peer model of communication as well as client-server,
|
4
|
+
# and mappers are nodes that know who to send work requests to agents.
|
5
|
+
#
|
6
|
+
# Mappers can reside inside a front end web application written in Merb/Rails
|
7
|
+
# and distribute heavy lifting to actors that register with the mapper as soon
|
8
|
+
# as they go online.
|
9
|
+
#
|
10
|
+
# Each mapper tracks nanites registered with it. It periodically checks
|
11
|
+
# when the last time a certain nanite sent a heartbeat notification,
|
12
|
+
# and removes those that have timed out from the list of available workers.
|
13
|
+
# As soon as a worker goes back online again it re-registers itself
|
14
|
+
# and the mapper adds it to the list and makes it available to
|
15
|
+
# be called again.
|
16
|
+
#
|
17
|
+
# This makes Nanite clusters self-healing and immune to individual node
|
18
|
+
# failures.
|
19
|
+
class Mapper
|
20
|
+
include AMQPHelper
|
21
|
+
include ConsoleHelper
|
22
|
+
include DaemonizeHelper
|
23
|
+
|
24
|
+
attr_reader :cluster, :identity, :job_warden, :options, :serializer, :amq
|
25
|
+
|
26
|
+
DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({:user => 'mapper', :identity => Identity.generate, :agent_timeout => 15,
|
27
|
+
:offline_redelivery_frequency => 10, :persistent => false, :offline_failsafe => false}) unless defined?(DEFAULT_OPTIONS)
|
28
|
+
|
29
|
+
# Initializes a new mapper and establishes
|
30
|
+
# AMQP connection. This must be used inside EM.run block or if EventMachine reactor
|
31
|
+
# is already started, for instance, by a Thin server that your Merb/Rails
|
32
|
+
# application runs on.
|
33
|
+
#
|
34
|
+
# Mapper options:
|
35
|
+
#
|
36
|
+
# identity : identity of this mapper, may be any string
|
37
|
+
#
|
38
|
+
# format : format to use for packets serialization. Can be :marshal, :json or :yaml or :secure.
|
39
|
+
# Defaults to Ruby's Marshall format. For interoperability with
|
40
|
+
# AMQP clients implemented in other languages, use JSON.
|
41
|
+
#
|
42
|
+
# Note that Nanite uses JSON gem,
|
43
|
+
# and ActiveSupport's JSON encoder may cause clashes
|
44
|
+
# if ActiveSupport is loaded after JSON gem.
|
45
|
+
#
|
46
|
+
# Also using the secure format requires prior initialization of the serializer, see
|
47
|
+
# SecureSerializer.init
|
48
|
+
#
|
49
|
+
# log_level : the verbosity of logging, can be debug, info, warn, error or fatal.
|
50
|
+
#
|
51
|
+
# agent_timeout : how long to wait before an agent is considered to be offline
|
52
|
+
# and thus removed from the list of available agents.
|
53
|
+
#
|
54
|
+
# log_dir : log file path, defaults to the current working directory.
|
55
|
+
#
|
56
|
+
# console : true tells mapper to start interactive console
|
57
|
+
#
|
58
|
+
# daemonize : true tells mapper to daemonize
|
59
|
+
#
|
60
|
+
# pid_dir : path to the directory where the agent stores its pid file (only if daemonized)
|
61
|
+
# defaults to the root or the current working directory.
|
62
|
+
#
|
63
|
+
# offline_redelivery_frequency : The frequency in seconds that messages stored in the offline queue will be retrieved
|
64
|
+
# for attempted redelivery to the nanites. Default is 10 seconds.
|
65
|
+
#
|
66
|
+
# persistent : true instructs the AMQP broker to save messages to persistent storage so that they aren't lost when the
|
67
|
+
# broker is restarted. Default is false. Can be overriden on a per-message basis using the request and push methods.
|
68
|
+
#
|
69
|
+
# secure : use Security features of rabbitmq to restrict nanites to themselves
|
70
|
+
#
|
71
|
+
# Connection options:
|
72
|
+
#
|
73
|
+
# vhost : AMQP broker vhost that should be used
|
74
|
+
#
|
75
|
+
# user : AMQP broker user
|
76
|
+
#
|
77
|
+
# pass : AMQP broker password
|
78
|
+
#
|
79
|
+
# host : host AMQP broker (or node of interest) runs on,
|
80
|
+
# defaults to 0.0.0.0
|
81
|
+
#
|
82
|
+
# port : port AMQP broker (or node of interest) runs on,
|
83
|
+
# this defaults to 5672, port used by some widely
|
84
|
+
# used AMQP brokers (RabbitMQ and ZeroMQ)
|
85
|
+
#
|
86
|
+
# @api :public:
|
87
|
+
def self.start(options = {})
|
88
|
+
mapper = new(options)
|
89
|
+
mapper.run
|
90
|
+
mapper
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize(options)
|
94
|
+
@options = DEFAULT_OPTIONS.clone.merge(options)
|
95
|
+
root = options[:root] || @options[:root]
|
96
|
+
custom_config = if root
|
97
|
+
file = File.expand_path(File.join(root, 'config.yml'))
|
98
|
+
File.exists?(file) ? (YAML.load(IO.read(file)) || {}) : {}
|
99
|
+
else
|
100
|
+
{}
|
101
|
+
end
|
102
|
+
options.delete(:identity) unless options[:identity]
|
103
|
+
@options.update(custom_config.merge(options))
|
104
|
+
@identity = "mapper-#{@options[:identity]}"
|
105
|
+
@options[:file_root] ||= File.join(@options[:root], 'files')
|
106
|
+
@options.freeze
|
107
|
+
end
|
108
|
+
|
109
|
+
def run
|
110
|
+
log_path = false
|
111
|
+
if @options[:daemonize]
|
112
|
+
log_path = (@options[:log_dir] || @options[:root] || Dir.pwd)
|
113
|
+
end
|
114
|
+
Nanite::Log.init(@identity, log_path)
|
115
|
+
Nanite::Log.level = @options[:log_level] if @options[:log_level]
|
116
|
+
@serializer = Serializer.new(@options[:format])
|
117
|
+
pid_file = PidFile.new(@identity, @options)
|
118
|
+
pid_file.check
|
119
|
+
if @options[:daemonize]
|
120
|
+
daemonize
|
121
|
+
pid_file.write
|
122
|
+
at_exit { pid_file.remove }
|
123
|
+
end
|
124
|
+
@amq = start_amqp(@options)
|
125
|
+
@job_warden = JobWarden.new(@serializer)
|
126
|
+
@cluster = Cluster.new(@amq, @options[:agent_timeout], @options[:identity], @serializer, self, @options[:redis])
|
127
|
+
Nanite::Log.info('starting mapper')
|
128
|
+
setup_queues
|
129
|
+
start_console if @options[:console] && !@options[:daemonize]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Make a nanite request which expects a response.
|
133
|
+
#
|
134
|
+
# ==== Parameters
|
135
|
+
# type<String>:: The dispatch route for the request
|
136
|
+
# payload<Object>:: Payload to send. This will get marshalled en route
|
137
|
+
#
|
138
|
+
# ==== Options
|
139
|
+
# :selector<Symbol>:: Method for selecting an actor. Default is :least_loaded.
|
140
|
+
# :least_loaded:: Pick the nanite which has the lowest load.
|
141
|
+
# :all:: Send the request to all nanites which respond to the service.
|
142
|
+
# :random:: Randomly pick a nanite.
|
143
|
+
# :rr: Select a nanite according to round robin ordering.
|
144
|
+
# :target<String>:: Select a specific nanite via identity, rather than using
|
145
|
+
# a selector.
|
146
|
+
# :offline_failsafe<Boolean>:: Store messages in an offline queue when all
|
147
|
+
# the nanites are offline. Messages will be redelivered when nanites come online.
|
148
|
+
# Default is false unless the mapper was started with the --offline-failsafe flag.
|
149
|
+
# :persistent<Boolean>:: Instructs the AMQP broker to save the message to persistent
|
150
|
+
# storage so that it isnt lost when the broker is restarted.
|
151
|
+
# Default is false unless the mapper was started with the --persistent flag.
|
152
|
+
# :intermediate_handler:: Takes a lambda to call when an IntermediateMessage
|
153
|
+
# event arrives from a nanite. If passed a Hash, hash keys should correspond to
|
154
|
+
# the IntermediateMessage keys provided by the nanite, and each should have a value
|
155
|
+
# that is a lambda/proc taking the parameters specified here. Can supply a key '*'
|
156
|
+
# as a catch-all for unmatched keys.
|
157
|
+
#
|
158
|
+
# ==== Block Parameters for intermediate_handler
|
159
|
+
# key<String>:: array of unique keys for which intermediate state has been received
|
160
|
+
# since the last call to this block.
|
161
|
+
# nanite<String>:: nanite which sent the message.
|
162
|
+
# state:: most recently delivered intermediate state for the key provided.
|
163
|
+
# job:: (optional) -- if provided, this parameter gets the whole job object, if there's
|
164
|
+
# a reason to do more complex work with the job.
|
165
|
+
#
|
166
|
+
# ==== Block Parameters
|
167
|
+
# :results<Object>:: The returned value from the nanite actor.
|
168
|
+
#
|
169
|
+
# @api :public:
|
170
|
+
def request(type, payload = '', opts = {}, &blk)
|
171
|
+
request = build_deliverable(Request, type, payload, opts)
|
172
|
+
send_request(request, opts, &blk)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Send request with pre-built request instance
|
176
|
+
def send_request(request, opts = {}, &blk)
|
177
|
+
request.reply_to = identity
|
178
|
+
intm_handler = opts.delete(:intermediate_handler)
|
179
|
+
targets = cluster.targets_for(request)
|
180
|
+
if !targets.empty?
|
181
|
+
job = job_warden.new_job(request, targets, intm_handler, blk)
|
182
|
+
cluster.route(request, job.targets)
|
183
|
+
job
|
184
|
+
elsif opts.key?(:offline_failsafe) ? opts[:offline_failsafe] : options[:offline_failsafe]
|
185
|
+
job_warden.new_job(request, [], intm_handler, blk)
|
186
|
+
cluster.publish(request, 'mapper-offline')
|
187
|
+
:offline
|
188
|
+
else
|
189
|
+
false
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Make a nanite request which does not expect a response.
|
194
|
+
#
|
195
|
+
# ==== Parameters
|
196
|
+
# type<String>:: The dispatch route for the request
|
197
|
+
# payload<Object>:: Payload to send. This will get marshalled en route
|
198
|
+
#
|
199
|
+
# ==== Options
|
200
|
+
# :selector<Symbol>:: Method for selecting an actor. Default is :least_loaded.
|
201
|
+
# :least_loaded:: Pick the nanite which has the lowest load.
|
202
|
+
# :all:: Send the request to all nanites which respond to the service.
|
203
|
+
# :random:: Randomly pick a nanite.
|
204
|
+
# :rr: Select a nanite according to round robin ordering.
|
205
|
+
# :offline_failsafe<Boolean>:: Store messages in an offline queue when all
|
206
|
+
# the nanites are offline. Messages will be redelivered when nanites come online.
|
207
|
+
# Default is false unless the mapper was started with the --offline-failsafe flag.
|
208
|
+
# :persistent<Boolean>:: Instructs the AMQP broker to save the message to persistent
|
209
|
+
# storage so that it isnt lost when the broker is restarted.
|
210
|
+
# Default is false unless the mapper was started with the --persistent flag.
|
211
|
+
#
|
212
|
+
# @api :public:
|
213
|
+
def push(type, payload = '', opts = {})
|
214
|
+
push = build_deliverable(Push, type, payload, opts)
|
215
|
+
targets = cluster.targets_for(push)
|
216
|
+
if !targets.empty?
|
217
|
+
cluster.route(push, targets)
|
218
|
+
true
|
219
|
+
elsif opts.key?(:offline_failsafe) ? opts[:offline_failsafe] : options[:offline_failsafe]
|
220
|
+
cluster.publish(push, 'mapper-offline')
|
221
|
+
:offline
|
222
|
+
else
|
223
|
+
false
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
def build_deliverable(deliverable_type, type, payload, opts)
|
230
|
+
deliverable = deliverable_type.new(type, payload, opts)
|
231
|
+
deliverable.from = identity
|
232
|
+
deliverable.token = Identity.generate
|
233
|
+
deliverable.persistent = opts.key?(:persistent) ? opts[:persistent] : options[:persistent]
|
234
|
+
deliverable
|
235
|
+
end
|
236
|
+
|
237
|
+
def setup_queues
|
238
|
+
setup_offline_queue
|
239
|
+
setup_message_queue
|
240
|
+
end
|
241
|
+
|
242
|
+
def setup_offline_queue
|
243
|
+
offline_queue = amq.queue('mapper-offline', :durable => true)
|
244
|
+
offline_queue.subscribe(:ack => true) do |info, deliverable|
|
245
|
+
deliverable = serializer.load(deliverable)
|
246
|
+
targets = cluster.targets_for(deliverable)
|
247
|
+
unless targets.empty?
|
248
|
+
info.ack
|
249
|
+
if deliverable.kind_of?(Request)
|
250
|
+
if job = job_warden.jobs[deliverable.token]
|
251
|
+
job.targets = targets
|
252
|
+
else
|
253
|
+
deliverable.reply_to = identity
|
254
|
+
job_warden.new_job(deliverable, targets)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
cluster.route(deliverable, targets)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
EM.add_periodic_timer(options[:offline_redelivery_frequency]) { offline_queue.recover }
|
262
|
+
end
|
263
|
+
|
264
|
+
def setup_message_queue
|
265
|
+
amq.queue(identity, :exclusive => true).bind(amq.fanout(identity)).subscribe do |msg|
|
266
|
+
begin
|
267
|
+
msg = serializer.load(msg)
|
268
|
+
Nanite::Log.debug("got result from #{msg.from}: #{msg.results.inspect}")
|
269
|
+
job_warden.process(msg)
|
270
|
+
rescue Exception => e
|
271
|
+
Nanite::Log.error("Error handling result: #{e.message}")
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Nanite
|
2
|
+
|
3
|
+
# This class allows sending requests to nanite agents without having
|
4
|
+
# to run a local mapper.
|
5
|
+
# It is used by Actor.request which can be used by actors than need
|
6
|
+
# to send requests to remote agents.
|
7
|
+
# All requests go through the mapper for security purposes.
|
8
|
+
class MapperProxy
|
9
|
+
|
10
|
+
$:.push File.dirname(__FILE__)
|
11
|
+
require 'amqp'
|
12
|
+
|
13
|
+
include AMQPHelper
|
14
|
+
|
15
|
+
attr_accessor :pending_requests, :identity, :options, :amqp, :serializer
|
16
|
+
|
17
|
+
# Accessor for actor
|
18
|
+
def self.instance
|
19
|
+
@@instance
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(id, opts)
|
23
|
+
@options = opts || {}
|
24
|
+
@identity = id
|
25
|
+
@pending_requests = {}
|
26
|
+
@amqp = start_amqp(options)
|
27
|
+
@serializer = Serializer.new(options[:format])
|
28
|
+
@@instance = self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Send request to given agent through the mapper
|
32
|
+
def request(type, payload = '', opts = {}, &blk)
|
33
|
+
raise "Mapper proxy not initialized" unless identity && options
|
34
|
+
request = Request.new(type, payload, opts)
|
35
|
+
request.from = identity
|
36
|
+
request.token = Identity.generate
|
37
|
+
request.persistent = opts.key?(:persistent) ? opts[:persistent] : options[:persistent]
|
38
|
+
pending_requests[request.token] =
|
39
|
+
{ :intermediate_handler => opts[:intermediate_handler], :result_handler => blk }
|
40
|
+
amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(request))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Handle intermediary result
|
44
|
+
def handle_intermediate_result(res)
|
45
|
+
handlers = pending_requests[res.token]
|
46
|
+
handlers[:intermediate_handler].call(res) if handlers && handlers[:intermediate_handler]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Handle final result
|
50
|
+
def handle_result(res)
|
51
|
+
handlers = pending_requests.delete(res.token)
|
52
|
+
handlers[:result_handler].call(res) if handlers && handlers[:result_handler]
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
module Nanite
|
2
|
+
# Base class for all Nanite packets,
|
3
|
+
# knows how to dump itself to JSON
|
4
|
+
class Packet
|
5
|
+
def initialize
|
6
|
+
raise NotImplementedError.new("#{self.class.name} is an abstract class.")
|
7
|
+
end
|
8
|
+
def to_json(*a)
|
9
|
+
{
|
10
|
+
'json_class' => self.class.name,
|
11
|
+
'data' => instance_variables.inject({}) {|m,ivar| m[ivar.sub(/@/,'')] = instance_variable_get(ivar); m }
|
12
|
+
}.to_json(*a)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# packet that means start of a file transfer
|
17
|
+
# operation
|
18
|
+
class FileStart < Packet
|
19
|
+
attr_accessor :filename, :token, :dest
|
20
|
+
def initialize(filename, dest, token)
|
21
|
+
@filename = filename
|
22
|
+
@dest = dest
|
23
|
+
@token = token
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.json_create(o)
|
27
|
+
i = o['data']
|
28
|
+
new(i['filename'], i['dest'], i['token'])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# packet that means end of a file transfer
|
33
|
+
# operation
|
34
|
+
class FileEnd < Packet
|
35
|
+
attr_accessor :token, :meta
|
36
|
+
def initialize(token, meta)
|
37
|
+
@token = token
|
38
|
+
@meta = meta
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.json_create(o)
|
42
|
+
i = o['data']
|
43
|
+
new(i['token'], i['meta'])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# packet that carries data chunks during a file transfer
|
48
|
+
class FileChunk < Packet
|
49
|
+
attr_accessor :chunk, :token
|
50
|
+
def initialize(token, chunk=nil)
|
51
|
+
@chunk = chunk
|
52
|
+
@token = token
|
53
|
+
end
|
54
|
+
def self.json_create(o)
|
55
|
+
i = o['data']
|
56
|
+
new(i['token'], i['chunk'])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# packet that means a work request from mapper
|
61
|
+
# to actor node
|
62
|
+
#
|
63
|
+
# type is a service name
|
64
|
+
# payload is arbitrary data that is transferred from mapper to actor
|
65
|
+
#
|
66
|
+
# Options:
|
67
|
+
# from is sender identity
|
68
|
+
# token is a generated request id that mapper uses to identify replies
|
69
|
+
# reply_to is identity of the node actor replies to, usually a mapper itself
|
70
|
+
# selector is the selector used to route the request
|
71
|
+
# target is the target nanite for the request
|
72
|
+
# persistent signifies if this request should be saved to persistent storage by the AMQP broker
|
73
|
+
class Request < Packet
|
74
|
+
attr_accessor :from, :payload, :type, :token, :reply_to, :selector, :target, :persistent, :tags
|
75
|
+
DEFAULT_OPTIONS = {:selector => :least_loaded}
|
76
|
+
def initialize(type, payload, opts={})
|
77
|
+
opts = DEFAULT_OPTIONS.merge(opts)
|
78
|
+
@type = type
|
79
|
+
@payload = payload
|
80
|
+
@from = opts[:from]
|
81
|
+
@token = opts[:token]
|
82
|
+
@reply_to = opts[:reply_to]
|
83
|
+
@selector = opts[:selector]
|
84
|
+
@target = opts[:target]
|
85
|
+
@persistent = opts[:persistent]
|
86
|
+
@tags = opts[:tags] || []
|
87
|
+
end
|
88
|
+
def self.json_create(o)
|
89
|
+
i = o['data']
|
90
|
+
new(i['type'], i['payload'], {:from => i['from'], :token => i['token'], :reply_to => i['reply_to'], :selector => i['selector'],
|
91
|
+
:target => i['target'], :persistent => i['persistent'], :tags => i['tags']})
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# packet that means a work push from mapper
|
96
|
+
# to actor node
|
97
|
+
#
|
98
|
+
# type is a service name
|
99
|
+
# payload is arbitrary data that is transferred from mapper to actor
|
100
|
+
#
|
101
|
+
# Options:
|
102
|
+
# from is sender identity
|
103
|
+
# token is a generated request id that mapper uses to identify replies
|
104
|
+
# selector is the selector used to route the request
|
105
|
+
# target is the target nanite for the request
|
106
|
+
# persistent signifies if this request should be saved to persistent storage by the AMQP broker
|
107
|
+
class Push < Packet
|
108
|
+
attr_accessor :from, :payload, :type, :token, :selector, :target, :persistent, :tags
|
109
|
+
DEFAULT_OPTIONS = {:selector => :least_loaded}
|
110
|
+
def initialize(type, payload, opts={})
|
111
|
+
opts = DEFAULT_OPTIONS.merge(opts)
|
112
|
+
@type = type
|
113
|
+
@payload = payload
|
114
|
+
@from = opts[:from]
|
115
|
+
@token = opts[:token]
|
116
|
+
@selector = opts[:selector]
|
117
|
+
@target = opts[:target]
|
118
|
+
@persistent = opts[:persistent]
|
119
|
+
@tags = opts[:tags] || []
|
120
|
+
end
|
121
|
+
def self.json_create(o)
|
122
|
+
i = o['data']
|
123
|
+
new(i['type'], i['payload'], {:from => i['from'], :token => i['token'], :selector => i['selector'],
|
124
|
+
:target => i['target'], :persistent => i['persistent'], :tags => i['tags']})
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# packet that means a work result notification sent from actor to mapper
|
129
|
+
#
|
130
|
+
# from is sender identity
|
131
|
+
# results is arbitrary data that is transferred from actor, a result of actor's work
|
132
|
+
# token is a generated request id that mapper uses to identify replies
|
133
|
+
# to is identity of the node result should be delivered to
|
134
|
+
class Result < Packet
|
135
|
+
attr_accessor :token, :results, :to, :from
|
136
|
+
def initialize(token, to, results, from)
|
137
|
+
@token = token
|
138
|
+
@to = to
|
139
|
+
@from = from
|
140
|
+
@results = results
|
141
|
+
end
|
142
|
+
def self.json_create(o)
|
143
|
+
i = o['data']
|
144
|
+
new(i['token'], i['to'], i['results'], i['from'])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# packet that means an intermediate status notification sent from actor to mapper. is appended to a list of messages matching messagekey.
|
149
|
+
#
|
150
|
+
# from is sender identity
|
151
|
+
# messagekey is a string that can become part of a redis key, which identifies the name under which the message is stored
|
152
|
+
# message is arbitrary data that is transferred from actor, an intermediate result of actor's work
|
153
|
+
# token is a generated request id that mapper uses to identify replies
|
154
|
+
# to is identity of the node result should be delivered to
|
155
|
+
class IntermediateMessage < Packet
|
156
|
+
attr_accessor :token, :messagekey, :message, :to, :from
|
157
|
+
def initialize(token, to, from, messagekey, message)
|
158
|
+
@token = token
|
159
|
+
@to = to
|
160
|
+
@from = from
|
161
|
+
@messagekey = messagekey
|
162
|
+
@message = message
|
163
|
+
end
|
164
|
+
def self.json_create(o)
|
165
|
+
i = o['data']
|
166
|
+
new(i['token'], i['to'], i['from'], i['messagekey'], i['message'])
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# packet that means an availability notification sent from actor to mapper
|
171
|
+
#
|
172
|
+
# from is sender identity
|
173
|
+
# services is a list of services provided by the node
|
174
|
+
# status is a load of the node by default, but may be any criteria
|
175
|
+
# agent may use to report it's availability, load, etc
|
176
|
+
class Register < Packet
|
177
|
+
attr_accessor :identity, :services, :status, :tags
|
178
|
+
def initialize(identity, services, status, tags)
|
179
|
+
@status = status
|
180
|
+
@tags = tags
|
181
|
+
@identity = identity
|
182
|
+
@services = services
|
183
|
+
end
|
184
|
+
def self.json_create(o)
|
185
|
+
i = o['data']
|
186
|
+
new(i['identity'], i['services'], i['status'], i['tags'])
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# packet that means deregister an agent from the mappers
|
191
|
+
#
|
192
|
+
# from is sender identity
|
193
|
+
class UnRegister < Packet
|
194
|
+
attr_accessor :identity
|
195
|
+
def initialize(identity)
|
196
|
+
@identity = identity
|
197
|
+
end
|
198
|
+
def self.json_create(o)
|
199
|
+
i = o['data']
|
200
|
+
new(i['identity'])
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# heartbeat packet
|
205
|
+
#
|
206
|
+
# identity is sender's identity
|
207
|
+
# status is sender's status (see Register packet documentation)
|
208
|
+
class Ping < Packet
|
209
|
+
attr_accessor :identity, :status
|
210
|
+
def initialize(identity, status)
|
211
|
+
@status = status
|
212
|
+
@identity = identity
|
213
|
+
end
|
214
|
+
def self.json_create(o)
|
215
|
+
i = o['data']
|
216
|
+
new(i['identity'], i['status'])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# packet that is sent by workers to the mapper
|
221
|
+
# when worker initially comes online to advertise
|
222
|
+
# it's services
|
223
|
+
class Advertise < Packet
|
224
|
+
def initialize
|
225
|
+
end
|
226
|
+
def self.json_create(o)
|
227
|
+
new
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|