Source code for alignak.graph

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015-2015: Alignak team, see AUTHORS.txt file for contributors
#
# This file is part of Alignak.
#
# Alignak 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.
#
# Alignak 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 Alignak.  If not, see <http://www.gnu.org/licenses/>.
#
#
# This file incorporates work covered by the following copyright and
# permission notice:
#
#  Copyright (C) 2009-2014:
#     Hartmut Goebel, h.goebel@goebel-consult.de
#     Nicolas Dupeux, nicolas@dupeux.net
#     Grégory Starck, g.starck@gmail.com
#     Sebastien Coavoux, s.coavoux@free.fr
#     Jean Gabes, naparuba@gmail.com
#     Zoran Zaric, zz@zoranzaric.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/>.
"""This modules provide Graph class. Used to check for loop into dependencies

"""


[docs]class Graph: """Graph is a class to make graph things like DFS checks or accessibility Why use an atomic bomb when a little hammer is enough? Graph are oriented. """ def __init__(self): self.nodes = {}
[docs] def add_node(self, node): """Create the node key into the mode dict with [] value :param node: node to add :type node: object :return: None """ self.nodes[node] = []
[docs] def add_nodes(self, nodes): """Add several nodes into the nodes dict :param nodes: nodes to add :type nodes: object :return: None """ for node in nodes: self.add_node(node)
[docs] def add_edge(self, from_node, to_node): """Add edge between two node The edge is oriented :param from_node: node where edge starts :type from_node: object :param to_node: node where edge ends :type to_node: object :return: None """ # Maybe to_node is unknown if to_node not in self.nodes: self.add_node(to_node) try: self.nodes[from_node].append(to_node) # If from_node does not exist, add it with its son except KeyError, exp: self.nodes[from_node] = [to_node]
[docs] def loop_check(self): """Check if we have a loop in the graph :return: Nodes in loop :rtype: list """ in_loop = [] # Add the tag for dfs check for node in self.nodes: node.dfs_loop_status = 'DFS_UNCHECKED' # Now do the job for node in self.nodes: # Run the dfs only if the node has not been already done */ if node.dfs_loop_status == 'DFS_UNCHECKED': self.dfs_loop_search(node) # If LOOP_INSIDE, must be returned if node.dfs_loop_status == 'DFS_LOOP_INSIDE': in_loop.append(node) # Remove the tag for node in self.nodes: del node.dfs_loop_status return in_loop
[docs] def get_accessibility_packs(self): """Get accessibility packs of the graph: in one pack element are related in a way. Between packs, there is no relation at all. TODO: Make it work for directional graph too Because for now, edge must be father->son AND son->father :return: packs of nodes :rtype: list """ packs = [] # Add the tag for dfs check for node in self.nodes: node.dfs_loop_status = 'DFS_UNCHECKED' for node in self.nodes: # Run the dfs only if the node is not already done */ if node.dfs_loop_status == 'DFS_UNCHECKED': packs.append(self.dfs_get_all_childs(node)) # Remove the tag for node in self.nodes: del node.dfs_loop_status return packs
[docs] def dfs_get_all_childs(self, root): """Recursively get all sons of this node :param root: node to get sons :type root: :return: sons :rtype: list """ root.dfs_loop_status = 'DFS_CHECKED' ret = set() # Me ret.add(root) # And my sons ret.update(self.nodes[root]) for child in self.nodes[root]: # I just don't care about already checked childs if child.dfs_loop_status == 'DFS_UNCHECKED': ret.update(self.dfs_get_all_childs(child)) return list(ret)