cirrocumulus 0.9.2 → 0.9.7

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.
@@ -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