cirrocumulus 0.9.2 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  = Cirrocumulus
2
2
 
3
- Cirrocumulus is an agent-based infrastructure management system. Each agent, running on a host, is responsible for its own problem and cooperates with the rest via XMPP as a transport and FIPA-ACL messages as a protocol.
3
+ Cirrocumulus is an agent-based infrastructure management engine. Each agent, running on a host, is responsible for its own problem and cooperates with the rest via XMPP as a transport and FIPA-ACL messages as a protocol.
@@ -23,6 +23,21 @@ class AbstractChannel
23
23
  # Query the other side if given proposition is true.
24
24
  #
25
25
  def query_if(sender, proposition, options = {}); end
26
+
27
+ #
28
+ # Agree to perform an action
29
+ #
30
+ def agree(sender, action, options = {}); end
31
+
32
+ #
33
+ # Refuse to perform an action, because of reason.
34
+ #
35
+ def refuse(sender, action, reason, options = {}); end
36
+
37
+ #
38
+ # Attempted to perform an action, but failed with given reason.
39
+ #
40
+ def failure(sender, action, reason, options = {}); end
26
41
  end
27
42
 
28
43
  #
@@ -48,6 +63,18 @@ class ThreadChannel < AbstractChannel
48
63
  def query_if(sender, proposition, options = {})
49
64
  @ontology.handle_query_if(sender, proposition, options)
50
65
  end
66
+
67
+ def agree(sender, action, options = {})
68
+ @ontology.handle_agree(sender, action, options)
69
+ end
70
+
71
+ def refuse(sender, action, reason, options = {})
72
+ @ontology.handle_refuse(sender, action, reason, options)
73
+ end
74
+
75
+ def failure(sender, action, reason, options = {})
76
+ @ontology.handle_failure(sender, action, reason, options)
77
+ end
51
78
  end
52
79
 
53
80
  #
@@ -76,6 +103,18 @@ class NetworkChannel < AbstractChannel
76
103
  client.queue(serializer.to_sexp(build_message(remote_jid, :query_if, proposition, options)))
77
104
  end
78
105
 
106
+ def agree(sender, action, options = {})
107
+ client.queue(serializer.to_sexp(build_message(remote_jid, :agree, action, options)))
108
+ end
109
+
110
+ def refuse(sender, action, reason, options = {})
111
+ client.queue(serializer.to_sexp(build_message(remote_jid, :refuse, [action, reason], options)))
112
+ end
113
+
114
+ def failure(sender, action, reason, options = {})
115
+ client.queue(serializer.to_sexp(build_message(remote_jid, :failure, [action, reason], options)))
116
+ end
117
+
79
118
  private
80
119
 
81
120
  attr_reader :client
@@ -6,7 +6,7 @@ require 'thread'
6
6
  class JabberIdentifier < RemoteIdentifier
7
7
  def initialize(jid)
8
8
  @jid = "#{Cirrocumulus::Environment.current.name}-#{jid}"
9
- @channel = JabberChannel.new('172.16.11.4', 'cirrocumulus')
9
+ @channel = JabberChannel.new()
10
10
  @channel.connect(@jid, 'q1w2e3r4')
11
11
  @thrd = Thread.new do
12
12
  parser = Sexpistol.new
@@ -58,6 +58,12 @@ class JabberIdentifier < RemoteIdentifier
58
58
  instance.handle_inform(id, action_content, options)
59
59
  when :request
60
60
  instance.handle_request(id, action_content, options)
61
+ when :agree
62
+ instance.handle_agree(id, action_content, options)
63
+ when :refuse
64
+ instance.handle_refuse(id, action_content[0], action_content[1], options)
65
+ when :failure
66
+ instance.handle_failure(id, action_content[0], action_content[1], options)
61
67
  end
62
68
  rescue Exception => ex
63
69
  puts ex.message
@@ -86,15 +92,27 @@ class JabberChannel
86
92
  def query_client(jid)
87
93
  @@jabber_clients.find {|c| c.jid == jid}
88
94
  end
