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