Source code for shinken.dependencynode

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# Copyright (C) 2009-2012:
#     Gabes Jean, naparuba@gmail.com
#     Gerhard Lausser, Gerhard.Lausser@consol.de
#     Gregory Starck, g.starck@gmail.com
#     Hartmut Goebel, h.goebel@goebel-consult.de
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

import re

"""
Here is a node class for dependency_node(s) and a factory to create them
"""
[docs]class DependencyNode(object): def __init__(self): self.operand = None self.sons = [] # Of: values are a triple OK,WARN,CRIT self.of_values = (0, 0, 0) self.is_of_mul = False self.configuration_errors = [] self.not_value = False def __str__(self): return "Op:'%s' Val:'%s' Sons:'[%s]' IsNot:'%s'" % (self.operand, self.of_values, ','.join([str(s) for s in self.sons]), self.not_value)
[docs] def get_reverse_state(self, state): # Warning is still warning if state == 1: return 1 if state == 0: return 2 if state == 2: return 0 # should not go here... return state # We will get the state of this node, by looking at the state of # our sons, and apply our operand
[docs] def get_state(self): #print "Ask state of me", self # If we are a host or a service, wee just got the host/service # hard state if self.operand in ['host', 'service']: state = self.sons[0].last_hard_state_id #print "Get the hard state (%s) for the object %s" % (state, self.sons[0].get_name()) # Make DOWN look as CRITICAL (2 instead of 1) if self.operand == 'host' and state == 1: state = 2 # Maybe we are a NOT node, so manage this if self.not_value: # We inverse our states if self.operand == 'host' and state == 1: return 0 if self.operand == 'host' and state == 0: return 1 # Critical -> OK if self.operand == 'service' and state == 2: return 0 # OK -> CRITICAL (warning is untouched) if self.operand == 'service' and state == 0: return 2 return state # First we get the state of all our sons states = [] for s in self.sons: st = s.get_state() states.append(st) # We will surely need the worst state worst_state = max(states) # Suggestion: What about returning min(states) for the | operand? # We don't need make a difference between an 0 and another no? # If you do so, it may be more efficient with lots of services # or host to return OK, but otherwise I can't see the reason for # this subcase. # We look for the better state but not OK/UP no_ok = [s for s in states if s != 0] if len(no_ok) != 0: best_not_good = min(no_ok) # Now look at the rule. For a or if self.operand == '|': if 0 in states: if self.not_value: return self.get_reverse_state(0) #print "We find a OK/UP match in an OR", states return 0 # no ok/UP-> return worst state else: if self.not_value: return self.get_reverse_state(best_not_good) #print "I send the best not good state...in an OR", best_not_good, states return best_not_good # With an AND, we just send the worst state if self.operand == '&': if self.not_value: return self.get_reverse_state(worst_state) #print "We raise worst state for a AND", worst_state,states return worst_state # Ok we've got a 'of:' rule # We search for OK, WARN or CRIT applications # And we will choice between them nb_search_ok = self.of_values[0] nb_search_warn = self.of_values[1] nb_search_crit = self.of_values[2] # We look for each application nb_ok = len([s for s in states if s == 0]) nb_warn = len([s for s in states if s == 1]) nb_crit = len([s for s in states if s == 2]) #print "NB:", nb_ok, nb_warn, nb_crit # Ok and Crit apply with their own values # Warn can apply with warn or crit values # so a W C can raise a Warning, but not enough for # a critical ok_apply = nb_ok >= nb_search_ok warn_apply = nb_warn + nb_crit >= nb_search_warn crit_apply = nb_crit >= nb_search_crit #print "What apply?", ok_apply, warn_apply, crit_apply # return the worst state that apply if crit_apply: if self.not_value: return self.get_reverse_state(2) return 2 if warn_apply: if self.not_value: return self.get_reverse_state(1) return 1 if ok_apply: if self.not_value: return self.get_reverse_state(0) return 0 # Maybe even OK is not possible, if so, it depends if the admin # ask a simple form Xof: or a multiple one A,B,Cof: # the simple should give OK, the mult should give the worst state if self.is_of_mul: #print "Is mul, send 0" if self.not_value: return self.get_reverse_state(0) return 0 else: #print "not mul, return worst", worse_state if self.not_value: return self.get_reverse_state(worst_state) return worst_state # return a list of all host/service in our node and below
[docs] def list_all_elements(self): r = [] # We are a host/service if self.operand in ['host', 'service']: return [self.sons[0]] for s in self.sons: r.extend(s.list_all_elements()) # and uniq the result return list(set(r)) # If we are a of: rule, we can get some 0 in of_values, # if so, change them with NB sons instead
[docs] def switch_zeros_of_values(self): nb_sons = len(self.sons) # Need a list for assignment self.of_values = list(self.of_values) for i in [0, 1, 2]: if self.of_values[i] == 0: self.of_values[i] = nb_sons self.of_values = tuple(self.of_values) # Check for empty (= not found) leaf nodes
[docs] def is_valid(self): valid = True if not self.sons: valid = False else: for s in self.sons: if isinstance(s, DependencyNode) and not s.is_valid(): self.configuration_errors.extend(s.configuration_errors) valid = False return valid
""" TODO: Add some comment about this class for the doc"""
[docs]class DependencyNodeFactory(object): def __init__(self): pass # the () will be eval in a recursiv way, only one level of ()
[docs] def eval_cor_pattern(self, pattern, hosts, services): pattern = pattern.strip() #print "***** EVAL ", pattern complex_node = False # Look if it's a complex pattern (with rule) or # if it's a leaf ofit, like a host/service for m in '()+&|': if m in pattern: complex_node = True is_of_nb = False node = DependencyNode() p = "^(\d+),*(\d*),*(\d*) *of: *(.+)" r = re.compile(p) m = r.search(pattern) if m is not None: #print "Match the of: thing N=", m.groups() node.operand = 'of:' g = m.groups() # We can have a Aof: rule, or a multiple A,B,Cof: rule. mul_of = (g[1] != u'' and g[2] != u'') # If multi got (A,B,C) if mul_of: node.is_of_mul = True node.of_values = (int(g[0]), int(g[1]), int(g[2])) else: # if not, use A,0,0, we will change 0 after to put MAX node.of_values = (int(g[0]), 0, 0) pattern = m.groups()[3] #print "Is so complex?", pattern, complex_node # if it's a single host/service if not complex_node: #print "Try to find?", pattern # If it's a not value, tag the node and find # the name without this ! operator if pattern.startswith('!'): node.not_value = True pattern = pattern[1:] node.operand = 'object' obj, error = self.find_object(pattern, hosts, services) if obj is not None: # Set host or service node.operand = obj.__class__.my_type node.sons.append(obj) else: node.configuration_errors.append(error) return node #else: # print "Is complex" in_par = False tmp = '' son_is_not = False # We keep is the next son will ne not or not stacked_par = 0 for c in pattern: #print "MATCHING", c, pattern if c == '&' or c == '|': # Maybe we are in a par, if so, just stack it if in_par: #print " & in a par, just staking it" tmp += c else: # Oh we got a real cut in an expression, if so, cut it #print "REAL & for cutting" tmp = tmp.strip() # Look at the rule viability current_rule = node.operand if current_rule is not None and current_rule != 'of:' and c != current_rule: # Should be logged as a warning / info? :) return None if current_rule != 'of:': node.operand = c if tmp != '': #print "Will analyse the current str", tmp o = self.eval_cor_pattern(tmp, hosts, services) # Maybe our son was notted if son_is_not: o.not_value = True son_is_not = False node.sons.append(o) tmp = '' continue elif c == '(': stacked_par += 1 #print "INCREASING STACK TO", stacked_par in_par = True tmp = tmp.strip() # Maybe we just start a par, but we got some things in tmp # that should not be good in fact ! if stacked_par == 1 and tmp != '': #TODO : real error print "ERROR : bad expression near", tmp continue # If we are already in a par, add this ( # but not if it's the first one so if stacked_par > 1: tmp += c #o = self.eval_cor_pattern(tmp) #print "1( I've %s got new sons" % pattern , o #node.sons.append(o) elif c == ')': #print "Need closeing a sub expression?", tmp stacked_par -= 1 if stacked_par < 0: # TODO : real error print "Error : bad expression near", tmp, "too much ')'" continue if stacked_par == 0: #print "THIS is closing a sub compress expression", tmp tmp = tmp.strip() o = self.eval_cor_pattern(tmp, hosts, services) # Maybe our son was notted if son_is_not: o.not_value = True son_is_not = False node.sons.append(o) in_par = False # OK now clean the tmp so we start clean tmp = '' continue # ok here we are still in a huge par, we just close one sub one tmp += c # Manage the NOT for an expression. If we are in a starting bloc, put as # a NOT node, but if inside a bloc, don't elif c == '!': if stacked_par == 0: son_is_not = True # DO NOT keep the c in tmp, we consumed it else: tmp += c # Maybe it's a classic character, if so, continue else: tmp += c # Be sure to manage the trainling part when the line is done tmp = tmp.strip() if tmp != '': #print "Managing trainling part", tmp o = self.eval_cor_pattern(tmp, hosts, services) # Maybe our son was notted if son_is_not: o.not_value = True son_is_not = False #print "4end I've %s got new sons" % pattern , o node.sons.append(o) # We got our nodes, so we can update 0 values of of_values # with the number of sons node.switch_zeros_of_values() #print "End, tmp", tmp #print "R %s:" % pattern #print "Node:", node return node # We've got an object, like h1,db1 that mean the # db1 service of the host db1, or just h1, that mean # the host h1.
[docs] def find_object(self, pattern, hosts, services): #print "Finding object", pattern obj = None error = None is_service = False # h_name, service_desc are , separated elts = pattern.split(',') host_name = elts[0].strip() # Look if we have a service if len(elts) > 1: is_service = True service_description = elts[1].strip() if is_service: obj = services.find_srv_by_name_and_hostname(host_name, service_description) if not obj: error = "Business rule uses unknown service %s/%s" % (host_name, service_description) else: obj = hosts.find_by_name(host_name) if not obj: error = "Business rule uses unknown host %s" % (host_name,) return obj, error
Read the Docs v: 1.4.2
Versions
latest
stable
branch-1.4
2.4.1
2.4
2.4---4
2.4---3
2.4---2
2.4---1
2.4-----1
2.2
2.2---1
2.0.3
1.4.2
Downloads
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.