95
+
96
+ def server(server)
97
+ @@server = server
98
+ end
99
+
100
+ def conference(conf)
101
+ @@conference = conf
102
+ end
103
+
104
+ def jid_suffix(suffix)
105
+ @@jid_suffix = suffix
106
+ end
89
107
  end
90
108
 
91
109
  attr_reader :jid
92
110
  attr_reader :conference
93
111
 
94
- def initialize(server, conference)
112
+ def initialize(server = nil, conference = nil)
95
113
  @jabber = nil
96
- @server = server
97
- @conference = conference
114
+ @server = server || @@server
115
+ @conference = conference || @@conference
98
116
  @send_q = Queue.new
99
117
  @recv_q = Queue.new
100
118
 
@@ -106,7 +124,7 @@ class JabberChannel
106
124
  end
107
125
 
108
126
  def connect(jid, password)
109
- @full_jid = "%s@%s" % [jid, @server]
127
+ @full_jid = "%s@%s" % [jid, @@jid_suffix || @server]
110
128
  @jid = jid
111
129
 
112
130
  puts "Using jid #{@jid}"
@@ -115,6 +133,7 @@ class JabberChannel
115
133
  @jabber = Jabber::Simple.new(@full_jid, password)
116
134
  rescue Jabber::ClientAuthenticationFailure => ex
117
135
  puts ex.class.name
136
+ puts ex.to_s
118
137
  client = Jabber::Client.new(@full_jid)
119
138
  client.connect()
120
139
  client.register(password)
@@ -163,6 +182,6 @@ class JabberChannel
163
182
  protected
164
183
 
165
184
  def join_conference(conference)
166
- @jabber.send!("<presence to='#{conference}@conference.#{@server}/#{@jid}' />")
185
+ @jabber.send!("<presence to='#{conference}@conference.#{@@jid_suffix || @server}/#{@jid}' />")
167
186
  end
168
187
  end
@@ -71,6 +71,20 @@ class KnowledgeClass
71
71
  @@classes[self.name].properties << property_name.to_s
72
72
  end
73
73
 
74
+ def to_template
75
+ description = @@classes[self.name]
76
+ fact = [description.name.to_sym]
77
+ fact << description.primary_key.upcase.to_sym
78
+ description.properties.each do |k|
79
+ next if k == description.primary_key
80
+
81
+ fact << k.to_sym
82
+ fact << k.upcase.to_sym
83
+ end
84
+
85
+ fact
86
+ end
87
+
74
88
  def from_fact(fact)
75
89
  description = @@classes[self.name]
76
90
  return nil if fact[0] != description.name.to_sym
@@ -88,8 +102,19 @@ class KnowledgeClass
88
102
 
89
103
  attr_reader :values
90
104
 
91
- def initialize
105
+ def initialize(options = {})
92
106
  @values = {}
107
+
108
+ description = @@classes[self.class.name]
109
+ if options.has_key?(description.primary_key.to_sym)
110
+ @values[description.primary_key] = options[description.primary_key.to_sym]
111
+ end
112
+
113
+ description.properties.each do |prop|
114
+ next if prop == description.primary_key
115
+ next unless options.has_key?(prop.to_sym)
116
+ @values[prop] = options[prop.to_sym]
117
+ end
93
118
  end
94
119
 
95
120
  def to_template
@@ -100,7 +125,21 @@ class KnowledgeClass
100
125
  next if k == description.primary_key
101
126
 
102
127
  fact << k.to_sym
103
- fact << k.upcase.to_sym
128
+ fact << values[k]
129
+ end
130
+
131
+ fact
132
+ end
133
+
134
+ def to_params
135
+ description = @@classes[self.class.name]
136
+ fact = [description.name.to_sym]
137
+ fact << (values.has_key?(description.primary_key) ? values[description.primary_key] : description.primary_key.upcase.to_sym)
138
+ description.properties.each do |k|
139
+ next if k == description.primary_key
140
+
141
+ fact << k.to_sym
142
+ fact << (values.has_key?(k) ? values[k] : k.upcase.to_sym)
104
143
  end
105
144
 
106
145
  fact
