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