cirrocumulus 0.3.0 → 0.4.2

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.2
data/cirrocumulus.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{cirrocumulus}
8
- s.version = "0.3.0"
8
+ s.version = "0.4.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Anton Kosyakin"]
@@ -32,7 +32,9 @@ Gem::Specification.new do |s|
32
32
  "lib/cirrocumulus/logger.rb",
33
33
  "lib/cirrocumulus/master_agent.rb",
34
34
  "lib/cirrocumulus/ontology.rb",
35
+ "lib/cirrocumulus/rule_engine.rb",
35
36
  "lib/cirrocumulus/saga.rb",
37
+ "lib/test.rb",
36
38
  "test/helper.rb",
37
39
  "test/test_cirrocumulus.rb"
38
40
  ]
@@ -0,0 +1,224 @@
1
+ module RuleEngine
2
+ class Base
3
+ def self.rule(name, facts, &block)
4
+ current_ruleset << {:name => name, :facts => facts, :body => block}
5
+ end
6
+
7
+ def dump_kb()
8
+ log "Dumping current knowledge base:\n"
9
+ @facts.each_with_index do |fact,i|
10
+ log "%d) %s" % [i, fact.inspect]
11
+ end
12
+
13
+ log "Empty" if @facts.empty?
14
+ end
15
+
16
+ def dump_ruleset()
17
+ log "Dumping current ruleset:\n"
18
+ self.class.current_ruleset.each_with_index do |rule,i|
19
+ log "%d) %s" % [i, rule[:name]]
20
+ end
21
+
22
+ log "Empty ruleset" if self.class.current_ruleset.empty?
23
+ end
24
+
25
+ def assert(fact, silent = false)
26
+ log "assert: #{fact.inspect}"
27
+
28
+ @facts = [] if @facts.nil?
29
+ @facts << fact
30
+ process() if !silent
31
+ end
32
+
33
+ def retract(fact, silent = false)
34
+ @facts = [] if @facts.nil?
35
+ if @facts.delete(fact)
36
+ log "retract: #{fact.inspect}"
37
+ process() if !silent
38
+ else
39
+ puts "fact #{fact.inspect} not found"
40
+ end
41
+ end
42
+
43
+ def execute()
44
+ process()
45
+ end
46
+
47
+ protected
48
+
49
+ @@loaded_rules = {}
50
+
51
+ def self.current_ruleset()
52
+ return @@loaded_rules[self.name] ||= []
53
+ end
54
+
55
+ def log(msg)
56
+ Log4r::Logger['kb'].info(msg)
57
+ rescue
58
+ puts "[INFO] %s" % msg
59
+ end
60
+
61
+ def debug(msg)
62
+ Log4r::Logger['kb'].debug(msg)
63
+ rescue
64
+ puts "[DEBUG] %s" % msg
65
+ end
66
+
67
+ def matches?(rule)
68
+ debug "Processing rule '#{rule[:name]}' (#{rule[:facts].size} condition(s))"
69
+
70
+ pattern_candidates = []
71
+ rule[:facts].each do |pattern|
72
+ pattern_candidates << match_pattern(pattern)
73
+ end
74
+
75
+ return nil if !pattern_candidates.all? {|c| c.size > 0}
76
+
77
+ debug "Rule '#{rule[:name]}' has candidates for each pattern"
78
+
79
+ match_parameters(rule, pattern_candidates)
80
+ end
81
+
82
+ def match_pattern(pattern)
83
+ debug "Attempting to match pattern #{pattern.inspect}"
84
+ fact_matches = true
85
+ candidates = []
86
+
87
+ @facts.each do |fact|
88
+ next if fact.size != pattern.size
89
+ fact_matches = true
90
+
91
+ pattern.each_with_index do |el,i|
92
+ if el.is_a?(Symbol) && el.to_s.upcase == el.to_s # parameter
93
+ else
94
+ fact_matches = false if el != fact[i]
95
+ end
96
+ end
97
+
98
+ candidates << fact if fact_matches
99
+ end
100
+
101
+ debug "Found #{candidates.size} candidate(s)"
102
+ candidates
103
+ end
104
+
105
+ def match_parameters(rule, candidates)
106
+ debug "Attempting to match parameters"
107
+
108
+ result = []
109
+ attempt = []
110
+ while (attempt = generate_combination(rule, candidates, attempt)) != [] do
111
+ bindings = test_combination(rule, candidates, attempt)
112
+ if bindings
113
+ debug "Matched! %s" % bindings.inspect
114
+ result << bindings
115
+ end
116
+ end
117
+
118
+ result
119
+ end
120
+
121
+ def test_combination(rule, candidates, attempt)
122
+ facts = []
123
+ attempt.each_with_index {|a,i| facts << candidates[i][a]}
124
+ debug "Testing combination #{attempt.inspect}: %s" % facts.inspect
125
+
126
+ binded_params = {}
127
+ pattern_params = {}
128
+ facts.each_with_index do |fact,i|
129
+ pattern_params = bind_parameters(rule[:facts][i], fact, binded_params)
130
+ if pattern_params.nil? # failure, parameters mismatch
131
+ return nil
132
+ else
133
+ binded_params.merge!(pattern_params)
134
+ end
135
+ end
136
+
137
+ binded_params
138
+ end
139
+
140
+ def bind_parameters(pattern, fact, current_bindings)
141
+ result = {}
142
+
143
+ pattern.each_with_index do |p,i|
144
+ if p.is_a?(Symbol) && p.to_s.upcase == p.to_s
145
+ return nil if current_bindings.has_key?(p) && current_bindings[p] != fact[i]
146
+ result[p] = fact[i]
147
+ end
148
+ end
149
+
150
+ result
151
+ end
152
+
153
+ def generate_combination(rule, candidates, attempt)
154
+ next_attempt = []
155
+
156
+ if attempt == []
157
+ rule[:facts].each {|pattern| next_attempt << 0}
158
+ else
159
+ next_attempt = increment_attempt(attempt, rule[:facts].size - 1, candidates.map {|c| c.size})
160
+ end
161
+
162
+ next_attempt
163
+ end
164
+
165
+ def increment_attempt(attempt, idx, limits)
166
+ return [] if idx < 0
167
+
168
+ if attempt[idx] < limits[idx] - 1
169
+ attempt[idx] += 1
170
+ else
171
+ i = idx
172
+ while i < limits.size do
173
+ attempt[i] = 0
174
+ i += 1
175
+ end
176
+
177
+ return increment_attempt(attempt, idx-1, limits)
178
+ end
179
+
180
+ attempt
181
+ end
182
+
183
+ def pattern_matches?(fact, pattern, current_params = {})
184
+ return nil if fact.size != pattern.size
185
+
186
+ puts "DEBUG: testing pattern %s against fact %s" % [pattern.inspect, fact.inspect]
187
+ puts "DEBUG: current parameters binding is %s" % current_params.inspect
188
+
189
+ binded_params = {}
190
+
191
+ pattern.each_with_index do |el,i|
192
+ if el.is_a?(Symbol) && el.to_s.upcase == el.to_s
193
+ puts "DEBUG: need to bind parameter %s" % el.to_s
194
+ if current_params && current_params.has_key?(el)
195
+ current_value = current_params[el]
196
+ return nil if fact[i] != current_value
197
+ else
198
+ binded_params[el] = fact[i]
199
+ end
200
+ else
201
+ return nil if el != fact[i]
202
+ end
203
+ end
204
+
205
+ puts "DEBUG: match! binding parameters: %s" % binded_params.inspect
206
+ binded_params
207
+ end
208
+
209
+ def execute_rule(rule, params)
210
+ debug "executing rule '#{rule[:name]}'"
211
+ rule[:body].call(self, params)
212
+ end
213
+
214
+ def process()
215
+ self.class.current_ruleset.each do |rule|
216
+ binded_params = matches?(rule)
217
+ next if binded_params.nil?
218
+ binded_params.each {|params| execute_rule(rule, params)}
219
+ end
220
+ end
221
+
222
+ end
223
+
224
+ end
data/lib/test.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'cirrocumulus/rule_engine.rb'
2
+
3
+ class Test < RuleEngine::Base
4
+ rule 'convert', [[:temperature, :X, 'F']] do |engine, params|
5
+ x = params[:X].to_i
6
+ engine.retract([:temperature, x, 'F'])
7
+ y = 5*(x - 32)/9
8
+ engine.assert([:temperature, y, 'C'])
9
+ end
10
+
11
+ rule 'monitor_md', [[:virtual_disk, :X, :active], [:mdraid, :X, :failed]] do |engine, params|
12
+ # md devices is failed, but virtual disk should be up
13
+ p params
14
+ puts "virtual disk #{params[:X]} should be up, but corresponding md devices is failed!"
15
+ end
16
+ end
17
+
18
+ e = Test.new
19
+ e.assert [:virtual_disk, 163, :active]
20
+ #e.assert [:virtual_disk, 139, :active]
21
+ #e.assert [:virtual_disk, 145, :active]
22
+ #e.assert [:virtual_disk, 146, :active]
23
+ #e.assert [:virtual_disk, 149, :active]
24
+ e.assert [:virtual_disk, 153, :active]
25
+ #e.assert [:virtual_disk, 154, :active]
26
+ #e.assert [:virtual_disk, 156, :active]
27
+ e.assert [:virtual_disk, 158, :active]
28
+ #e.assert [:virtual_disk, 137, :active]
29
+ #e.assert [:virtual_disk, 135, :active]
30
+ #e.assert [:virtual_disk, 159, :active]
31
+ #e.assert [:virtual_disk, 103, :active]
32
+ #e.assert [:virtual_disk, 102, :active]
33
+ #e.assert [:virtual_disk, 20, :active]
34
+ #e.assert [:virtual_disk, 2, :active]
35
+ #e.assert [:virtual_disk, 777, :active]
36
+ #e.assert [:virtual_disk, 90, :active]
37
+ e.assert [:mdraid, 153, :failed], true
38
+ e.assert [:mdraid, 158, :failed], true
39
+ e.execute()
40
+ gets
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cirrocumulus
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 0
10
- version: 0.3.0
8
+ - 4
9
+ - 2
10
+ version: 0.4.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Anton Kosyakin
@@ -180,7 +180,9 @@ files:
180
180
  - lib/cirrocumulus/logger.rb
181
181
  - lib/cirrocumulus/master_agent.rb
182
182
  - lib/cirrocumulus/ontology.rb
183
+ - lib/cirrocumulus/rule_engine.rb
183
184
  - lib/cirrocumulus/saga.rb
185
+ - lib/test.rb
184
186
  - test/helper.rb
185
187
  - test/test_cirrocumulus.rb
186
188
  has_rdoc: true