@@ -79,7 +79,8 @@ class Ontology
79
79
  return if predicate.empty?
80
80
  return if current_ruleset.count {|rule| rule.name == name} > 0
81
81
 
82
- current_ruleset << RuleDescription.new(name, predicate, options, block)
82
+ distilled_predicate = predicate.map {|cond| cond.is_a?(KnowledgeClass) ? cond.to_params : cond}
83
+ current_ruleset << RuleDescription.new(name, distilled_predicate, options, block)
83
84
  end
84
85
  end
85
86
 
@@ -180,6 +181,10 @@ class Ontology
180
181
  end
181
182
  end
182
183
 
184
+ def report_incident(subject, description)
185
+
186
+ end
187
+
183
188
  def dump_kb()
184
189
  @facts.enumerate.map {|fact| fact.data.to_s}
185
190
  end
@@ -222,7 +227,37 @@ class Ontology
222
227
 
223
228
  def inform_and_wait(agent, fact, options = {})
224
229
 
225
- end
230
+ end
231
+
232
+ #
233
+ # The action of agreeing to perform some action, possibly in the future.
234
+ #
235
+ def agree(agent, action, options = {})
236
+ puts "%25s | agree with %s to perform %s %s" % [identifier, agent, Sexpistol.new.to_sexp(action), print_message_options(options)]
237
+
238
+ channel = ChannelFactory.retrieve(identifier, agent)
239
+ channel.agree(identifier, action, options) if channel
240
+ end
241
+
242
+ #
243
+ # The action of refusing to perform a given action, and explaining the reason for the refusal.
244
+ #
245
+ def refuse(agent, action, reason, options = {})
246
+ puts "%25s | refuse %s to perform %s because %s %s" % [identifier, agent, Sexpistol.new.to_sexp(action), Sexpistol.new.to_sexp(reason), print_message_options(options)]
247
+
248
+ channel = ChannelFactory.retrieve(identifier, agent)
249
+ channel.refuse(identifier, action, reason, options) if channel
250
+ end
251
+
252
+ #
253
+ # The action of telling another agent that an action was attempted but the attempt failed.
254
+ #
255
+ def failure(agent, action, reason = true , options = {})
256
+ puts "%25s | inform %s that action %s failed with reason %s %s" % [identifier, agent, Sexpistol.new.to_sexp(action), Sexpistol.new.to_sexp(reason), print_message_options(options)]
257
+
258
+ channel = ChannelFactory.retrieve(identifier, agent)
259
+ channel.failure(identifier, action, reason, options) if channel
260
+ end
226
261
 
227
262
  #
228
263
  # Send request to another agent.
@@ -268,29 +303,62 @@ class Ontology
268
303
  def handle_inform(sender, proposition, options = {})
269
304
  puts "%25s | received %s from %s %s" % [identifier, Sexpistol.new.to_sexp(proposition), sender, print_message_options(options)]
270
305
 
271
- if options.has_key?(:conversation_id) || options.has_key?(:in_reply_to)
272
- saga_id = options[:conversation_id] || options[:in_reply_to]
273
- saga = @sagas.find {|saga| saga.id == saga_id}
274
- saga.handle_reply(sender, proposition, :action => :inform) if saga
275
- else
306
+ if !handle_saga_reply(sender, :inform, proposition, options)
307
+ @classes.each do |klass|
308
+ if instance = klass.from_fact(proposition)
309
+ matcher = PatternMatcher.new(@facts.enumerate)
310
+ bindings = matcher.match(instance.to_template)
311
+ if !bindings.empty?
312
+ replace instance.to_template, matcher.pattern_matches?(proposition, instance.to_template)
313
+ else
314
+ assert(proposition)
315
+ end
316
+
317
+ return
318
+ end
319
+ end
320
+
276
321
  assert proposition, :origin => sender
277
322
  end
278
323
  end
279
324
 
