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 +1 -1
- data/cirrocumulus.gemspec +3 -1
- data/lib/cirrocumulus/rule_engine.rb +224 -0
- data/lib/test.rb +40 -0
- metadata +6 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
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.
|
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:
|
4
|
+
hash: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 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
|