Merge branch 'master' of ssh://lausser,shinken@shinken.git.sourceforge.net/gitroot...
[shinken.git] / shinken / dependencynode.py
blobb87aae10bdeeaee1e2ea02bf1e548a812175a651
1 #!/usr/bin/env python
2 # Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
6 # Hartmut Goebel, h.goebel@goebel-consult.de
8 # This file is part of Shinken.
10 # Shinken is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU Affero General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
15 # Shinken is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU Affero General Public License for more details.
20 # You should have received a copy of the GNU Affero General Public License
21 # along with Shinken. If not, see <http://www.gnu.org/licenses/>.
24 """
25 Here is a node class for dependency_node(s) and a factory to create them
26 """
28 import re
30 #pat = "(h1;db | h2;db | h3;db) & (h4;Apache & h5;Apache & h6;Apache) & (h7;lvs | h8;lvs)"
31 #pat2 = "h1;db | h2;db"
32 #pat3 = "(h1;db | h2;db | h3;db) & (h4;Apache & h5;Apache)"
33 #pat4 = "2 of: h1;db | h2;db | h3;db"
35 class DependencyNode(object):
36 def __init__(self):
37 self.operand = None
38 self.sons = []
39 self.of_values = 0
40 self.configuration_errors = []
42 def __str__(self):
43 return "Op:'%s' Val:'%s' Sons:'[%s]'" % (self.operand, self.of_values, ','.join([str(s) for s in self.sons]))
46 # We will get the state of this node, by looking at the state of
47 # our sons, and apply our operand
48 def get_state(self):
49 #print "Ask state of me", self
51 # If we are a host or a service, wee just got the host/service
52 # hard state
53 if self.operand in ['host', 'service']:
54 state = self.sons[0].last_hard_state_id
55 #print "Get the hard state (%s) for the object %s" % (state, self.sons[0].get_name())
56 # Make DOWN look as CRITICAL (2 instead of 1)
57 if self.operand == 'host' and state == 1:
58 state = 2
59 return state
61 # First we get teh state of all our sons
62 states = []
63 for s in self.sons:
64 st = s.get_state()
65 states.append(st)
67 # We will surely need the worse state
68 worse_state = max(states)
70 # We look for the better state but not OK/UP
71 no_ok = [s for s in states if s != 0]
72 if len(no_ok) != 0:
73 better_no_good = min(no_ok)
75 # Now look at the rule. For a or
76 if self.operand == '|':
77 if 0 in states:
78 #print "We find a OK/UP match in an OR", states
79 return 0
80 # no ok/UP-> return worse state
81 else:
82 #print "I send the better no good state...in an OR", better_no_good, states
83 return better_no_good
85 # With an AND, we just send the worse state
86 if self.operand == '&':
87 #print "We raise worse state for a AND", worse_state,states
88 return worse_state
90 # Ok we've got a 'of:' rule
91 nb_search = self.of_values
92 # Look if we've got enouth 0
93 if len([s for s in states if s == 0]) >= nb_search:
94 #print "Good, we find at least %d 0 in states for a of:" % nb_search, states
95 return 0
97 # Now maybe at least enouth WARNING, still beter than CRITICAL...
98 if len([s for s in states if s == 1]) >= nb_search:
99 #print "Beter than nothing, we find at least %d 1 in states for a of:" % nb_search, states
100 return 1
102 # Sic... not good, return 2
103 #print "ARG, not enough 1 or 0, return 2..."
104 return 2
107 #return a list of all host/service in our node and below
108 def list_all_elements(self):
109 r = []
111 #We are a host/service
112 if self.operand in ['host', 'service']:
113 return [self.sons[0]]
115 for s in self.sons:
116 r.extend(s.list_all_elements())
118 #and uniq the result
119 return list(set(r))
122 def is_valid(self):
123 """Check for empty (= not found) leaf nodes"""
124 valid = True
125 if not self.sons:
126 valid = False
127 else:
128 for s in self.sons:
129 if isinstance(s, DependencyNode) and not s.is_valid():
130 self.configuration_errors.extend(s.configuration_errors)
131 valid = False
132 return valid
137 class DependencyNodeFactory(object):
138 def __init__(self):
139 pass
141 # the () will be eval in a recursiv way, only one level of ()
142 def eval_cor_patern(self, patern, hosts, services):
143 patern = patern.strip()
144 #print "*****Loop", patern
145 complex_node = False
147 # Look if it's a complex patern (with rule) or
148 # if it's a leef ofit, like a host/service
149 for m in '()+&|':
150 if m in patern:
151 complex_node = True
153 is_of_nb = False
155 node = DependencyNode()
156 p = "^(\d+) *of: *(.+)"
157 r = re.compile(p)
158 m = r.search(patern)
159 if m is not None:
160 #print "Match the of: thing N=", m.groups()
161 node.operand = 'of:'
162 node.of_values = int(m.groups()[0])
163 patern = m.groups()[1]
165 #print "Is so complex?", patern, complex_node
167 # if it's a single host/service
168 if not complex_node:
169 #print "Try to find?", patern
170 node.operand = 'object'
171 obj, error = self.find_object(patern, hosts, services)
172 if obj is not None:
173 # Set host or service
174 node.operand = obj.__class__.my_type
175 node.sons.append(obj)
176 else:
177 node.configuration_errors.append(error)
178 return node
179 #else:
180 # print "Is complex"
182 in_par = False
183 tmp = ''
184 for c in patern:
185 if c == '(':
186 in_par = True
187 tmp = tmp.strip()
188 if tmp != '':
189 o = self.eval_cor_patern(tmp, hosts, services)
190 #print "1( I've %s got new sons" % patern , o
191 node.sons.append(o)
192 continue
193 if c == ')':
194 in_par = False
195 tmp = tmp.strip()
196 if tmp != '':
197 #print "Evaling sub pat", tmp
198 o = self.eval_cor_patern(tmp, hosts, services)
199 #print "2) I've %s got new sons" % patern , o
200 node.sons.append(o)
201 #else:
202 #print "Fuck a node son!"
203 tmp = ''
204 continue
206 if not in_par:
207 if c in ('&', '|'):
208 current_rule = node.operand
209 #print "Current rule", current_rule
210 if current_rule is not None and current_rule != 'of:' and c != current_rule:
211 #print "Fuck, you mix all dumbass!"
212 return None
213 if current_rule != 'of:':
214 node.operand = c
215 tmp = tmp.strip()
216 if tmp != '':
217 o = self.eval_cor_patern(tmp, hosts, services)
218 #print "3&| I've %s got new sons" % patern , o
219 node.sons.append(o)
220 tmp = ''
221 continue
222 else:
223 tmp += c
224 else:
225 tmp += c
227 tmp = tmp.strip()
228 if tmp != '':
229 o = self.eval_cor_patern(tmp, hosts, services)
230 #print "4end I've %s got new sons" % patern , o
231 node.sons.append(o)
233 #print "End, tmp", tmp
234 #print "R %s :" % patern, node
235 return node
238 # We've got an object, like h1,db1 that mean the
239 # db1 service of the host db1, or just h1, that mean
240 # the host h1.
241 def find_object(self, patern, hosts, services):
242 #print "Finding object", patern
243 obj = None
244 error = None
245 is_service = False
246 # h_name, service_desc are , separated
247 elts = patern.split(',')
248 host_name = elts[0]
249 # Look if we have a service
250 if len(elts) > 1:
251 is_service = True
252 service_description = elts[1]
253 if is_service:
254 obj = services.find_srv_by_name_and_hostname(host_name, service_description)
255 if not obj:
256 error = "Business rule uses unknown service %s/%s" % (host_name, service_description)
257 else:
258 obj = hosts.find_by_name(host_name)
259 if not obj:
260 error = "Business rule uses unknown host %s" % (host_name,)
261 return obj, error