325
+
326
+ def handle_agree(sender, action, options = {})
327
+ if !handle_saga_reply(sender, :agree, action, options)
328
+ puts "%25s | %s agreed to perform %s %s" % [identifier, sender, Sexpistol.new.to_sexp(action), print_message_options(options)]
329
+ end
330
+ end
331
+
332
+ def handle_refuse(sender, action, reason, options = {})
333
+ if !handle_saga_reply(sender, :refuse, [action, reason], options)
334
+ puts "%25s | %s refused to perform %s because %s %s" % [identifier, sender, Sexpistol.new.to_sexp(action), Sexpistol.new.to_sexp(reason), print_message_options(options)]
335
+ end
336
+ end
337
+
338
+ def handle_failure(sender, action, reason, options = {})
339
+ if !handle_saga_reply(sender, :failure, [action, reason], options)
340
+ puts "%25s | %s failed to perform %s because %s %s" % [identifier, sender, Sexpistol.new.to_sexp(action), Sexpistol.new.to_sexp(reason), print_message_options(options)]
341
+ end
342
+ end
343
+
280
344
  #
281
345
  # Abstract method to handle requests to this ontology.
282
346
  #
283
- def handle_request(sender, contents, options = {}); end
347
+ def handle_request(sender, contents, options = {})
348
+ if !handle_saga_reply(sender, :request, contents, options)
349
+ puts "%25s | %s requests to perform %s %s" % [identifier, sender, Sexpistol.new.to_sexp(contents), print_message_options(options)]
350
+ end
351
+ end
284
352
 
285
353
  def handle_query(sender, expression, options = {})
286
- puts "%25s | %s queries %s %s" % [identifier, sender, Sexpistol.new.to_sexp(expression), print_message_options(options)]
354
+ puts "%25s | %s queries %s %s" % [identifier, sender, Sexpistol.new.to_sexp(expression), print_message_options(options)] unless handle_saga_reply(sender, :query, expression, options)
287
355
  end
288
356
 
289
357
  #
290
358
  # Handles query-if to ontology. By default, it lookups the fact in KB and replies to the sender.
291
359
  #
292
360
  def handle_query_if(sender, proposition, options = {})
293
- puts "%25s | %s queries if %s %s" % [identifier, sender, Sexpistol.new.to_sexp(proposition), print_message_options(options)]
361
+ puts "%25s | %s queries if %s %s" % [identifier, sender, Sexpistol.new.to_sexp(proposition), print_message_options(options)] unless handle_saga_reply(sender, :query, expression, options)
294
362
  end
295
363
 
296
364
  protected
@@ -298,6 +366,17 @@ class Ontology
298
366
  attr_reader :facts
299
367
  attr_accessor :running
300
368
 
369
+ def handle_saga_reply(sender, action, content, options)
370
+ if options.has_key?(:conversation_id) || options.has_key?(:in_reply_to)
371
+ saga_id = options[:conversation_id] || options[:in_reply_to]
372
+ saga = @sagas.find {|saga| saga.id == saga_id}
373
+ saga.handle_reply(sender, content, :action => action) if saga
374
+ return true
375
+ end
376
+
377
+ false
378
+ end
379
+
301
380
  def assert_nb(fact, options = {}, silent = false)
302
381
  silent = options unless options.is_a?(Hash)
303
382
  options = {} unless options.is_a?(Hash)
@@ -43,10 +43,23 @@ class Saga
43
43
  #
44
44
  # Inter-agent communications with context of this saga.
45
45
  #
46
+
46
47
  def inform(agent, proposition)
47
48
  @ontology.inform agent, proposition, :conversation_id => self.id
48
49
  end
49
50
 
51
+ def agree(agent, action)
52
+ @ontology.agree agent, action, :conversation_id => self.id
53
+ end
54
+
55
+ def refuse(agent, action, reason)
56
+ @ontology.refuse agent, action, reason, :conversation_id => self.id
57
+ end
58
+
59
+ def failure(agent, action, reason = true)
60
+ @ontology.failure agent, action, reason, :conversation_id => self.id
61
+ end
62
+
50
63
  def request(agent, action)
51
64
  @ontology.request agent, action, :conversation_id => self.id
52
65
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cirrocumulus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-16 00:00:00.000000000 Z
12
+ date: 2012-12-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: log4r